[SCM] PostGIS branch master updated. 3.7.0rc1-94-g026463814
git at osgeo.org
git at osgeo.org
Wed Oct 7 00:29:49 PDT 2026
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 0264638140f7c516a5dca4387e7c742822488c3f (commit)
via 9425e81dba350754a138ecb569d9f2cf3af5a88c (commit)
via 7b4f46cde3afd117331828efdb38c8bd6ff0ad13 (commit)
via d03570c8597d49e4ca382283b3385c22f52a5b7a (commit)
from 41ac1d9faf4cb737f12ece8eb6bbe2c38476bdf3 (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 0264638140f7c516a5dca4387e7c742822488c3f
Author: Sandro Santilli <strk at kbt.io>
Date: Wed Oct 7 09:29:33 2026 +0200
Add NEWS item
diff --git a/NEWS b/NEWS
index 0620786c3..ce2670387 100644
--- a/NEWS
+++ b/NEWS
@@ -66,6 +66,8 @@ PostgreSQL instance.
* Enhancements *
+ - #6140, #6138, #6129, [topology] improve topology building and checking
+ robustness (Sandro Santilli)
- #2045, pgsql2shp query dumps no longer require temporary table
privileges (Darafei Praliaskouski)
- #1577, [loader] Report unsupported .zip archive input before trying
commit 9425e81dba350754a138ecb569d9f2cf3af5a88c
Author: Sandro Santilli <strk at kbt.io>
Date: Tue Oct 6 14:51:26 2026 +0200
Add test for ST_AddEdgeModFace issue #6140
diff --git a/topology/test/regress/st_addedgemodface.sql b/topology/test/regress/st_addedgemodface.sql
index 62fdc09d6..fdbb4dcf4 100644
--- a/topology/test/regress/st_addedgemodface.sql
+++ b/topology/test/regress/st_addedgemodface.sql
@@ -542,6 +542,20 @@ END;
$TEST$ LANGUAGE 'plpgsql';
ROLLBACK; -- restores the topology
+-----------------------------------------------------
+-- Very close segments
+-----------------------------------------------------
+
+BEGIN;
+SELECT 't6140', 'start';
+SELECT NULL FROM CreateTopology('t6140');
+SELECT NULL FROM ST_AddIsoNode('t6140', 0, '01010000005163B45884333540352259FDE66A5140'); -- start node of edge 1
+SELECT NULL FROM ST_AddIsoNode('t6140', 0, '0101000000685E205B88333540B7363CF4E66A5140'); -- start node of edge 2
+SELECT NULL FROM ST_AddIsoNode('t6140', 0, '0101000000831D215B88333540107B41F4E66A5140'); -- end node of both edges
+SELECT NULL FROM ST_AddEdgeModFace('t6140', 1, 3, '0102000000030000005163B45884333540352259FDE66A51406043FEC3BA333540C0BFF081E66A5140831D215B88333540107B41F4E66A5140');
+SELECT 't6140', 'unexpected success', * FROM ST_AddEdgeModFace('t6140', 2, 3, '010200000002000000685E205B88333540B7363CF4E66A5140831D215B88333540107B41F4E66A5140');
+ROLLBACK;
+
---------------------------------------------------------------------
-- Cleanups
---------------------------------------------------------------------
diff --git a/topology/test/regress/st_addedgemodface_expected b/topology/test/regress/st_addedgemodface_expected
index 7a4be20d8..4b92f3a5f 100644
--- a/topology/test/regress/st_addedgemodface_expected
+++ b/topology/test/regress/st_addedgemodface_expected
@@ -203,4 +203,6 @@ F33|POLYGON((38 40,38 43,41 43,41 40,38 40))
F34|POLYGON((35 25,35 45,63 45,63 25,35 25))
F35|POLYGON((9 14,9 22,21 22,21 14,9 14))
ERROR: Backend error: Corrupted topology: ring of edge 58 is topologically non-closed
+t6140|start
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 1 at or near POINT(21.2012993770454 69.6703463210545)
Topology 'city_data' dropped
commit 7b4f46cde3afd117331828efdb38c8bd6ff0ad13
Author: Sandro Santilli <strk at kbt.io>
Date: Tue Oct 6 14:48:54 2026 +0200
Add test for ValidateTopology issue #6138
diff --git a/topology/test/regress/validatetopology.sql b/topology/test/regress/validatetopology.sql
index aa852bb0d..670b29159 100644
--- a/topology/test/regress/validatetopology.sql
+++ b/topology/test/regress/validatetopology.sql
@@ -243,3 +243,37 @@ VALUES
SELECT '#6065', 'invalidities', * FROM topology.ValidateTopology('t6065');
ROLLBACK;
+
+
+-- See https://trac.osgeo.org/postgis/ticket/6138
+BEGIN;
+SELECT NULL FROM CreateTopology('t6138');
+
+COPY t6138.node(node_id,geom) FROM stdin;
+1 0101000000831D215B88333540107B41F4E66A5140
+2 01010000005163B45884333540352259FDE66A5140
+3 0101000000685E205B88333540B7363CF4E66A5140
+\.
+
+COPY t6138.edge_data(
+ edge_id,
+ start_node,
+ end_node,
+ next_left_edge,
+ abs_next_left_edge,
+ next_right_edge,
+ abs_next_right_edge,
+ left_face,
+ right_face,
+ geom
+)
+FROM stdin;
+1 2 1 -2 2 1 1 0 0 0102000000030000005163B45884333540352259FDE66A51406043FEC3BA333540C0BFF081E66A5140831D215B88333540107B41F4E66A5140
+2 3 1 -1 1 2 2 0 0 010200000002000000685E205B88333540B7363CF4E66A5140831D215B88333540107B41F4E66A5140
+\.
+
+SELECT NULL FROM setval('t6138.edge_data_edge_id_seq', 2);
+SELECT NULL FROM setval('t6138.node_node_id_seq', 3);
+
+SELECT 't6139', 'invalidity', * FROM ValidateTopology('t6138');
+SELECT 't6138', 'end of validation';
diff --git a/topology/test/regress/validatetopology_expected b/topology/test/regress/validatetopology_expected
index 5f667e048..efc2359ce 100644
--- a/topology/test/regress/validatetopology_expected
+++ b/topology/test/regress/validatetopology_expected
@@ -47,3 +47,5 @@
#5766.2|overlapping bbox|("face has wrong mbr",0,)
#5766.2|disjoint bbox|
#6065|invalidities|hole not in advertised face|-4|
+t6139|invalidity|edge crosses edge|1|2
+t6138|end of validation
commit d03570c8597d49e4ca382283b3385c22f52a5b7a
Author: Sandro Santilli <strk at kbt.io>
Date: Mon Oct 5 15:58:31 2026 +0200
Implement edge crossing internally
Adds a lwline_have_nonboundary_2d_intersection function,
expose it to SQL via topology.TopoEdgesHaveInteriorIntersection
Include unit tests for the liblwgeom function
WARNING: changes topology error messages to not distinguish anymore
between "crosses", "coincident" and "intersects".
diff --git a/liblwgeom/cunit/cu_algorithm.c b/liblwgeom/cunit/cu_algorithm.c
index d9b3c0e25..9c2434e4a 100644
--- a/liblwgeom/cunit/cu_algorithm.c
+++ b/liblwgeom/cunit/cu_algorithm.c
@@ -1880,6 +1880,157 @@ static void test_ptarray_simplify(void)
lwgeom_free(geom2);
}
+/*
+** Assert the intersection status of a pair of lines described by WKT.
+**
+** The check must be symmetric, so both argument orders are verified.
+*/
+#define ASSERT_NONBOUNDARY_INTERSECTION(wkt1, wkt2, expected) \
+ do \
+ { \
+ LWLINE *l1 = (LWLINE *)lwgeom_from_wkt(wkt1, LW_PARSER_CHECK_NONE); \
+ LWLINE *l2 = (LWLINE *)lwgeom_from_wkt(wkt2, LW_PARSER_CHECK_NONE); \
+ int exp = (expected); \
+ CU_ASSERT_EQUAL(lwline_have_nonboundary_2d_intersection(l1, l2), exp); \
+ CU_ASSERT_EQUAL(lwline_have_nonboundary_2d_intersection(l2, l1), exp); \
+ lwline_free(l1); \
+ lwline_free(l2); \
+ } while (0)
+
+static void test_lwline_have_nonboundary_2d_intersection(void)
+{
+ /* Non intersecting lines */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(200 0, 400 0)", 0);
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(0 50, 100 50)", 0);
+ /* The two lines would cross, but only outside of both segments */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(200 100, 200 -100)", 0);
+ /* The two lines would touch, but only outside of both segments */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(-100 0, -50 0)", 0);
+ /* Lines parallel and apart, also on a diagonal */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 100)", "LINESTRING(0 50, 100 150)", 0);
+
+ /* Touching on a boundary point of both lines */
+ /* End to start */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(100 0, 200 0)", 0);
+ /* Start to start */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(0 0, 0 100)", 0);
+ /* Start to end */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(100 0, 100 100)", 0);
+ /* End to end */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(200 0, 100 0)", 0);
+ /* Same point, opposite directions */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(100 0, 0 0)", "LINESTRING(0 0, -100 0)", 0);
+ /* Collinear, touching end to start on a vertical line */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 0 100)", "LINESTRING(0 100, 0 200)", 0);
+ /* Collinear, touching end to start on a diagonal */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 100)", "LINESTRING(100 100, 200 200)", 0);
+ /* Collinear, touching on the interior vertex of the first line */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 50 0, 100 0)", "LINESTRING(100 0, 150 0)", 0);
+ /* Diverging from the shared start point */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(0 0, 50 50)", 0);
+ /* The endpoint of the second line is the start point of the first */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(-50 0, 0 0)", 0);
+ /* Zero length second line, on the boundary of the first one */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(100 0, 100 0)", 0);
+ /* Zero length lines, on the same point */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(50 50, 50 50)", "LINESTRING(50 50, 50 50)", 0);
+ /* Closed line, touching another line on its boundary point */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0, 100 100, 0 100, 0 0)",
+ "LINESTRING(0 0, -100 0)",
+ 0);
+
+ /* Crossing lines */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(50 10, 50 -10)", 1);
+ /* Crossing on a diagonal */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 100)", "LINESTRING(0 100, 100 0)", 1);
+ /* Crossing, but one line is zero length and on the other one interior */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(50 0, 50 0)", 1);
+
+ /* Identical lines */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(0 0, 100 0)", 1);
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 50 0, 100 0)", "LINESTRING(0 0, 50 0, 100 0)", 1);
+ /* Identical lines, opposite direction, on a vertical */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 0 100)", "LINESTRING(0 100, 0 0)", 1);
+
+ /* Endpoint of one line on the interior of the other one */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(50 0, 50 50)", 1);
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(50 -50, 50 0)", 1);
+ /* Zero length first line, on the interior of the second one */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(50 0, 50 0)", "LINESTRING(0 0, 100 0)", 1);
+ /* Collinear, zero length second line on the interior of the first one */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 50 0, 100 0)", "LINESTRING(50 0, 50 0)", 1);
+ /* Interior vertex of the first line, endpoint of the second one */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 50 0, 100 0)", "LINESTRING(50 0, 50 100)", 1);
+ /* Interior vertex of the first line, interior of the second one */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 50 0, 100 0)", "LINESTRING(50 -50, 50 50)", 1);
+ /* Collinear, overlapping for a positive length */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(50 0, 150 0)", 1);
+ /* Collinear, overlapping for a positive length, opposite direction */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(100 0, 0 0)", "LINESTRING(-50 0, 50 0)", 1);
+ /* Collinear, second line contained in the first one */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(25 0, 75 0)", 1);
+ /* Collinear, overlapping on a diagonal */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 100)", "LINESTRING(50 50, 150 150)", 1);
+ /* Collinear, overlapping on a vertical */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 0 100)", "LINESTRING(0 50, 0 200)", 1);
+ /* Collinear, overlapping, the shared point is on the line boundary */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(0 0, 50 0)", 1);
+ /* Collinear, overlapping across multiple segments */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(-50 0, 25 0, 150 0)", 1);
+ /* Collinear, the second line is back and forth over the first one */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(50 0, 100 100, 50 0)", 1);
+ /* Second line touches an interior vertex of first line */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0, 200 0)", "LINESTRING(100 0, 100 -50)", 1);
+
+ /*
+ ** Multiple intersections on the same pair of lines, the shared
+ ** boundary point must not hide the non-boundary ones
+ */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING(0 0, 50 50, 50 -50, 0 0)", 1);
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)",
+ "LINESTRING(0 0, 50 50, 100 0, 50 -50, 0 0)",
+ 1);
+
+ /* Z and M coordinates are ignored */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING Z (0 0 100, 100 0 200)",
+ "LINESTRING M (50 10 1, 50 -10 2)",
+ 1);
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING Z (0 0 1, 100 0 1)", "LINESTRING Z (0 0 2, 100 0 2)", 1);
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING ZM (0 0 1 1, 100 0 1 1)",
+ "LINESTRING Z (100 0 9, 100 0 9)",
+ 0);
+
+ /* Empty lines have nothing to intersect */
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING EMPTY", "LINESTRING(0 0, 100 0)", 0);
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING(0 0, 100 0)", "LINESTRING EMPTY", 0);
+ ASSERT_NONBOUNDARY_INTERSECTION("LINESTRING EMPTY", "LINESTRING EMPTY", 0);
+
+ /* Single point lines have no segment to intersect */
+ {
+ POINT4D pt;
+ LWLINE *single, *line;
+
+ pt.x = 0;
+ pt.y = 0;
+ pt.z = 0;
+ pt.m = 0;
+
+ single = lwline_construct_empty(SRID_UNKNOWN, 0, 0);
+ ptarray_append_point(single->points, &pt, LW_TRUE);
+ line = (LWLINE*)lwgeom_from_wkt("LINESTRING(0 0, 100 0)", LW_PARSER_CHECK_NONE);
+
+ CU_ASSERT_EQUAL( lwline_have_nonboundary_2d_intersection(single, line), 0 );
+ CU_ASSERT_EQUAL( lwline_have_nonboundary_2d_intersection(line, single), 0 );
+ CU_ASSERT_EQUAL( lwline_have_nonboundary_2d_intersection(single, single), 0 );
+
+ lwline_free(single);
+ lwline_free(line);
+ }
+
+ /* NULL lines are handled gracefully */
+ CU_ASSERT_EQUAL( lwline_have_nonboundary_2d_intersection(NULL, NULL), 0 );
+}
+
/*
** Used by test harness to register the tests in this file.
@@ -1920,4 +2071,5 @@ void algorithms_suite_setup(void)
PG_ADD_TEST(suite,test_lwpoly_construct_circle);
PG_ADD_TEST(suite,test_trim_bits);
PG_ADD_TEST(suite,test_lwgeom_remove_repeated_points);
+ PG_ADD_TEST(suite,test_lwline_have_nonboundary_2d_intersection);
}
diff --git a/liblwgeom/liblwgeom_internal.h b/liblwgeom/liblwgeom_internal.h
index d55f1bb1e..179ac4460 100644
--- a/liblwgeom/liblwgeom_internal.h
+++ b/liblwgeom/liblwgeom_internal.h
@@ -481,6 +481,7 @@ int ptarray_raycast_intersections(const POINTARRAY *pa, const POINT2D *p, int *o
int ptarrayarc_raycast_intersections(const POINTARRAY *pa, const POINT2D *p, int *on_boundary);
int lwcompound_contains_point(const LWCOMPOUND *comp, const POINT2D *pt);
int lwgeom_contains_point(const LWGEOM *geom, const POINT2D *pt);
+int lwline_have_nonboundary_2d_intersection(const LWLINE *line1, const LWLINE *line2);
/**
* Split a line by a point and push components to the provided multiline.
diff --git a/liblwgeom/lwline.c b/liblwgeom/lwline.c
index 50185f510..d8398dfc0 100644
--- a/liblwgeom/lwline.c
+++ b/liblwgeom/lwline.c
@@ -747,3 +747,264 @@ lwline_extend(const LWLINE *line, double distance_forward, double distance_backw
}
return lwline_construct(line->srid, NULL, opa);
}
+
+/*
+* The ways two line segments can intersect.
+* Returned by lwsegment_intersection()
+*/
+enum LWSEGMENT_INTERSECTION
+{
+ /* The segments do not intersect */
+ LWSEG_INTERSECT_NONE = 0,
+ /* The segments are collinear and overlap for a positive length */
+ LWSEG_INTERSECT_OVERLAP,
+ /* The segments cross at a point interior to both of them */
+ LWSEG_INTERSECT_CROSS,
+ /* The segments touch at a single point, an endpoint of one of them */
+ LWSEG_INTERSECT_POINT
+};
+
+/**
+* Does the point lie on the segment, endpoints included?
+*
+* Unlike lw_pt_on_segment(), which excludes the segment endpoints.
+*
+* @param A1 first point of the segment
+* @param A2 second point of the segment
+* @param P the point to test
+*
+* @return LW_TRUE if P is on the A1/A2 segment
+*/
+static int
+lw_pt_on_segment_inclusive(const POINT2D *A1, const POINT2D *A2, const POINT2D *P)
+{
+ if ( lw_segment_side(A1, A2, P) != 0 )
+ return LW_FALSE;
+
+ /* The point is on the segment line, check it's within its extent */
+ return (P->x >= FP_MIN(A1->x, A2->x) && P->x <= FP_MAX(A1->x, A2->x) &&
+ P->y >= FP_MIN(A1->y, A2->y) && P->y <= FP_MAX(A1->y, A2->y));
+}
+
+/**
+* How do the segment (P1,P2) and the segment (Q1,Q2) intersect?
+*
+* Only 2D coordinates are considered. Zero length segments are supported,
+* they can only intersect at their single point.
+*
+* @param P1 first point of the first segment
+* @param P2 second point of the first segment
+* @param Q1 first point of the second segment
+* @param Q2 second point of the second segment
+* @param ipoint storage for the intersection point, set when the returned
+* value is #LWSEG_INTERSECT_POINT
+*
+* @return one of the #LWSEGMENT_INTERSECTION values
+*/
+static int
+lwsegment_intersection(const POINT2D *P1,
+ const POINT2D *P2,
+ const POINT2D *Q1,
+ const POINT2D *Q2,
+ POINT2D *ipoint)
+{
+ int s11, s12, s21, s22;
+ int zeroP, zeroQ;
+ double len2, tQ1, tQ2, tmin, tmax;
+
+ zeroP = P2D_SAME_STRICT(P1, P2);
+ zeroQ = P2D_SAME_STRICT(Q1, Q2);
+
+ /* Zero length segments, if they intersect, do it on their single point */
+ if ( zeroP || zeroQ )
+ {
+ if ( zeroP && zeroQ )
+ {
+ if ( ! P2D_SAME_STRICT(P1, Q1) )
+ return LWSEG_INTERSECT_NONE;
+ }
+ else if ( zeroP )
+ {
+ if ( ! lw_pt_on_segment_inclusive(Q1, Q2, P1) )
+ return LWSEG_INTERSECT_NONE;
+ }
+ else
+ {
+ if ( ! lw_pt_on_segment_inclusive(P1, P2, Q1) )
+ return LWSEG_INTERSECT_NONE;
+ }
+
+ *ipoint = zeroP ? *P1 : *Q1;
+ return LWSEG_INTERSECT_POINT;
+ }
+
+ /* Are both endpoints of the second segment on the same side of the first? */
+ s11 = lw_segment_side(P1, P2, Q1);
+ s12 = lw_segment_side(P1, P2, Q2);
+ if ( (s11 > 0 && s12 > 0) || (s11 < 0 && s12 < 0) )
+ return LWSEG_INTERSECT_NONE;
+
+ /* Are both endpoints of the first segment on the same side of the second? */
+ s21 = lw_segment_side(Q1, Q2, P1);
+ s22 = lw_segment_side(Q1, Q2, P2);
+ if ( (s21 > 0 && s22 > 0) || (s21 < 0 && s22 < 0) )
+ return LWSEG_INTERSECT_NONE;
+
+ /*
+ * Both endpoints of the second segment are on the line of the first,
+ * the segments are collinear and the intersection is their overlap.
+ *
+ * Use the projection on the (P1,P2) vector as the segment parameter,
+ * the overlap is the [0,len2] interval trimmed by the [tQ1,tQ2] one.
+ */
+ if ( s11 == 0 && s12 == 0 )
+ {
+ len2 = (P2->x - P1->x) * (P2->x - P1->x) +
+ (P2->y - P1->y) * (P2->y - P1->y);
+ tQ1 = (Q1->x - P1->x) * (P2->x - P1->x) +
+ (Q1->y - P1->y) * (P2->y - P1->y);
+ tQ2 = (Q2->x - P1->x) * (P2->x - P1->x) +
+ (Q2->y - P1->y) * (P2->y - P1->y);
+
+ tmin = FP_MAX(0.0, FP_MIN(tQ1, tQ2));
+ tmax = FP_MIN(len2, FP_MAX(tQ1, tQ2));
+
+ /* No overlap at all */
+ if ( tmin > tmax )
+ return LWSEG_INTERSECT_NONE;
+
+ /* The segments are collinear and share a positive length */
+ if ( tmin < tmax )
+ return LWSEG_INTERSECT_OVERLAP;
+
+ /* The segments touch on a single point, always a segment endpoint */
+ if ( tmin == 0.0 )
+ *ipoint = *P1;
+ else if ( tmin == len2 )
+ *ipoint = *P2;
+ else if ( tmin == tQ1 )
+ *ipoint = *Q1;
+ else if ( tmin == tQ2 )
+ *ipoint = *Q2;
+ else
+ {
+ /* Rounding fallback, the point is one of the four endpoints */
+ double t = tmin / len2;
+ ipoint->x = P1->x + t * (P2->x - P1->x);
+ ipoint->y = P1->y + t * (P2->y - P1->y);
+ }
+ return LWSEG_INTERSECT_POINT;
+ }
+
+ /*
+ * The segments are not collinear, they intersect on a single point.
+ * When that point is not a vertex of either segment it is interior
+ * to both of them.
+ */
+ if ( lw_pt_on_segment_inclusive(P1, P2, Q1) )
+ {
+ *ipoint = *Q1;
+ return LWSEG_INTERSECT_POINT;
+ }
+
+ if ( lw_pt_on_segment_inclusive(P1, P2, Q2) )
+ {
+ *ipoint = *Q2;
+ return LWSEG_INTERSECT_POINT;
+ }
+
+ if ( lw_pt_on_segment_inclusive(Q1, Q2, P1) )
+ {
+ *ipoint = *P1;
+ return LWSEG_INTERSECT_POINT;
+ }
+
+ if ( lw_pt_on_segment_inclusive(Q1, Q2, P2) )
+ {
+ *ipoint = *P2;
+ return LWSEG_INTERSECT_POINT;
+ }
+
+ return LWSEG_INTERSECT_CROSS;
+}
+
+/**
+* Is the point a boundary point of the line, that is one of its endpoints?
+*
+* Those are the only points of a line that may be shared with another line
+* without requiring any of them to be split.
+*
+* @param line the line to test the point against
+* @param P the point to test
+*
+* @return LW_TRUE if P is the first or the last point of the line
+*/
+static int
+lwline_point_is_boundary(const LWLINE *line, const POINT2D *P)
+{
+ const POINT2D *start = getPoint2d_cp(line->points, 0);
+ const POINT2D *end = getPoint2d_cp(line->points, line->points->npoints - 1);
+
+ return P2D_SAME_STRICT(P, start) || P2D_SAME_STRICT(P, end);
+}
+
+/**
+* Do two lines intersect anywhere but on a point shared by both of their
+* boundaries?
+*
+* Only 2D coordinates are considered.
+*
+* @param line1 first line
+* @param line2 second line
+*
+* @return 1 if there's a non-boundary 2d intersection, 0 if there's NO
+* non-boundary 2d intersection
+*/
+int
+lwline_have_nonboundary_2d_intersection(const LWLINE *line1, const LWLINE *line2)
+{
+ const POINTARRAY *pa1, *pa2;
+ uint32_t i1, i2;
+ POINT2D ipoint;
+
+ if ( !line1 || !line2 )
+ return LW_FALSE;
+
+ pa1 = line1->points;
+ pa2 = line2->points;
+
+ /* Lines without segments can't intersect anything */
+ if ( !pa1 || !pa2 || pa1->npoints < 2 || pa2->npoints < 2 )
+ return LW_FALSE;
+
+ for (i1 = 0; i1 < pa1->npoints - 1; ++i1)
+ {
+ const POINT2D *p1 = getPoint2d_cp(pa1, i1);
+ const POINT2D *p2 = getPoint2d_cp(pa1, i1 + 1);
+
+ for (i2 = 0; i2 < pa2->npoints - 1; ++i2)
+ {
+ const POINT2D *q1 = getPoint2d_cp(pa2, i2);
+ const POINT2D *q2 = getPoint2d_cp(pa2, i2 + 1);
+ int inter;
+
+ inter = lwsegment_intersection(p1, p2, q1, q2, &ipoint);
+
+ /* Crossings and overlaps are never boundary only */
+ if ( inter == LWSEG_INTERSECT_CROSS ||
+ inter == LWSEG_INTERSECT_OVERLAP )
+ return LW_TRUE;
+
+ /*
+ ** A touch on a point which is a boundary point of both lines
+ ** is not a non-boundary intersection
+ */
+ if ( inter == LWSEG_INTERSECT_POINT &&
+ ( ! lwline_point_is_boundary(line1, &ipoint) ||
+ ! lwline_point_is_boundary(line2, &ipoint) ) )
+ return LW_TRUE;
+ }
+ }
+
+ return LW_FALSE;
+}
diff --git a/liblwgeom/topo/lwgeom_topo.c b/liblwgeom/topo/lwgeom_topo.c
index 04e32d140..d683673a7 100644
--- a/liblwgeom/topo/lwgeom_topo.c
+++ b/liblwgeom/topo/lwgeom_topo.c
@@ -54,56 +54,33 @@ _lwt_describe_point(const LWPOINT *pt, char *buf, size_t bufsize)
}
/*
- * Report a deterministic GEOS-derived location for topology errors.
+ * Report a deterministic location for topology errors.
* Normalize the intersection geometry so PointOnSurface is stable
* across operand-order and platform differences.
*/
-static bool
-_lwt_describe_intersection_point(const GEOSGeometry *g1, const GEOSGeometry *g2, char *buf, size_t bufsize)
+static int
+_lwt_describe_intersection_point(const LWGEOM *g1, const LWGEOM *g2, char *buf, size_t bufsize)
{
- unsigned int n = 0;
- POINT2D p;
- GEOSGeometry *isect;
- GEOSGeometry *surface_pt;
- const GEOSCoordSequence *seq;
+ const POINT2D *p;
+ LWGEOM *isect;
+ LWGEOM *normalized;
+ LWGEOM *surface_pt;
- isect = GEOSIntersection(g1, g2);
- if (!isect)
- return false;
+ isect = lwgeom_intersection(g1, g2);
+ if (!isect) return 0;
- if (GEOSNormalize(isect) != 0)
- {
- GEOSGeom_destroy(isect);
- return false;
- }
+ normalized = lwgeom_clone_deep(isect);
+ lwgeom_free(isect);
+ lwgeom_normalize(normalized);
- surface_pt = GEOSPointOnSurface(isect);
- GEOSGeom_destroy(isect);
- if (!surface_pt)
- return false;
+ surface_pt = lwgeom_pointonsurface(normalized);
+ lwgeom_free(normalized);
+ p = getPoint2d_cp(lwgeom_as_lwpoint(surface_pt)->point, 0);
- seq = GEOSGeom_getCoordSeq(surface_pt);
- if (!seq)
- {
- GEOSGeom_destroy(surface_pt);
- return false;
- }
+ snprintf(buf, bufsize, "POINT(%.15g %.15g)", p->x, p->y);
+ lwgeom_free(surface_pt);
- if (!GEOSCoordSeq_getSize(seq, &n) || !n)
- {
- GEOSGeom_destroy(surface_pt);
- return false;
- }
-
- if (!GEOSCoordSeq_getX(seq, 0, &p.x) || !GEOSCoordSeq_getY(seq, 0, &p.y))
- {
- GEOSGeom_destroy(surface_pt);
- return false;
- }
-
- GEOSGeom_destroy(surface_pt);
- snprintf(buf, bufsize, " at POINT(%.15g %.15g)", p.x, p.y);
- return true;
+ return 1;
}
/*********************************************************************
@@ -653,16 +630,7 @@ _lwt_CheckEdgeCrossing( LWT_TOPOLOGY* topo,
LWT_ISO_EDGE *edges;
LWT_ISO_NODE *nodes;
const GBOX *edgebox;
- GEOSGeometry *edgegg;
- initGEOS(lwnotice, lwgeom_geos_error);
-
- edgegg = LWGEOM2GEOS(lwline_as_lwgeom(geom), 0);
- if (!edgegg)
- {
- lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
- return -1;
- }
edgebox = lwgeom_get_bbox( lwline_as_lwgeom(geom) );
/* loop over each node within the edge's gbox */
@@ -680,16 +648,12 @@ _lwt_CheckEdgeCrossing( LWT_TOPOLOGY* topo,
LWT_ISO_NODE *node = &(nodes[i]);
LWT_ELEMID node_id = node->node_id;
- /* check if the edge contains this node (not on boundary) */
- /* ST_RelateMatch(rec.relate, 'T********') */
-
/* skip boundary nodes */
if (node_id == start_node) continue;
if (node_id == end_node) continue;
if (!node->geom || !node->geom->point || !node->geom->point->npoints)
{
- GEOSGeom_destroy(edgegg);
_lwt_release_nodes(nodes, num_nodes);
lwerror("Internal error: lwt_be_getNodeWithinBox2D returned node %"
LWTFMT_ELEMID " with no vertices", node_id);
@@ -702,7 +666,6 @@ _lwt_CheckEdgeCrossing( LWT_TOPOLOGY* topo,
{
char locinfo[128] = "";
snprintf(locinfo, sizeof(locinfo), " at POINT(%.15g %.15g)", p.x, p.y);
- GEOSGeom_destroy(edgegg);
_lwt_release_nodes(nodes, num_nodes);
lwerror("SQL/MM Spatial exception - geometry crosses a node%s", locinfo);
return -1;
@@ -716,7 +679,6 @@ _lwt_CheckEdgeCrossing( LWT_TOPOLOGY* topo,
LWDEBUGF(1, "lwt_be_getEdgeWithinBox2D returned %" PRIu64 " edges", num_edges);
if (num_edges == UINT64_MAX)
{
- GEOSGeom_destroy(edgegg);
PGTOPO_BE_ERROR();
return -1;
}
@@ -724,9 +686,6 @@ _lwt_CheckEdgeCrossing( LWT_TOPOLOGY* topo,
{
LWT_ISO_EDGE* edge = &(edges[i]);
LWT_ELEMID edge_id = edge->edge_id;
- GEOSGeometry *eegg;
- char *relate;
- int match;
if ( edge_id == myself ) continue;
@@ -736,169 +695,22 @@ _lwt_CheckEdgeCrossing( LWT_TOPOLOGY* topo,
return -1;
}
- eegg = LWGEOM2GEOS( lwline_as_lwgeom(edge->geom), 0 );
- if ( ! eegg ) {
- GEOSGeom_destroy(edgegg);
- _lwt_release_edges(edges, num_edges);
- lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
- return -1;
- }
-
- LWDEBUGF(2, "Edge %" LWTFMT_ELEMID " converted to GEOS", edge_id);
-
/* check if the edge has a non-boundary-boundary intersection with our edge */
-
- relate = GEOSRelateBoundaryNodeRule(eegg, edgegg, 2);
- if ( ! relate ) {
- GEOSGeom_destroy(eegg);
- GEOSGeom_destroy(edgegg);
- _lwt_release_edges(edges, num_edges);
- lwerror("GEOSRelateBoundaryNodeRule error: %s", lwgeom_geos_errmsg);
- return -1;
- }
-
- LWDEBUGF(2, "Edge %" LWTFMT_ELEMID " relate pattern is %s", edge_id, relate);
-
- match = GEOSRelatePatternMatch(relate, "FF*F*****");
- if ( match ) {
- /* error or no interior intersection */
- GEOSFree(relate);
- GEOSGeom_destroy(eegg);
- if ( match == 2 ) {
- _lwt_release_edges(edges, num_edges);
- GEOSGeom_destroy(edgegg);
- lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
- return -1;
- }
- continue; /* no interior intersection */
- }
-
- match = GEOSRelatePatternMatch(relate, "1FFF*FFF2");
- if ( match ) {
- char locinfo[128] = "";
- const char *locsuffix = match == 2 ? ""
- : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo))
- ? locinfo
- : "";
- GEOSGeom_destroy(eegg);
- GEOSGeom_destroy(edgegg);
- _lwt_release_edges(edges, num_edges);
- GEOSFree(relate);
- if (match == 2)
- {
- lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
- }
- else
- {
- lwerror("SQL/MM Spatial exception - coincident edge %" LWTFMT_ELEMID "%s", edge_id, locsuffix);
- }
- return -1;
- }
-
- match = GEOSRelatePatternMatch(relate, "1********");
- if ( match ) {
- char locinfo[128] = "";
- const char *locsuffix = match == 2 ? ""
- : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo))
- ? locinfo
- : "";
- GEOSGeom_destroy(eegg);
- GEOSGeom_destroy(edgegg);
- _lwt_release_edges(edges, num_edges);
- GEOSFree(relate);
- if (match == 2)
- {
- lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
- }
- else
- {
- lwerror("Spatial exception - geometry intersects edge %" LWTFMT_ELEMID "%s", edge_id, locsuffix);
- }
- return -1;
- }
-
- match = GEOSRelatePatternMatch(relate, "T********");
- if ( match ) {
- char locinfo[128] = "";
- const char *locsuffix = match == 2 ? ""
- : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo))
- ? locinfo
- : "";
- GEOSGeom_destroy(eegg);
- GEOSGeom_destroy(edgegg);
- _lwt_release_edges(edges, num_edges);
- GEOSFree(relate);
- if (match == 2)
- {
- lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
- }
- else
- {
- lwerror(
- "SQL/MM Spatial exception - geometry crosses edge %" LWTFMT_ELEMID "%s", edge_id, locsuffix);
- }
- return -1;
- }
-
- match = GEOSRelatePatternMatch(relate, "*T*******");
- if ( match ) {
- char locinfo[128] = "";
- const char *locsuffix = match == 2 ? ""
- : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo))
- ? locinfo
- : "";
- GEOSGeom_destroy(eegg);
- GEOSGeom_destroy(edgegg);
- _lwt_release_edges(edges, num_edges);
- GEOSFree(relate);
- if (match == 2)
- {
- lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
- }
- else
- {
- lwerror("Spatial exception - geometry boundary touches interior of edge %" LWTFMT_ELEMID "%s",
- edge_id,
- locsuffix);
- }
- return -1;
- }
-
- match = GEOSRelatePatternMatch(relate, "***T*****");
- if ( match ) {
- char locinfo[128] = "";
- const char *locsuffix = match == 2 ? ""
- : _lwt_describe_intersection_point(edgegg, eegg, locinfo, sizeof(locinfo))
- ? locinfo
- : "";
- GEOSGeom_destroy(eegg);
- GEOSGeom_destroy(edgegg);
- _lwt_release_edges(edges, num_edges);
- GEOSFree(relate);
- if (match == 2)
- {
- lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg);
- }
- else
- {
- lwerror("Spatial exception - boundary of edge %" LWTFMT_ELEMID " touches interior of geometry%s",
- edge_id,
- locsuffix);
- }
+ if ( lwline_have_nonboundary_2d_intersection(edge->geom, geom) )
+ {
+ char locinfo[128] = "";
+ _lwt_describe_intersection_point(lwline_as_lwgeom(edge->geom), lwline_as_lwgeom(geom), locinfo, sizeof(locinfo));
+ _lwt_release_edges(edges, num_edges);
+ lwerror("SQL/MM Spatial exception - non-boundary intersection with edge %" LWTFMT_ELEMID " at or near %s", edge_id, locinfo);
return -1;
}
LWDEBUGF(2, "Edge %" LWTFMT_ELEMID " analysis completed, it does no harm", edge_id);
-
- GEOSFree(relate);
- GEOSGeom_destroy(eegg);
}
LWDEBUGF(1, "No edge crossing detected among the %" PRIu64 " candidate edges", num_edges);
if ( edges ) _lwt_release_edges(edges, num_edges);
/* would be NULL if num_edges was 0 */
- GEOSGeom_destroy(edgegg);
-
return 0;
}
diff --git a/topology/postgis_topology.c b/topology/postgis_topology.c
index ab8232230..c96637b6e 100644
--- a/topology/postgis_topology.c
+++ b/topology/postgis_topology.c
@@ -5952,3 +5952,48 @@ Datum TopoRingIsCCW(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isCCW);
}
+
+/* TopoEdgesHaveInteriorIntersection(line1 geometry, line2 geometry) */
+Datum TopoEdgesHaveInteriorIntersection(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(TopoEdgesHaveInteriorIntersection);
+Datum TopoEdgesHaveInteriorIntersection(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED *geom1, *geom2;
+ LWGEOM *lwgeom1, *lwgeom2;
+ int ret;
+
+ geom1 = PG_GETARG_GSERIALIZED_P(0);
+ lwgeom1 = lwgeom_from_gserialized(geom1);
+
+ geom2 = PG_GETARG_GSERIALIZED_P(1);
+ lwgeom2 = lwgeom_from_gserialized(geom2);
+
+ if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
+ {
+ lwgeom_free(lwgeom1);
+ lwgeom_free(lwgeom2);
+ PG_FREE_IF_COPY(geom1, 0);
+ PG_FREE_IF_COPY(geom2, 1);
+ PG_RETURN_BOOL(false);
+ }
+
+
+ if (lwgeom1->type != LINETYPE || lwgeom2->type != LINETYPE)
+ {
+ lwgeom_free(lwgeom1);
+ lwgeom_free(lwgeom2);
+ PG_FREE_IF_COPY(geom1, 0);
+ PG_FREE_IF_COPY(geom2, 1);
+ lwpgerror("Unsupported geometry type passed to TopoEdgesHaveInteriorIntersection");
+ PG_RETURN_NULL();
+ }
+
+ ret = lwline_have_nonboundary_2d_intersection(lwgeom_as_lwline(lwgeom1), lwgeom_as_lwline(lwgeom2));
+ lwgeom_free(lwgeom1);
+ lwgeom_free(lwgeom2);
+
+ PG_FREE_IF_COPY(geom1, 0);
+ PG_FREE_IF_COPY(geom2, 1);
+
+ PG_RETURN_BOOL(ret);
+}
diff --git a/topology/sql/manage/ValidateTopology.sql.in b/topology/sql/manage/ValidateTopology.sql.in
index fb6f23c02..042e00e15 100644
--- a/topology/sql/manage/ValidateTopology.sql.in
+++ b/topology/sql/manage/ValidateTopology.sql.in
@@ -371,6 +371,11 @@ RETURNS BOOLEAN AS
'MODULE_PATHNAME', 'TopoRingIsCCW'
LANGUAGE 'c' IMMUTABLE STRICT;
+CREATE OR REPLACE FUNCTION topology.TopoEdgesHaveInteriorIntersection(line1 geometry, line2 geometry)
+RETURNS BOOLEAN AS
+ 'MODULE_PATHNAME', 'TopoEdgesHaveInteriorIntersection'
+LANGUAGE 'c' IMMUTABLE STRICT;
+
--
-- Check that the edges forming all rings
-- (as advertised by their next_right_edge/next_left_edge)
@@ -770,7 +775,7 @@ BEGIN
e2.edge_id as id2,
e1.geom as g1,
e2.geom as g2,
- ST_Relate(e1.geom, e2.geom, 2) as im
+ topology.TopoEdgesHaveInteriorIntersection(e1.geom, e2.geom) as cross
FROM
edge e1,
edge e2
@@ -794,15 +799,14 @@ BEGIN
)
LOOP --{
- IF ST_RelateMatch(rec.im, 'FF*F*****') THEN
- -- no interior-interior or interior-boundary intersections
- CONTINUE;
+ IF rec.cross THEN
+ -- interior-interior or interior-boundary intersections found
+ retrec.error = 'edge crosses edge';
+ retrec.id1 = rec.id1;
+ retrec.id2 = rec.id2;
+ RETURN NEXT retrec;
END IF;
- retrec.error = 'edge crosses edge';
- retrec.id1 = rec.id1;
- retrec.id2 = rec.id2;
- RETURN NEXT retrec;
END LOOP; --}
-- Check for edge start_node geometry mismatch
diff --git a/topology/test/regress/st_addedgemodface_expected b/topology/test/regress/st_addedgemodface_expected
index 29aef17ce..7a4be20d8 100644
--- a/topology/test/regress/st_addedgemodface_expected
+++ b/topology/test/regress/st_addedgemodface_expected
@@ -7,10 +7,10 @@ ERROR: SQL/MM Spatial exception - non-existent node
ERROR: SQL/MM Spatial exception - curve not simple
ERROR: Invalid edge (no two distinct vertices exist)
ERROR: Invalid edge (no two distinct vertices exist)
-ERROR: SQL/MM Spatial exception - coincident edge 8 at POINT(35 22)
-ERROR: SQL/MM Spatial exception - geometry crosses edge 5 at POINT(57 33)
-ERROR: SQL/MM Spatial exception - geometry crosses edge 4 at POINT(45 32)
-ERROR: Spatial exception - geometry intersects edge 4 at POINT(36 38)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 8 at or near POINT(35 22)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 5 at or near POINT(57 33)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 4 at or near POINT(45 32)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 4 at or near POINT(36 38)
L1
L2
T1|E7|8|-19|0|10
diff --git a/topology/test/regress/st_addedgenewfaces_expected b/topology/test/regress/st_addedgenewfaces_expected
index df23f1044..8a05ace86 100644
--- a/topology/test/regress/st_addedgenewfaces_expected
+++ b/topology/test/regress/st_addedgenewfaces_expected
@@ -7,10 +7,10 @@ ERROR: SQL/MM Spatial exception - non-existent node
ERROR: SQL/MM Spatial exception - curve not simple
ERROR: Invalid edge (no two distinct vertices exist)
ERROR: Invalid edge (no two distinct vertices exist)
-ERROR: SQL/MM Spatial exception - coincident edge 8 at POINT(35 22)
-ERROR: SQL/MM Spatial exception - geometry crosses edge 5 at POINT(57 33)
-ERROR: SQL/MM Spatial exception - geometry crosses edge 4 at POINT(45 32)
-ERROR: Spatial exception - geometry intersects edge 4 at POINT(36 38)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 8 at or near POINT(35 22)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 5 at or near POINT(57 33)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 4 at or near POINT(45 32)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 4 at or near POINT(36 38)
L1
L2
T1|E7|8|-19|0|11
diff --git a/topology/test/regress/st_addisoedge_expected b/topology/test/regress/st_addisoedge_expected
index 46d9f9916..05602c571 100644
--- a/topology/test/regress/st_addisoedge_expected
+++ b/topology/test/regress/st_addisoedge_expected
@@ -29,5 +29,5 @@ ERROR: SQL/MM Spatial exception - not isolated node
ERROR: SQL/MM Spatial exception - not isolated node
ERROR: Closed edges would not be isolated, try ST_AddEdgeNewFaces
ERROR: SQL/MM Spatial exception - not isolated node
-ERROR: SQL/MM Spatial exception - geometry crosses edge 2 at POINT(10 3.33333333333333)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 2 at or near POINT(10 3.33333333333333)
Topology 'tt' dropped
diff --git a/topology/test/regress/st_changeedgegeom_expected b/topology/test/regress/st_changeedgegeom_expected
index c4dd61a06..f1bc3c380 100644
--- a/topology/test/regress/st_changeedgegeom_expected
+++ b/topology/test/regress/st_changeedgegeom_expected
@@ -5,7 +5,7 @@ ERROR: SQL/MM Spatial exception - geometry crosses a node at POINT(20 37)
ERROR: SQL/MM Spatial exception - curve not simple
ERROR: Invalid edge (no two distinct vertices exist)
ERROR: SQL/MM Spatial exception - non-existent edge 666
-ERROR: SQL/MM Spatial exception - geometry crosses edge 1 at POINT(11.8 38)
+ERROR: SQL/MM Spatial exception - non-boundary intersection with edge 1 at or near POINT(10.2 38)
T2|Edge 5 changed
T3|Edge 5 changed
T4|Edge 26 changed
diff --git a/topology/test/regress/topology_error_location_expected b/topology/test/regress/topology_error_location_expected
index 85bd4abe3..c90c9aff9 100644
--- a/topology/test/regress/topology_error_location_expected
+++ b/topology/test/regress/topology_error_location_expected
@@ -5,5 +5,5 @@ n3|3
n4|4
NOTICE: duplicate node: SQL/MM Spatial exception - coincident node at POINT(0 0)
base_edge|1
-NOTICE: crossing edge: SQL/MM Spatial exception - geometry crosses edge 1 at POINT(5 0)
+NOTICE: crossing edge: SQL/MM Spatial exception - non-boundary intersection with edge 1 at or near POINT(5 0)
dropped|t
-----------------------------------------------------------------------
Summary of changes:
NEWS | 2 +
liblwgeom/cunit/cu_algorithm.c | 152 ++++++++++++
liblwgeom/liblwgeom_internal.h | 1 +
liblwgeom/lwline.c | 261 +++++++++++++++++++++
liblwgeom/topo/lwgeom_topo.c | 236 ++-----------------
topology/postgis_topology.c | 45 ++++
topology/sql/manage/ValidateTopology.sql.in | 20 +-
topology/test/regress/st_addedgemodface.sql | 14 ++
topology/test/regress/st_addedgemodface_expected | 10 +-
topology/test/regress/st_addedgenewfaces_expected | 8 +-
topology/test/regress/st_addisoedge_expected | 2 +-
topology/test/regress/st_changeedgegeom_expected | 2 +-
.../test/regress/topology_error_location_expected | 2 +-
topology/test/regress/validatetopology.sql | 34 +++
topology/test/regress/validatetopology_expected | 2 +
15 files changed, 560 insertions(+), 231 deletions(-)
hooks/post-receive
--
PostGIS
More information about the postgis-tickets
mailing list