[SCM] PostGIS branch master updated. 3.5.0-15-g45d2fc8cc

git at osgeo.org git at osgeo.org
Thu Oct 3 00:58:27 PDT 2024


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".

The branch, master has been updated
       via  45d2fc8ccee5abce925f250dde040da5d0520795 (commit)
      from  e11ea6aff2045d28206e2dbc027359611a2a843a (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 45d2fc8ccee5abce925f250dde040da5d0520795
Author: Sandro Santilli <strk at kbt.io>
Date:   Wed Oct 2 17:02:56 2024 +0200

    Check that ST_ChangeEdgeGeom doesn't change winding of rings
    
    References #5787 in master branch (3.6.0dev)

diff --git a/liblwgeom/topo/lwgeom_topo.c b/liblwgeom/topo/lwgeom_topo.c
index 922cdd12a..c7c650d90 100644
--- a/liblwgeom/topo/lwgeom_topo.c
+++ b/liblwgeom/topo/lwgeom_topo.c
@@ -1517,7 +1517,7 @@ _lwt_InitEdgeEndByLine(edgeend *fee, edgeend *lee, LWLINE *edge,
 /*
  * Find the first edges encountered going clockwise and counterclockwise
  * around a node, starting from the given azimuth, and take
- * note of the face on the both sides.
+ * note of the face on both sides.
  *
  * @param topo the topology to load edges from
  * @param node the identifier of the node to analyze
@@ -3269,6 +3269,7 @@ lwt_ChangeEdgeGeom(LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, LWLINE *geom)
   POINT2D p1, p2, pt;
   uint64_t i;
   int isclosed = 0;
+  int leftRingIsCCW = -1;
 
   /* curve must be simple */
   if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
@@ -3454,6 +3455,62 @@ lwt_ChangeEdgeGeom(LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, LWLINE *geom)
               span_pre.nextCW, span_pre.nextCCW,
               epan_pre.nextCW, epan_pre.nextCCW);
 
+  /* If the same edge is both on CW and CCW direction on both start
+   * and end points we need to verify winding of the left and right
+   * rings to verify we didn't twist.
+   * See https://trac.osgeo.org/postgis/ticket/5787
+   *
+   * NOTE: this could probably replace the "isclosed" test.
+   *
+   * NOTE: if either start or end node had different CW and CCW
+   *       edges a twist would be cought in the previous check.
+   */
+  if ( ! isclosed &&
+       oldedge->face_left != oldedge->face_right &&
+       span_pre.nextCW == span_pre.nextCCW &&
+       epan_pre.nextCW == epan_pre.nextCCW )
+  {{
+    uint64_t num_signed_edge_ids;
+    LWT_ELEMID *signed_edge_ids;
+    LWPOLY *shell;
+
+    lwnotice("Twist check before");
+    signed_edge_ids = lwt_be_getRingEdges(topo, edge_id, &num_signed_edge_ids, 0);
+    /* Get winding of left face ring */
+    if (!signed_edge_ids)
+    {
+      //PGTOPO_BE_ERRORF("no ring edges for edge %" LWTFMT_ELEMID, sedge);
+      PGTOPO_BE_ERROR();
+      return -1;
+    }
+    LWDEBUGF(1, "getRingEdges returned %d edges", num_signed_edge_ids);
+
+    shell = _lwt_MakeRingShell(topo, signed_edge_ids, num_signed_edge_ids);
+    if ( ! shell ) {
+      lwfree( signed_edge_ids );
+      /* ring_edges should be NULL */
+      lwerror("Could not create ring shell: %s", lwt_be_lastErrorMessage(topo->be_iface));
+      return -1;
+    }
+
+    const POINTARRAY *pa = shell->rings[0];
+    if ( ! ptarray_is_closed_2d(pa) )
+    {
+      lwpoly_free(shell);
+      lwfree( signed_edge_ids );
+      lwerror("Corrupted topology: ring of edge %" LWTFMT_ELEMID
+              " is geometrically not-closed", edge_id);
+      return -1;
+    }
+
+    leftRingIsCCW = ptarray_isccw(pa);
+    lwpoly_free(shell);
+    lwfree( signed_edge_ids );
+
+    lwnotice("Ring of edge %" LWTFMT_ELEMID " is %sclockwise", edge_id, leftRingIsCCW ? "counter" : "");
+  }}
+
+
   /* update edge geometry */
   newedge.edge_id = edge_id;
   newedge.geom = geom;
@@ -3513,6 +3570,55 @@ lwt_ChangeEdgeGeom(LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, LWLINE *geom)
     return -1;
   }}
 
+  /* Check winding of left face ring did not change */
+  if ( leftRingIsCCW != -1 )
+  {{
+    uint64_t num_signed_edge_ids;
+    LWT_ELEMID *signed_edge_ids;
+    LWPOLY *shell;
+    int isCCW;
+
+    lwnotice("Twist check after");
+    signed_edge_ids = lwt_be_getRingEdges(topo, edge_id, &num_signed_edge_ids, 0);
+    /* Get winding of left face ring */
+    if (!signed_edge_ids)
+    {
+      //PGTOPO_BE_ERRORF("no ring edges for edge %" LWTFMT_ELEMID, sedge);
+      PGTOPO_BE_ERROR();
+      return -1;
+    }
+    LWDEBUGF(1, "getRingEdges returned %d edges", num_signed_edge_ids);
+
+    shell = _lwt_MakeRingShell(topo, signed_edge_ids, num_signed_edge_ids);
+    if ( ! shell ) {
+      lwfree( signed_edge_ids );
+      /* ring_edges should be NULL */
+      lwerror("Could not create ring shell: %s", lwt_be_lastErrorMessage(topo->be_iface));
+      return -1;
+    }
+
+    const POINTARRAY *pa = shell->rings[0];
+    if ( ! ptarray_is_closed_2d(pa) )
+    {
+      lwpoly_free(shell);
+      lwfree( signed_edge_ids );
+      lwerror("Corrupted topology: ring of edge %" LWTFMT_ELEMID
+              " is geometrically not-closed", edge_id);
+      return -1;
+    }
+
+    isCCW = ptarray_isccw(pa);
+    lwpoly_free(shell);
+    lwfree( signed_edge_ids );
+
+    if ( isCCW != leftRingIsCCW )
+    {
+      _lwt_release_edges(oldedge, 1);
+      lwerror("Edge ring changes winding");
+      return -1;
+    }
+  }}
+
   /*
   -- Update faces MBR of left and right faces
   -- TODO: think about ways to optimize this part, like see if
diff --git a/topology/test/regress/st_changeedgegeom.sql b/topology/test/regress/st_changeedgegeom.sql
index 7f2605e4e..769157a5f 100644
--- a/topology/test/regress/st_changeedgegeom.sql
+++ b/topology/test/regress/st_changeedgegeom.sql
@@ -186,3 +186,11 @@ AND NOT ST_Equals(
     )
   );
 SELECT NULL FROM topology.DropTopology('t5709');
+
+-- See https://trac.osgeo.org/postgis/ticket/5787
+SELECT NULL FROM topology.CreateTopology('t5787');
+SELECT NULL FROM topology.TopoGeo_addLinestring('t5787', 'LINESTRING(0 0, 5 5, 10 0)');
+SELECT NULL FROM topology.TopoGeo_addLinestring('t5787', 'LINESTRING(0 0, 10 0)');
+SELECT '#5787', 'unexpected success' FROM topology.ST_ChangeEdgeGeom('t5787', 1, 'LINESTRING(0 0, 5 -5, 10 0)');
+SELECT '#5787', 'unexpected invalidity', * FROM topology.ValidateTopology('t5787');
+SELECT NULL FROM topology.DropTopology('t5787');
diff --git a/topology/test/regress/st_changeedgegeom_expected b/topology/test/regress/st_changeedgegeom_expected
index f2bc4fb95..bfb094b73 100644
--- a/topology/test/regress/st_changeedgegeom_expected
+++ b/topology/test/regress/st_changeedgegeom_expected
@@ -37,3 +37,4 @@ T13.3|26
 ERROR:  Edge motion collision at POINT(-1.1697 47.7825)
 Topology 'city_data' dropped
 t5709|edge|1
+ERROR:  Edge ring changes winding

-----------------------------------------------------------------------

Summary of changes:
 liblwgeom/topo/lwgeom_topo.c                     | 108 ++++++++++++++++++++++-
 topology/test/regress/st_changeedgegeom.sql      |   8 ++
 topology/test/regress/st_changeedgegeom_expected |   1 +
 3 files changed, 116 insertions(+), 1 deletion(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list