[postgis-tickets] r16035 - Do not snap incoming lines to nodes in isolation

Sandro Santilli strk at kbt.io
Sun Oct 22 08:39:01 PDT 2017


Author: strk
Date: 2017-10-22 08:39:01 -0700 (Sun, 22 Oct 2017)
New Revision: 16035

Modified:
   trunk/liblwgeom/lwgeom_topo.c
Log:
Do not snap incoming lines to nodes in isolation

But rather include edges as the snap target, to avoid moving
vertices that already snapped to edges to move further

Modified: trunk/liblwgeom/lwgeom_topo.c
===================================================================
--- trunk/liblwgeom/lwgeom_topo.c	2017-10-22 15:38:54 UTC (rev 16034)
+++ trunk/liblwgeom/lwgeom_topo.c	2017-10-22 15:39:01 UTC (rev 16035)
@@ -5694,7 +5694,7 @@
   LWT_ELEMID *ids;
   LWT_ISO_EDGE *edges;
   LWT_ISO_NODE *nodes;
-  int num;
+  int num, numedges=0, numnodes=0;
   int i;
   GBOX qbox;
 
@@ -5726,21 +5726,26 @@
   LWDEBUGF(1, "BOX expanded by %g is %.15g %.15g, %.15g %.15g",
               tol, qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax);
 
+  LWGEOM **nearby = 0;
+  int nearbyindex=0;
+  int nearbycount = 0;
+
   /* 2. Node to edges falling within tol distance */
-  edges = lwt_be_getEdgeWithinBox2D( topo, &qbox, &num, LWT_COL_EDGE_ALL, 0 );
-  if ( num == -1 )
+  edges = lwt_be_getEdgeWithinBox2D( topo, &qbox, &numedges, LWT_COL_EDGE_ALL, 0 );
+  if ( numedges == -1 )
   {
     lwgeom_free(noded);
     lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
     return NULL;
   }
-  LWDEBUGF(1, "Line has %d points, its bbox intersects %d edges bboxes", line->points->npoints, num);
-  if ( num )
+  LWDEBUGF(1, "Line has %d points, its bbox intersects %d edges bboxes",
+    line->points->npoints, numedges);
+  if ( numedges )
   {{
     /* collect those whose distance from us is < tol */
-    LWGEOM **nearby = lwalloc(sizeof(LWGEOM *)*num);
-    int nn=0;
-    for (i=0; i<num; ++i)
+    nearbycount += numedges;
+    nearby = lwalloc(nearbycount * sizeof(LWGEOM *));
+    for (i=0; i<numedges; ++i)
     {
       LW_ON_INTERRUPT(return NULL);
       LWT_ISO_EDGE *e = &(edges[i]);
@@ -5749,20 +5754,20 @@
       double dist = lwgeom_mindistance2d(g, noded);
       /* must be closer than tolerated, unless distance is zero */
       if ( dist && dist >= tol ) continue;
-      nearby[nn++] = g;
+      nearby[nearbyindex++] = g;
     }
-    LWDEBUGF(2, "Found %d lines closer than tolerance (%g)", nn, tol);
-    if ( nn )
+    LWDEBUGF(2, "Found %d lines closer than tolerance (%g)", nearbyindex, tol);
+    if ( nearbyindex )
     {{
       LWCOLLECTION *col;
       LWGEOM *iedges; /* just an alias for col */
       LWGEOM *snapped;
       LWGEOM *set1, *set2;
 
-      LWDEBUGF(1, "Line intersects %d edges", nn);
+      LWDEBUGF(1, "Line intersects %d edges", nearbyindex);
 
       col = lwcollection_construct(COLLECTIONTYPE, topo->srid,
-                                   NULL, nn, nearby);
+                                   NULL, nearbyindex, nearby);
       iedges = lwcollection_as_lwgeom(col);
       LWDEBUGG(1, iedges, "Collected edges");
       LWDEBUGF(1, "Snapping noded, with srid=%d "
@@ -5798,79 +5803,94 @@
       /* will not release the geoms array */
       lwcollection_release(col);
     }}
-    lwfree(nearby);
-    _lwt_release_edges(edges, num);
   }}
 
   /* 2.1. Node with existing nodes within tol
    * TODO: check if we should be only considering _isolated_ nodes! */
-  nodes = lwt_be_getNodeWithinBox2D( topo, &qbox, &num, LWT_COL_NODE_ALL, 0 );
-  if ( num == -1 )
+  nodes = lwt_be_getNodeWithinBox2D( topo, &qbox, &numnodes, LWT_COL_NODE_ALL, 0 );
+  if ( numnodes == -1 )
   {
     lwgeom_free(noded);
     lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
     return NULL;
   }
-  LWDEBUGF(1, "Line bbox intersects %d nodes bboxes", num);
-  if ( num )
+  LWDEBUGF(1, "Line bbox intersects %d nodes bboxes", numnodes);
+  int nearbyedgecount = nearbyindex;
+  if ( numnodes )
   {{
     /* collect those whose distance from us is < tol */
-    LWGEOM **nearby = lwalloc(sizeof(LWGEOM *)*num);
-    int nn=0;
-    for (i=0; i<num; ++i)
+    nearbycount = nearbyindex + numnodes;
+    nearby = nearby ?
+        lwrealloc(nearby, nearbycount * sizeof(LWGEOM *))
+        :
+        lwalloc(nearbycount * sizeof(LWGEOM *))
+        ;
+    int nn = 0;
+    for (i=0; i<numnodes; ++i)
     {
       LWT_ISO_NODE *n = &(nodes[i]);
       LWGEOM *g = lwpoint_as_lwgeom(n->geom);
       double dist = lwgeom_mindistance2d(g, noded);
       /* must be closer than tolerated, unless distance is zero */
       if ( dist && dist >= tol ) continue;
-      nearby[nn++] = g;
+      nearby[nearbyindex++] = g;
+      ++nn;
     }
-    if ( nn )
-    {{
-      LWCOLLECTION *col;
-      LWGEOM *inodes; /* just an alias for col */
-      LWGEOM *tmp;
+  }}
 
-      LWDEBUGF(1, "Line intersects %d nodes", nn);
+  LWDEBUGF(1, "Number of nearby elements is %d", nearbyindex);
 
-      col = lwcollection_construct(MULTIPOINTTYPE, topo->srid,
-                                   NULL, nn, nearby);
-      inodes = lwcollection_as_lwgeom(col);
+  if ( numnodes )
+  {{
+    LWCOLLECTION *col;
+    LWGEOM *elems;
 
-      LWDEBUGG(1, inodes, "Collected nodes");
+    col = lwcollection_construct(COLLECTIONTYPE, topo->srid,
+                                 NULL, nearbyindex, nearby);
+    elems = lwcollection_as_lwgeom(col);
 
-      /* TODO: consider snapping once against all elements
-       *      (rather than once with edges and once with nodes) */
-      tmp = _lwt_toposnap(noded, inodes, tol);
-      lwgeom_free(noded);
-      noded = tmp;
-      LWDEBUGG(1, noded, "Node-snapped");
+    LWDEBUGG(1, elems, "Collected nearby elements");
 
+    tmp = _lwt_toposnap(noded, elems, tol);
+    lwgeom_free(noded);
+    noded = tmp;
+    LWDEBUGG(1, noded, "Elements-snapped");
+
+    /* will not release the geoms array */
+    lwcollection_release(col);
+
+    if ( numnodes )
+    {{
+      col = lwcollection_construct(MULTIPOINTTYPE, topo->srid,
+                             NULL, nearbyindex-nearbyedgecount,
+                             nearby + nearbyedgecount);
+      LWGEOM *inodes = lwcollection_as_lwgeom(col);
       tmp = _lwt_split_by_nodes(noded, inodes);
-          /* lwgeom_split(noded, inodes); */
       lwgeom_free(noded);
       noded = tmp;
       LWDEBUGG(1, noded, "Node-split");
-
       /* will not release the geoms array */
       lwcollection_release(col);
+    }}
 
-      /*
-      -- re-node to account for ST_Snap introduced self-intersections
-      -- See http://trac.osgeo.org/postgis/ticket/1714
-      -- TODO: consider running UnaryUnion once after all noding
-      */
-      tmp = lwgeom_unaryunion(noded);
-      lwgeom_free(noded);
-      noded = tmp;
-      LWDEBUGG(1, noded, "Unary-unioned");
+    /*
+    -- re-node to account for ST_Snap introduced self-intersections
+    -- See http://trac.osgeo.org/postgis/ticket/1714
+    -- TODO: consider running UnaryUnion once after all noding
+    */
+    tmp = lwgeom_unaryunion(noded);
+    lwgeom_free(noded);
+    noded = tmp;
+    LWDEBUGG(1, noded, "Unary-unioned");
 
-    }}
-    lwfree(nearby);
-    _lwt_release_nodes(nodes, num);
   }}
 
+  LWDEBUG(1, "Freeing up nearby elements");
+
+  if ( nearby ) lwfree(nearby);
+  if ( nodes ) _lwt_release_nodes(nodes, numnodes);
+  if ( edges ) _lwt_release_edges(edges, numedges);
+
   LWDEBUGG(1, noded, "Finally-noded");
 
   /* 3. For each (now-noded) segment, insert an edge */



More information about the postgis-tickets mailing list