[SCM] PostGIS branch stable-3.5 updated. 3.5.0-5-g6e378cfbb

git at osgeo.org git at osgeo.org
Thu Oct 3 01:06:45 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, stable-3.5 has been updated
       via  6e378cfbb992ae7b6ebbb87ac0b8e56bf4e9fded (commit)
      from  bdecd88fe3f506b2b1f64de3f6cf25dc8f102f66 (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 6e378cfbb992ae7b6ebbb87ac0b8e56bf4e9fded
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 3.5 branch (3.5.1dev)

diff --git a/NEWS b/NEWS
index 10ee4f7dd..f91a3a24f 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,8 @@ PostgreSQL 12-17 required. GEOS 3.8+ required. Proj 6.1+ required.
 
 - #5785, [raster] ST_MapAlgebra segfaults when expression references
          a supernumerary rast argument (Dian M Fay)
+- #5787, Check that ST_ChangeEdgeGeom doesn't change winding of rings
+         (Sandro Santilli)
 
 * Enhancements *
 
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:
 NEWS                                             |   2 +
 liblwgeom/topo/lwgeom_topo.c                     | 108 ++++++++++++++++++++++-
 topology/test/regress/st_changeedgegeom.sql      |   8 ++
 topology/test/regress/st_changeedgegeom_expected |   1 +
 4 files changed, 118 insertions(+), 1 deletion(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list