]> granicus.if.org Git - postgis/commitdiff
Make ST_ChangeEdgeGeom motion collision detection code more robust
authorSandro Santilli <strk@keybit.net>
Wed, 16 Jan 2013 15:14:06 +0000 (15:14 +0000)
committerSandro Santilli <strk@keybit.net>
Wed, 16 Jan 2013 15:14:06 +0000 (15:14 +0000)
The new model avoids a call to GEOSSymDifference but rather checks
each candidate node against both "motion ranges" containment.
It still constructs something, but only MULTIPOINT, which should
be safe. Haven't profiled but the new code should also be faster
than the previous. Fixes ticket #2176, includes testcase for it.

git-svn-id: http://svn.osgeo.org/postgis/trunk@10984 b70326c6-7e19-0410-871a-916f4a2858ee

topology/sql/sqlmm.sql.in.c
topology/test/regress/st_changeedgegeom.sql
topology/test/regress/st_changeedgegeom_expected

index 5abb4e20c6b3e771e6cca5f84dc3901f3478391d..cdc35ca9d7d2a2e864b46ede63ee183d6f443d6b 100644 (file)
@@ -13,7 +13,7 @@
 --  
 -- 
 
-/*#define POSTGIS_TOPOLOGY_DEBUG 1*/
+/* #define POSTGIS_TOPOLOGY_DEBUG 1 */
 
 --={ ----------------------------------------------------------------
 --  SQL/MM block
@@ -2545,10 +2545,10 @@ CREATE OR REPLACE FUNCTION topology.ST_ChangeEdgeGeom(atopology varchar, anedge
 $$
 DECLARE
   rec RECORD;
+  rng_info RECORD; -- movement range info
   oldedge RECORD;
   range GEOMETRY; -- movement range
   tmp1 GEOMETRY;
-  tmp2 GEOMETRY;
   snode_info RECORD;
   enode_info RECORD;
   sql TEXT;
@@ -2645,6 +2645,7 @@ BEGIN
     || '.node WHERE geom && '
     || quote_literal(acurve::text)
     || '::geometry'
+    -- TODO: skip start_node and end_node !
   LOOP
     IF ST_RelateMatch(rec.relate, 'T********') THEN
       RAISE EXCEPTION 'SQL/MM Spatial exception - geometry crosses a node';
@@ -2694,51 +2695,71 @@ BEGIN
   -- Check that the "motion range" doesn't include any node 
   --{
 
-  tmp1 := ST_MakeLine(ST_EndPoint(oldedge.geom), ST_StartPoint(oldedge.geom));
+  sql := 'SELECT ST_Collect(geom) as nodes, '
+    || 'null::geometry as r1, null::geometry as r2 FROM '
+    || quote_ident(atopology)
+    || '.node WHERE geom && '
+    || quote_literal(ST_Collect(ST_Envelope(oldedge.geom),
+                                ST_Envelope(acurve))::text)
+    || '::geometry AND node_id NOT IN ( '
+    || oldedge.start_node || ',' || oldedge.end_node || ')';
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-  RAISE DEBUG 'end-to-start: %', ST_AsText(tmp1);
+  RAISE DEBUG '%', sql;
 #endif
+  EXECUTE sql INTO rng_info;
+
+  -- There's no collision if there's no nodes in the combined
+  -- bbox of old and new edges.
+  --
+  IF NOT ST_IsEmpty(rng_info.nodes) THEN -- {
 
-  tmp2 := ST_MakeLine(oldedge.geom, tmp1);
-  IF ST_NumPoints(tmp2) < 4 THEN
-    tmp2 := ST_AddPoint(tmp2, ST_StartPoint(oldedge.geom));
-  END IF;
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-  RAISE DEBUG 'Old-ring: %', ST_AsText(tmp2);
+    RAISE DEBUG '% nodes in the edge movement range bbox: %',
+                              ST_NumGeometries(rng_info.nodes),
+                              ST_AsText(rng_info.nodes)
+                              ;
 #endif
-  tmp2 := ST_CollectionExtract(ST_MakeValid(ST_MakePolygon(tmp2)), 3);
+
+    tmp1 := ST_MakeLine(ST_EndPoint(oldedge.geom), ST_StartPoint(oldedge.geom));
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-  RAISE DEBUG 'Old-ring (poly): %', ST_AsText(tmp2);
+    RAISE DEBUG 'end-to-start: %', ST_AsText(tmp1);
 #endif
 
-  range := ST_MakeLine(acurve, tmp1);
-  IF ST_NumPoints(range) < 4 THEN
-    range := ST_AddPoint(range, ST_StartPoint(oldedge.geom));
-  END IF;
+    rng_info.r1 := ST_MakeLine(oldedge.geom, tmp1);
+    IF ST_NumPoints(rng_info.r1) < 4 THEN
+      rng_info.r1 := ST_AddPoint(rng_info.r1, ST_StartPoint(oldedge.geom));
+    END IF;
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-  RAISE DEBUG 'New-ring: %', ST_AsText(range);
+    RAISE DEBUG 'Old-ring: %', ST_AsText(rng_info.r1);
 #endif
-  range := ST_CollectionExtract(ST_MakeValid(ST_MakePolygon(range)), 3);
+    rng_info.r1 := ST_CollectionExtract(
+                       ST_MakeValid(ST_MakePolygon(rng_info.r1)), 3);
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-  RAISE DEBUG 'New-ring (poly): %', ST_AsText(range);
+    RAISE DEBUG 'Old-ring (poly): %', ST_AsText(rng_info.r1);
 #endif
 
-  range := ST_SymDifference(range, tmp2);
+    rng_info.r2 := ST_MakeLine(acurve, tmp1);
+    IF ST_NumPoints(rng_info.r2) < 4 THEN
+      rng_info.r2 := ST_AddPoint(rng_info.r2, ST_StartPoint(oldedge.geom));
+    END IF;
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-  RAISE DEBUG 'Range motion: %', ST_AsText(range);
+    RAISE DEBUG 'New-ring: %', ST_AsText(rng_info.r2);
 #endif
-
-  sql := 'SELECT node_id, geom FROM '
-    || quote_ident(atopology)
-    || '.node WHERE ST_Contains('
-    || quote_literal(range::text)
-    || '::geometry, geom) LIMIT 1';
+    rng_info.r2 := ST_CollectionExtract(
+                       ST_MakeValid(ST_MakePolygon(rng_info.r2)), 3);
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-  RAISE DEBUG '%', sql;
+    RAISE DEBUG 'New-ring (poly): %', ST_AsText(rng_info.r2);
 #endif
-  FOR rec IN EXECUTE sql LOOP -- {
-    RAISE EXCEPTION 'Edge motion collision at %', ST_AsText(rec.geom);
-  END LOOP; -- }
+
+     tmp1 := ST_CollectionExtract(ST_Intersection(rng_info.nodes, rng_info.r1), 1);
+    range := ST_CollectionExtract(ST_Intersection(rng_info.nodes, rng_info.r2), 1);
+    IF ST_IsEmpty(tmp1) != ST_IsEmpty(range) OR NOT ST_Equals(tmp1, range)
+    THEN
+      RAISE EXCEPTION 'Edge motion collision at %',
+                        ST_AsText(ST_GeometryN(ST_Union(tmp1, range), 1));
+    END IF;
+
+  END IF; -- }
 
   --} motion range checking end
 
index f995e47c4907e197373451b406d2128e5bf832ab..3fa533d27cd1fb28634671f8c434bb31fa174faa 100644 (file)
@@ -129,6 +129,14 @@ SELECT 'T11F',
 SELECT 'T12.1', ST_AddIsoNode('city_data', 8, 'POINT(49 10)');
 SELECT 'T12', ST_ChangeEdgeGeom('city_data', 16, 'LINESTRING(47 6, 47 14)');
 
+-- See http://trac.osgeo.org/postgis/ticket/2176
+SELECT 'T13.1', TopoGeo_AddLineString('city_data', '01020000001D000000E42CEC69873FF2BF9E98F56228E347400EDB16653648F2BF4985B18520E34740E92B4833164DF2BF3A1E335019E34740A94D9CDCEF50F2BF33F9669B1BE347407DAEB6627F59F2BF2CF180B229E34740758E01D9EB5DF2BFD0D556EC2FE34740533F6F2A5261F2BFD717096D39E34740F4893C49BA66F2BFC8073D9B55E34740B8239C16BC68F2BF33A7CB6262E34740AA2B9FE57970F2BF4165FCFB8CE347406DC5FEB27B72F2BFBA4E232D95E34740978BF84ECC7AF2BF24EEB1F4A1E34740E527D53E1D8FF2BF8F8D40BCAEE3474036CD3B4ED191F2BF649291B3B0E34740841266DAFE95F2BF1DE6CB0BB0E34740E3361AC05BA0F2BFB2632310AFE347405C5A0D897BACF2BF72F90FE9B7E3474031D3F6AFACB4F2BF4F232D95B7E347402B137EA99FB7F2BFD656EC2FBBE347402D431CEBE2B6F2BF551344DD07E4474011E4A08499B6F2BF15E3FC4D28E447406519E25817B7F2BF63EE5A423EE447409DD7D825AAB7F2BFE3FC4D2844E447405969520ABABDF2BF2384471B47E44740A31EA2D11DC4F2BFB1F9B83654E447400473F4F8BDCDF2BFEA5BE67459E447405070B1A206D3F2BFF19D98F562E4474062670A9DD7D8F2BF0E4FAF9465E447407FF6234564D8F2BFF1BA7EC16EE44740' );
+SELECT 'T13.2', ST_ChangeEdgeGeom('city_data', 29, '010200000008000000E42CEC69873FF2BF9E98F56228E34740E92B4833164DF2BF3B1E335019E34740768E01D9EB5DF2BFD0D556EC2FE347406EC5FEB27B72F2BFBA4E232D95E34740988BF84ECC7AF2BF25EEB1F4A1E347402C137EA99FB7F2BFD656EC2FBBE347409DD7D825AAB7F2BFE4FC4D2844E447407FF6234564D8F2BFF1BA7EC16EE44740' );
+-- Now add an obstacle and try to change back (should fail)
+SELECT 'T13.3', TopoGeo_AddPoint('city_data', 'POINT(-1.1697 47.7825)'::geometry);
+SELECT 'T13.4', ST_ChangeEdgeGeom('city_data', 29, '01020000001D000000E42CEC69873FF2BF9E98F56228E347400EDB16653648F2BF4985B18520E34740E92B4833164DF2BF3A1E335019E34740A94D9CDCEF50F2BF33F9669B1BE347407DAEB6627F59F2BF2CF180B229E34740758E01D9EB5DF2BFD0D556EC2FE34740533F6F2A5261F2BFD717096D39E34740F4893C49BA66F2BFC8073D9B55E34740B8239C16BC68F2BF33A7CB6262E34740AA2B9FE57970F2BF4165FCFB8CE347406DC5FEB27B72F2BFBA4E232D95E34740978BF84ECC7AF2BF24EEB1F4A1E34740E527D53E1D8FF2BF8F8D40BCAEE3474036CD3B4ED191F2BF649291B3B0E34740841266DAFE95F2BF1DE6CB0BB0E34740E3361AC05BA0F2BFB2632310AFE347405C5A0D897BACF2BF72F90FE9B7E3474031D3F6AFACB4F2BF4F232D95B7E347402B137EA99FB7F2BFD656EC2FBBE347402D431CEBE2B6F2BF551344DD07E4474011E4A08499B6F2BF15E3FC4D28E447406519E25817B7F2BF63EE5A423EE447409DD7D825AAB7F2BFE3FC4D2844E447405969520ABABDF2BF2384471B47E44740A31EA2D11DC4F2BFB1F9B83654E447400473F4F8BDCDF2BFEA5BE67459E447405070B1A206D3F2BFF19D98F562E4474062670A9DD7D8F2BF0E4FAF9465E447407FF6234564D8F2BFF1BA7EC16EE44740' );
+
 -- TODO: test changing some clockwise closed edges..
 
 SELECT topology.DropTopology('city_data');
+
index 34525f93fc6b18d566097a7e664504379635a88b..102a3223d5aa094b9807407a4708c6ad14f0bd4c 100644 (file)
@@ -37,4 +37,8 @@ T11|Edge 16 changed
 T11F|t
 T12.1|23
 ERROR:  Edge motion collision at POINT(49 10)
+T13.1|29
+T13.2|Edge 29 changed
+T13.3|26
+ERROR:  Edge motion collision at POINT(-1.1697 47.7825)
 Topology 'city_data' dropped