Revision: 6650
          http://sourceforge.net/p/jump-pilot/code/6650
Author:   michaudm
Date:     2020-12-20 11:59:38 +0000 (Sun, 20 Dec 2020)
Log Message:
-----------
replace MergeSelectedPolygonsWithNeighbourPlugIn by 
MergePolygonsWithNeighbourPlugIn :faster, transactional, more options

Modified Paths:
--------------
    core/trunk/Changes.txt
    core/trunk/src/language/jump.properties
    core/trunk/src/language/jump_cz.properties
    core/trunk/src/language/jump_de.properties
    core/trunk/src/language/jump_es.properties
    core/trunk/src/language/jump_fi.properties
    core/trunk/src/language/jump_fr.properties
    core/trunk/src/language/jump_hu.properties
    core/trunk/src/language/jump_it.properties
    core/trunk/src/language/jump_ja_JP.properties
    core/trunk/src/language/jump_ml.properties
    core/trunk/src/language/jump_pt.properties
    core/trunk/src/language/jump_pt_BR.properties
    core/trunk/src/language/jump_ta_IN.properties
    core/trunk/src/language/jump_te.properties
    core/trunk/src/language/jump_zh_CN.properties
    core/trunk/src/language/jump_zh_HK.properties
    
core/trunk/src/org/openjump/core/ui/plugin/tools/analysis/onelayer/MergePolygonsWithNeighbourPlugIn.java

Modified: core/trunk/Changes.txt
===================================================================
--- core/trunk/Changes.txt      2020-12-19 23:57:38 UTC (rev 6649)
+++ core/trunk/Changes.txt      2020-12-20 11:59:38 UTC (rev 6650)
@@ -28,6 +28,7 @@
 
 
   Improvements
+  * make MergePolygonsWithNeighbourPlugIn fast and transactional
   * Symbolizing : Updated CadTool plugin to ver. 1.0
     - added capability to load Python console and tools
   * Raster : Added gdal support for debian/ubuntu

Modified: core/trunk/src/language/jump.properties
===================================================================
--- core/trunk/src/language/jump.properties     2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump.properties     2020-12-20 11:59:38 UTC (rev 
6650)
@@ -1577,6 +1577,7 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute
 = Use an attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip
 = Only merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute
 = Select the attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=Feature
 with id={0} is not valid : merging of features is canceled
 
org.openjump.core.ui.plugin.tools.MergeTwoSelectedPolygonsPlugIn.Merge-Two-Polygons
 = Merge Two Polygons
 
org.openjump.core.ui.plugin.tools.MultiRingBufferSelectedPlugIn.Multiple-Ring-Buffer
 = Multiple Ring Buffer
 
org.openjump.core.ui.plugin.tools.MultiRingBufferSelectedPlugIn.Reset-all-buffer-options
 = Reset all buffer options

Modified: core/trunk/src/language/jump_cz.properties
===================================================================
--- core/trunk/src/language/jump_cz.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_cz.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -2987,3 +2987,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_de.properties
===================================================================
--- core/trunk/src/language/jump_de.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_de.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -2980,3 +2980,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_es.properties
===================================================================
--- core/trunk/src/language/jump_es.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_es.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -2957,4 +2957,5 @@
 
com.vividsolutions.jump.workbench.imagery.geoimg.GeoReferencedRaster.no-georeference-found=Ninguna
 referencia geografica encontrada
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
-org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
\ No newline at end of file
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled
\ No newline at end of file

Modified: core/trunk/src/language/jump_fi.properties
===================================================================
--- core/trunk/src/language/jump_fi.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_fi.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -2957,3 +2957,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_fr.properties
===================================================================
--- core/trunk/src/language/jump_fr.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_fr.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -2992,3 +2992,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=S\xE9lectionner
 l'attribut
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=Fusionner
 uniquement avec des objets de m\xEAme valeur d'attribut
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=Utiliser
 un attribut
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=L''objet
 d''id={0} n''est pas valide : la fusion est annul\xE9e

Modified: core/trunk/src/language/jump_hu.properties
===================================================================
--- core/trunk/src/language/jump_hu.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_hu.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -2978,3 +2978,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_it.properties
===================================================================
--- core/trunk/src/language/jump_it.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_it.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -2959,4 +2959,5 @@
 
com.vividsolutions.jump.workbench.imagery.geoimg.GeoReferencedRaster.no-georeference-found=Nessun
 riferimento geografico trovato
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
-org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
\ No newline at end of file
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled
\ No newline at end of file

Modified: core/trunk/src/language/jump_ja_JP.properties
===================================================================
--- core/trunk/src/language/jump_ja_JP.properties       2020-12-19 23:57:38 UTC 
(rev 6649)
+++ core/trunk/src/language/jump_ja_JP.properties       2020-12-20 11:59:38 UTC 
(rev 6650)
@@ -2982,3 +2982,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_ml.properties
===================================================================
--- core/trunk/src/language/jump_ml.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_ml.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -3757,3 +3757,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_pt.properties
===================================================================
--- core/trunk/src/language/jump_pt.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_pt.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -2981,3 +2981,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_pt_BR.properties
===================================================================
--- core/trunk/src/language/jump_pt_BR.properties       2020-12-19 23:57:38 UTC 
(rev 6649)
+++ core/trunk/src/language/jump_pt_BR.properties       2020-12-20 11:59:38 UTC 
(rev 6650)
@@ -2981,3 +2981,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_ta_IN.properties
===================================================================
--- core/trunk/src/language/jump_ta_IN.properties       2020-12-19 23:57:38 UTC 
(rev 6649)
+++ core/trunk/src/language/jump_ta_IN.properties       2020-12-20 11:59:38 UTC 
(rev 6650)
@@ -2980,3 +2980,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_te.properties
===================================================================
--- core/trunk/src/language/jump_te.properties  2020-12-19 23:57:38 UTC (rev 
6649)
+++ core/trunk/src/language/jump_te.properties  2020-12-20 11:59:38 UTC (rev 
6650)
@@ -3487,3 +3487,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_zh_CN.properties
===================================================================
--- core/trunk/src/language/jump_zh_CN.properties       2020-12-19 23:57:38 UTC 
(rev 6649)
+++ core/trunk/src/language/jump_zh_CN.properties       2020-12-20 11:59:38 UTC 
(rev 6650)
@@ -3144,3 +3144,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: core/trunk/src/language/jump_zh_HK.properties
===================================================================
--- core/trunk/src/language/jump_zh_HK.properties       2020-12-19 23:57:38 UTC 
(rev 6649)
+++ core/trunk/src/language/jump_zh_HK.properties       2020-12-20 11:59:38 UTC 
(rev 6650)
@@ -3143,3 +3143,4 @@
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute=#T:Select
 the attribute
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip=#T:Only
 merge with features having the same attribute value
 
org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute=#T:Use
 an attribute
+org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid=#T:Feature
 with id={0} is not valid : merging of features is canceled

Modified: 
core/trunk/src/org/openjump/core/ui/plugin/tools/analysis/onelayer/MergePolygonsWithNeighbourPlugIn.java
===================================================================
--- 
core/trunk/src/org/openjump/core/ui/plugin/tools/analysis/onelayer/MergePolygonsWithNeighbourPlugIn.java
    2020-12-19 23:57:38 UTC (rev 6649)
+++ 
core/trunk/src/org/openjump/core/ui/plugin/tools/analysis/onelayer/MergePolygonsWithNeighbourPlugIn.java
    2020-12-20 11:59:38 UTC (rev 6650)
@@ -18,6 +18,7 @@
 
 import javax.swing.*;
 import java.util.*;
+import java.util.stream.Collectors;
 
 public class MergePolygonsWithNeighbourPlugIn extends ThreadedBasePlugIn {
 
@@ -41,6 +42,7 @@
   String sAttributeToUse = "Attribute to use";
   String attribute = null;
   String layerName;
+  //String sInvalidFeatureMessage = "Features must be valid to be merged";
   final static String sMERGEMETHOD = "MERGE METHOD";
 
   private MultiInputDialog dialog;
@@ -59,6 +61,7 @@
     sUseAttribute = 
I18N.get("org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute");
     sUseAttributeTooltip = 
I18N.get("org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Use-attribute-tooltip");
     sAttributeToUse = 
I18N.get("org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Attribute");
+    //sInvalidFeatureMessage = 
I18N.get("org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid");
 
     FeatureInstaller featureInstaller = new 
FeatureInstaller(context.getWorkbenchContext());
     featureInstaller.addMainMenuPlugin(
@@ -124,7 +127,9 @@
   private void getDialogValues(MultiInputDialog dialog) {
     this.useArea = dialog.getBoolean(this.sUseArea);
     this.useBorder = dialog.getBoolean(this.sUseBoder);
-    if (dialog.getBoolean(sUseAttribute)) attribute = 
(String)dialog.getComboBox(sAttributeToUse).getSelectedItem();
+    if (dialog.getBoolean(sUseAttribute)) {
+      attribute = 
(String)dialog.getComboBox(sAttributeToUse).getSelectedItem();
+    }
     else attribute = null;
   }
 
@@ -134,58 +139,55 @@
     context.getWorkbenchContext().getLayerManager().setFiringEvents(false);
 
     Layer activeLayer = context.getLayerManager().getLayer(layerName);
+    Collection<Feature> selection = 
context.getWorkbenchContext().getLayerViewPanel()
+            .getSelectionManager().getFeaturesWithSelectedItems();
+    Set<Integer> selectedIds = 
selection.stream().map(Feature::getID).collect(Collectors.toSet());
 
     FeatureCollection fc = activeLayer.getFeatureCollectionWrapper();
-    STRtree index = new STRtree();
     monitor.report("Indexing...");
-    for (Feature f : fc.getFeatures()) {
-      index.insert(f.getGeometry().getEnvelopeInternal(), f);
-    }
+    STRtree index = index(fc);
 
-    Collection<Feature> selection = 
context.getWorkbenchContext().getLayerViewPanel()
-            .getSelectionManager().getFeaturesWithSelectedItems();
+    monitor.report("Building the graph");
+    Map<Integer,Set<Integer>> graph = getPolygonGraph(index, selection, 
monitor);
 
-    Map<Integer,Set<Integer>> graph = getPolygonGraph(index, selection, 
monitor);
-    // ids of a all features which may change
-    Set<Integer> ids = new HashSet<>();
-    for (Map.Entry<Integer,Set<Integer>> entry : graph.entrySet()) {
-      ids.add(entry.getKey());
-      ids.addAll(entry.getValue());
-    }
+    // ids of all features which may change (selection + neighbouring)
+    Set<Integer> graphIds = getGraphIds(graph);
     // Preserve original geometries
+    Map<Integer,Geometry> sourceGeometries = new HashMap<>();
     Map<Integer,Feature> currentFeatures = new HashMap<>();
-    Map<Integer, Geometry> sourceGeometries = new HashMap<>();
+    Map<Integer,Geometry> newGeometries = new HashMap<>();
     for (Feature f : fc.getFeatures()) {
-      if (ids.contains(f.getID())) currentFeatures.put(f.getID(), f);
-      if (ids.contains(f.getID())) sourceGeometries.put(f.getID(), 
(Geometry)f.getGeometry().clone());
+      if (graphIds.contains(f.getID())) currentFeatures.put(f.getID(), f);
+      if (graphIds.contains(f.getID())) sourceGeometries.put(f.getID(), 
(Geometry)f.getGeometry().clone());
     }
 
     monitor.report(sMergingPolygons);
-    int count = 0;
-    for (Feature f : selection) {
-      Feature target = useArea ? chooseMaxAreaNeighbour(f.getID(), graph, 
currentFeatures)
-              : chooseLongestBoundaryNeighbour(f.getID(), graph, 
currentFeatures);
-      monitor.report(++count, selection.size(), sMerged);
-      if (target != null) {
-        merge(f.getID(), target.getID(), graph, currentFeatures);
-        //fc.remove(f);
+    try {
+      int count = 0;
+      for (Feature f : selection) {
+        Feature target = useArea ? chooseMaxAreaNeighbour(f.getID(), graph, 
currentFeatures)
+                : chooseLongestBoundaryNeighbour(f.getID(), graph, 
currentFeatures);
+        monitor.report(++count, selection.size(), sMerged);
+        if (target != null) {
+          merge(f.getID(), target.getID(), graph, currentFeatures);
+        }
       }
+    } finally {
+      // restore original geometries, even if something wrong happened during 
the merge
+      // but before, save new geometries to prepare the transaction which will 
be executed
+      // if no exception occured (transaction is postponed because of the 
iterative nature
+      // of the process where each feature may be modified several times)
+      for (int i : currentFeatures.keySet()) {
+        // Save new geometries from currentFeatures
+        if (selectedIds.contains(i) && !graph.containsKey(i)) 
newGeometries.put(i, null);
+        else newGeometries.put(i, currentFeatures.get(i).getGeometry());
+        // Restore original geometries in currentFeatures
+        currentFeatures.get(i).setGeometry(sourceGeometries.get(i));
+      }
     }
+
     monitor.report("Prepare transaction");
-    // Preserve new calculated geometries and restore original one as
-    // because the graph calculation was not transactional (it is difficult
-    // to make it transactional beacause of its iterative nature)
-    Map<Integer,Geometry> newGeometries = new HashMap<>();
-    ids.clear();
-    for (Feature f : selection) ids.add(f.getID());
-    for (int i : currentFeatures.keySet()) {
-      if (ids.contains(i) && !graph.containsKey(i)) newGeometries.put(i, null);
-      else newGeometries.put(i, currentFeatures.get(i).getGeometry());
-      currentFeatures.get(i).setGeometry(sourceGeometries.get(i));
-    }
-
     // Now make the changes within a transaction
-
     reportNothingToUndoYet(context);
     activeLayer.getLayerManager().getUndoableEditReceiver().startReceiving();
     try {
@@ -198,8 +200,8 @@
             context.getLayerViewPanel()
       );
       for (int i : currentFeatures.keySet()) {
-        if (!graph.containsKey(i))
-        if (ids.contains(i)) {
+        //if (!graph.containsKey(i))
+        if (selectedIds.contains(i)) {
           transaction.deleteFeature(currentFeatures.get(i));
         } else {
           transaction.modifyFeatureGeometry(currentFeatures.get(i), 
newGeometries.get(i));
@@ -206,28 +208,38 @@
         }
       }
       transaction.commit();
-      context.getWorkbenchContext().getLayerManager().setFiringEvents(true);
+      
context.getWorkbenchContext().getLayerViewPanel().getSelectionManager().clear();
       context.getWorkbenchContext().getLayerViewPanel().repaint();
-      transaction.commit();
-      activeLayer.getLayerManager().getUndoableEditReceiver().stopReceiving();
     }
     finally {
+      context.getWorkbenchContext().getLayerManager().setFiringEvents(true);
       activeLayer.getLayerManager().getUndoableEditReceiver().stopReceiving();
     }
   }
 
+  private STRtree index(FeatureCollection fc) {
+    STRtree index = new STRtree();
+    for (Feature f : fc.getFeatures()) {
+      index.insert(f.getGeometry().getEnvelopeInternal(), f);
+    }
+    return index;
+  }
+
   // Create a map containing relations between each selected feature and 
adjacent features
   private Map<Integer,Set<Integer>> getPolygonGraph(SpatialIndex index,
                                                     Collection<Feature> 
selection,
-                                                    TaskMonitor monitor) {
+                                                    TaskMonitor monitor) 
throws Exception {
 
     Map<Integer,Set<Integer>> graph = new HashMap<>();
-    monitor.report("Building the graph");
+    Set<Integer> validated = new HashSet<>();
     int count = 0;
     int total = selection.size();
     for (Feature f : selection) {
       monitor.report(++count, total, "polygons");
       if (f.getGeometry().getDimension() != 2) continue;
+      if (!f.getGeometry().isValid())
+        throw new 
Exception(I18N.getMessage("org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid",
 f.getID()));
+      validated.add(f.getID());
       List<Feature> candidates = 
index.query(f.getGeometry().getEnvelopeInternal());
       int fid = f.getID();
       Set<Integer> neighbours = graph.get(fid);
@@ -236,11 +248,14 @@
         int cid = candidate.getID();
         if (cid == fid) continue;
         if (candidate.getGeometry().getDimension() != 2) continue;
+        if (!validated.contains(cid) && !candidate.getGeometry().isValid())
+          throw new 
Exception(I18N.getMessage("org.openjump.core.ui.plugin.tools.MergeSelectedPolygonsWithNeighbourPlugIn.Features-must-be-valid",
 candidate.getID()));
+        validated.add(cid);
         if (attribute != null && !Objects.equals(f.getAttribute(attribute), 
candidate.getAttribute(attribute))) continue;
-        // Non, \xE7a fait des trous !
-        //if (c.getGeometry().getArea() < f.getGeometry().getArea()) continue;
         if (f.getGeometry().intersects(candidate.getGeometry())) {
-        //if (f.getGeometry().relate(c.getGeometry(),"****1****")) {
+          //relate is slower than intersects and has small benefit
+          //for the following step (choosing the best candidate)
+          //if (f.getGeometry().relate(c.getGeometry(),"****1****")) {
           neighbours.add(candidate.getID());
         }
       }
@@ -249,25 +264,36 @@
     return graph;
   }
 
+  private Set<Integer> getGraphIds(Map<Integer,Set<Integer>> graph) {
+    Set<Integer> ids = new HashSet<>();
+    for (Map.Entry<Integer,Set<Integer>> entry : graph.entrySet()) {
+      ids.add(entry.getKey());
+      ids.addAll(entry.getValue());
+    }
+    return ids;
+  }
+
+
   private boolean merge(int srcId, int dstId, Map<Integer,Set<Integer>> graph, 
Map<Integer,Feature> currentFeatures) {
     Feature src = currentFeatures.get(srcId);
     Feature dst = currentFeatures.get(dstId);
     dst.setGeometry(dst.getGeometry().union(src.getGeometry()));
-    Set<Integer> newDestSet = graph.get(dstId);
-    if (newDestSet != null) newDestSet.remove(srcId);
-    // Neighbours of src
-    for (Integer id : graph.get(srcId)) {
-      if (id != srcId && id != dstId) {
-        if (newDestSet != null) newDestSet.add(id);
-      }
-      if (graph.containsKey(id)) graph.get(id).remove(srcId);
+    Set<Integer> neighboursOfDst = graph.get(dstId);
+    // graph only contains neighbours of selected features
+    if (neighboursOfDst != null) {
+      neighboursOfDst.remove(srcId);
     }
-    if (graph.containsKey(dst.getID())) {
-      for (Integer id : graph.get(dstId)) {
-        if (id != srcId && id != dstId) {
-          if (newDestSet != null) newDestSet.add(id);
-        }
+    // Avoid concurrent modification
+    Set<Integer> neighboursOfSrc = new HashSet<>(graph.get(srcId));
+    for (int id : neighboursOfSrc) {
+      if (id != srcId && id != dstId && neighboursOfDst != null) {
+        neighboursOfDst.add(id);
       }
+      // If id is also a selected feature, update its neighbours
+      if (graph.containsKey(id)) {
+        graph.get(id).remove(srcId);
+        graph.get(id).add(dstId);
+      }
     }
     graph.remove(srcId);
     return true;
@@ -275,19 +301,16 @@
 
   private Feature chooseMaxAreaNeighbour(int fid, Map<Integer,Set<Integer>> 
graph,
                                          Map<Integer,Feature> currentFeatures) 
{
-    Set<Integer> candidates = graph.get(fid);
-    Feature src = currentFeatures.get(fid);
-    double srcArea = src.getGeometry().getArea();
+    Set<Integer> neighbours = graph.get(fid);
     double max = 0;
     Feature selected = null;
-    for (Integer cid : candidates) {
-      Feature candidate = currentFeatures.get(cid);
+    for (int cid : neighbours) {
       if (cid == fid) continue;
-      double area = candidate.getGeometry().getArea();
-      //if (area < srcArea) continue;
+      Feature neighbour = currentFeatures.get(cid);
+      double area = neighbour.getGeometry().getArea();
       if (area > max) {
         max = area;
-        selected = candidate;
+        selected = neighbour;
       }
     }
     return selected;
@@ -295,24 +318,18 @@
 
   private Feature chooseLongestBoundaryNeighbour(int fid, 
Map<Integer,Set<Integer>> graph,
                                                  Map<Integer,Feature> 
currentFeatures) {
-    Set<Integer> candidates = graph.get(fid);
+    Set<Integer> neighbours = graph.get(fid);
     Feature src = currentFeatures.get(fid);
-    double srcArea = src.getGeometry().getArea();
     double max = 0;
     Feature selected = null;
-    if (candidates != null) {
-      for (Integer cid : candidates) {
-        Feature candidate = currentFeatures.get(cid);
-        if (cid == fid) continue;
-        //if (candidate.getGeometry().getArea() < srcArea) continue;
-        double length = 
candidate.getGeometry().intersection(src.getGeometry()).getLength();
-        if (length > max) {
-          max = length;
-          selected = candidate;
-        }
+    for (int cid : neighbours) {
+      if (cid == fid) continue;
+      Feature neighbour = currentFeatures.get(cid);
+      double length = 
neighbour.getGeometry().intersection(src.getGeometry()).getLength();
+      if (length > max) {
+        max = length;
+        selected = neighbour;
       }
-    } else {
-      System.out.println("L'objet " + fid + " n'est plus dans le graph");
     }
     return selected;
   }



_______________________________________________
Jump-pilot-devel mailing list
Jump-pilot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel

Reply via email to