From: Sandro Santilli Date: Fri, 17 Aug 2012 17:18:41 +0000 (+0000) Subject: Fix ST_ModEdgeHeal and ST_NewEdgeHeal for doubly connected edges X-Git-Tag: 2.1.0beta2~693 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=76c5fa465c24f8bb8bbe0918f2b9faff77d80da3;p=postgis Fix ST_ModEdgeHeal and ST_NewEdgeHeal for doubly connected edges Includes testcases, closes #1955 git-svn-id: http://svn.osgeo.org/postgis/trunk@10189 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/topology/sql/sqlmm.sql.in.c b/topology/sql/sqlmm.sql.in.c index b6d0b3110..0176de6b5 100644 --- a/topology/sql/sqlmm.sql.in.c +++ b/topology/sql/sqlmm.sql.in.c @@ -166,6 +166,7 @@ DECLARE e2rec RECORD; rec RECORD; newedgeid int; + connectededges int[]; commonnode int; caseno int; topoid int; @@ -228,31 +229,56 @@ BEGIN END IF; -- Find common node + IF e1rec.end_node = e2rec.start_node THEN commonnode = e1rec.end_node; caseno = 1; ELSIF e1rec.end_node = e2rec.end_node THEN commonnode = e1rec.end_node; caseno = 2; - ELSIF e1rec.start_node = e2rec.start_node THEN - commonnode = e1rec.start_node; - caseno = 3; - ELSIF e1rec.start_node = e2rec.end_node THEN - commonnode = e1rec.start_node; - caseno = 4; - ELSE - RAISE EXCEPTION 'SQL/MM Spatial exception - non-connected edges'; END IF; -- Check if any other edge is connected to the common node - FOR rec IN EXECUTE 'SELECT edge_id FROM ' || quote_ident(toponame) - || '.edge_data WHERE ( edge_id != ' || e1id - || ' AND edge_id != ' || e2id || ') AND ( start_node = ' - || commonnode || ' OR end_node = ' || commonnode || ' )' - LOOP - RAISE EXCEPTION - 'SQL/MM Spatial exception - other edges connected (ie: %)', rec.edge_id; - END LOOP; + IF commonnode IS NOT NULL THEN + FOR rec IN EXECUTE 'SELECT edge_id FROM ' || quote_ident(toponame) + || '.edge_data WHERE ( edge_id != ' || e1id + || ' AND edge_id != ' || e2id || ') AND ( start_node = ' + || commonnode || ' OR end_node = ' || commonnode || ' )' + LOOP + commonnode := NULL; + connectededges = connectededges || rec.edge_id; + END LOOP; + END IF; + + IF commonnode IS NULL THEN + IF e1rec.start_node = e2rec.start_node THEN + commonnode = e1rec.start_node; + caseno = 3; + ELSIF e1rec.start_node = e2rec.end_node THEN + commonnode = e1rec.start_node; + caseno = 4; + END IF; + + -- Check if any other edge is connected to the common node + IF commonnode IS NOT NULL THEN + FOR rec IN EXECUTE 'SELECT edge_id FROM ' || quote_ident(toponame) + || '.edge_data WHERE ( edge_id != ' || e1id + || ' AND edge_id != ' || e2id || ') AND ( start_node = ' + || commonnode || ' OR end_node = ' || commonnode || ' )' + LOOP + commonnode := NULL; + connectededges = connectededges || rec.edge_id; + END LOOP; + END IF; + END IF; + + IF commonnode IS NULL THEN + IF connectededges IS NOT NULL THEN + RAISE EXCEPTION 'SQL/MM Spatial exception - other edges connected (%)', array_to_string(connectededges, ','); + ELSE + RAISE EXCEPTION 'SQL/MM Spatial exception - non-connected edges'; + END IF; + END IF; -- NOT IN THE SPECS: -- check if any topo_geom is defined only by one of the @@ -454,6 +480,7 @@ DECLARE e1rec RECORD; e2rec RECORD; rec RECORD; + connectededges int[]; commonnode int; caseno int; topoid int; @@ -516,31 +543,56 @@ BEGIN END IF; -- Find common node + IF e1rec.end_node = e2rec.start_node THEN commonnode = e1rec.end_node; caseno = 1; ELSIF e1rec.end_node = e2rec.end_node THEN commonnode = e1rec.end_node; caseno = 2; - ELSIF e1rec.start_node = e2rec.start_node THEN - commonnode = e1rec.start_node; - caseno = 3; - ELSIF e1rec.start_node = e2rec.end_node THEN - commonnode = e1rec.start_node; - caseno = 4; - ELSE - RAISE EXCEPTION 'SQL/MM Spatial exception - non-connected edges'; END IF; -- Check if any other edge is connected to the common node - FOR rec IN EXECUTE 'SELECT edge_id FROM ' || quote_ident(toponame) - || '.edge_data WHERE ( edge_id != ' || e1id - || ' AND edge_id != ' || e2id || ') AND ( start_node = ' - || commonnode || ' OR end_node = ' || commonnode || ' )' - LOOP - RAISE EXCEPTION - 'SQL/MM Spatial exception - other edges connected (ie: %)', rec.edge_id; - END LOOP; + IF commonnode IS NOT NULL THEN + FOR rec IN EXECUTE 'SELECT edge_id FROM ' || quote_ident(toponame) + || '.edge_data WHERE ( edge_id != ' || e1id + || ' AND edge_id != ' || e2id || ') AND ( start_node = ' + || commonnode || ' OR end_node = ' || commonnode || ' )' + LOOP + commonnode := NULL; + connectededges = connectededges || rec.edge_id; + END LOOP; + END IF; + + IF commonnode IS NULL THEN + IF e1rec.start_node = e2rec.start_node THEN + commonnode = e1rec.start_node; + caseno = 3; + ELSIF e1rec.start_node = e2rec.end_node THEN + commonnode = e1rec.start_node; + caseno = 4; + END IF; + + -- Check if any other edge is connected to the common node + IF commonnode IS NOT NULL THEN + FOR rec IN EXECUTE 'SELECT edge_id FROM ' || quote_ident(toponame) + || '.edge_data WHERE ( edge_id != ' || e1id + || ' AND edge_id != ' || e2id || ') AND ( start_node = ' + || commonnode || ' OR end_node = ' || commonnode || ' )' + LOOP + commonnode := NULL; + connectededges = connectededges || rec.edge_id; + END LOOP; + END IF; + END IF; + + IF commonnode IS NULL THEN + IF connectededges IS NOT NULL THEN + RAISE EXCEPTION 'SQL/MM Spatial exception - other edges connected (%)', array_to_string(connectededges, ','); + ELSE + RAISE EXCEPTION 'SQL/MM Spatial exception - non-connected edges'; + END IF; + END IF; -- NOT IN THE SPECS: -- check if any topo_geom is defined only by one of the diff --git a/topology/test/regress/st_modedgeheal.sql b/topology/test/regress/st_modedgeheal.sql index 2d4038b27..1e39f2351 100644 --- a/topology/test/regress/st_modedgeheal.sql +++ b/topology/test/regress/st_modedgeheal.sql @@ -115,6 +115,51 @@ SELECT topology.DropTopology('t'); ------------------------------------------------------------------------- ------------------------------------------------------------------------- +-- Test edges sharing both endpoints +-- See http://trac.osgeo.org/postgis/ticket/1955 + +SELECT '#1955', topology.CreateTopology('t') > 1; + +SELECT '#1955.1', 'E'||topology.AddEdge('t', 'LINESTRING(0 0, 10 0, 10 10)'); -- 1 +SELECT '#1955.1', 'E'||topology.AddEdge('t', 'LINESTRING(0 0, 0 10, 10 10)'); ; -- 2 + +SELECT '#1955.1', count(node_id), 'start nodes' as label FROM t.node GROUP BY label; + +-- Deletes second node. Not very predictable which one is removed +SELECT '#1955.1', 'H:1,2', 'N' || topology.ST_ModEdgeHeal('t', 1, 2), 'deleted'; + +SELECT '#1955.1', count(node_id), 'nodes left' as label FROM t.node GROUP BY label; + +SELECT '#1955.2', 'E'||topology.AddEdge('t', 'LINESTRING(50 0, 60 0, 60 10)'); -- 3 +SELECT '#1955.2', 'E'||topology.AddEdge('t', 'LINESTRING(50 0, 50 10, 60 10)'); ; -- 4 +SELECT '#1955.2', 'E'||topology.AddEdge('t', 'LINESTRING(60 10, 70 10)'); ; -- 5 + +SELECT '#1955.2', count(node_id), 'start nodes' as label FROM t.node GROUP BY label; + +-- Only the start node can be deleted (50 0) because the other is shared by +-- another edge +SELECT '#1955.2', 'H:3,4', 'N' || topology.ST_ModEdgeHeal('t', 3, 4), 'deleted'; + +SELECT '#1955.2', count(node_id), 'nodes left' as label FROM t.node GROUP BY label; + +SELECT '#1955.3', 'E'||topology.AddEdge('t', 'LINESTRING(80 0, 90 0, 90 10)'); -- 6 +SELECT '#1955.3', 'E'||topology.AddEdge('t', 'LINESTRING(80 0, 80 10, 90 10)'); ; -- 7 +SELECT '#1955.3', 'E'||topology.AddEdge('t', 'LINESTRING(70 10, 80 0)'); ; -- 8 + +SELECT '#1955.3', count(node_id), 'start nodes' as label FROM t.node GROUP BY label; + +-- Only the end node can be deleted (90 10) because the other is shared by +-- another edge +SELECT '#1955.3', 'H:6,7', 'N' || topology.ST_ModEdgeHeal('t', 6, 7), 'deleted'; + +SELECT '#1955.3', count(node_id), 'nodes left' as label FROM t.node GROUP BY label; + +SELECT '#1955', topology.DropTopology('t'); + +------------------------------------------------------------------------- +------------------------------------------------------------------------- +------------------------------------------------------------------------- + -- TODO: test registered but unexistent topology -- TODO: test registered but corrupted topology -- (missing node, edge, relation...) diff --git a/topology/test/regress/st_modedgeheal_expected b/topology/test/regress/st_modedgeheal_expected index c4f195644..94e5294e8 100644 --- a/topology/test/regress/st_modedgeheal_expected +++ b/topology/test/regress/st_modedgeheal_expected @@ -9,7 +9,7 @@ ERROR: SQL/MM Spatial exception - null argument ERROR: SQL/MM Spatial exception - null argument ERROR: SQL/MM Spatial exception - invalid topology name ERROR: SQL/MM Spatial exception - non-connected edges -ERROR: SQL/MM Spatial exception - other edges connected (ie: 19) +ERROR: SQL/MM Spatial exception - other edges connected (19,20) ERROR: Edge 2 is closed, cannot heal to edge 3 ERROR: Edge 2 is closed, cannot heal to edge 3 ERROR: Cannot heal edge 25 with itself, try with another @@ -120,3 +120,22 @@ ERROR: SQL/MM Spatial exception - non-connected edges 2|3 3|-3 Topology 't' dropped +#1955|t +#1955.1|E1 +#1955.1|E2 +#1955.1|2|start nodes +#1955.1|H:1,2|N2|deleted +#1955.1|1|nodes left +#1955.2|E3 +#1955.2|E4 +#1955.2|E5 +#1955.2|4|start nodes +#1955.2|H:3,4|N3|deleted +#1955.2|3|nodes left +#1955.3|E6 +#1955.3|E7 +#1955.3|E8 +#1955.3|5|start nodes +#1955.3|H:6,7|N7|deleted +#1955.3|4|nodes left +#1955|Topology 't' dropped diff --git a/topology/test/regress/st_newedgeheal.sql b/topology/test/regress/st_newedgeheal.sql index 641a347cc..e3192205d 100644 --- a/topology/test/regress/st_newedgeheal.sql +++ b/topology/test/regress/st_newedgeheal.sql @@ -115,6 +115,49 @@ SELECT topology.DropTopology('t'); ------------------------------------------------------------------------- ------------------------------------------------------------------------- +SELECT '#1955', topology.CreateTopology('t') > 1; + +SELECT '#1955.1', 'E'||topology.AddEdge('t', 'LINESTRING(0 0, 10 0, 10 10)'); -- 1 +SELECT '#1955.1', 'E'||topology.AddEdge('t', 'LINESTRING(0 0, 0 10, 10 10)'); ; -- 2 + +SELECT '#1955.1', count(node_id), 'start nodes' as label FROM t.node GROUP BY label; + +-- Deletes second node. Not very predictable which one is removed +SELECT '#1955.1', 'H:1,2', 'E' || topology.ST_NewEdgeHeal('t', 1, 2), 'created'; + +SELECT '#1955.1', count(node_id), 'nodes left' as label FROM t.node GROUP BY label; + +SELECT '#1955.2', 'E'||topology.AddEdge('t', 'LINESTRING(50 0, 60 0, 60 10)'); -- 4 +SELECT '#1955.2', 'E'||topology.AddEdge('t', 'LINESTRING(50 0, 50 10, 60 10)'); ; -- 5 +SELECT '#1955.2', 'E'||topology.AddEdge('t', 'LINESTRING(60 10, 70 10)'); ; -- 6 + +SELECT '#1955.2', count(node_id), 'start nodes' as label FROM t.node GROUP BY label; + +-- Only the start node can be deleted (50 0) because the other is shared by +-- another edge +SELECT '#1955.2', 'H:4,5', 'E' || topology.ST_NewEdgeHeal('t', 4, 5), 'created'; + +SELECT '#1955.2', count(node_id), 'nodes left' as label FROM t.node GROUP BY label; + +SELECT '#1955.3', 'E'||topology.AddEdge('t', 'LINESTRING(80 0, 90 0, 90 10)'); -- 8 +SELECT '#1955.3', 'E'||topology.AddEdge('t', 'LINESTRING(80 0, 80 10, 90 10)'); ; -- 9 +SELECT '#1955.3', 'E'||topology.AddEdge('t', 'LINESTRING(70 10, 80 0)'); ; -- 10 + +SELECT '#1955.3', count(node_id), 'start nodes' as label FROM t.node GROUP BY label; + +-- Only the end node can be deleted (90 10) because the other is shared by +-- another edge +SELECT '#1955.3', 'H:8,9', 'E' || topology.ST_NewEdgeHeal('t', 8, 9), 'created'; + +SELECT '#1955.3', count(node_id), 'nodes left' as label FROM t.node GROUP BY label; + +SELECT '#1955', topology.DropTopology('t'); + +------------------------------------------------------------------------- +------------------------------------------------------------------------- +------------------------------------------------------------------------- + + -- TODO: test registered but unexistent topology -- TODO: test registered but corrupted topology -- (missing node, edge, relation...) diff --git a/topology/test/regress/st_newedgeheal_expected b/topology/test/regress/st_newedgeheal_expected index 6593547d7..09be659bc 100644 --- a/topology/test/regress/st_newedgeheal_expected +++ b/topology/test/regress/st_newedgeheal_expected @@ -9,7 +9,7 @@ ERROR: SQL/MM Spatial exception - null argument ERROR: SQL/MM Spatial exception - null argument ERROR: SQL/MM Spatial exception - invalid topology name ERROR: SQL/MM Spatial exception - non-connected edges -ERROR: SQL/MM Spatial exception - other edges connected (ie: 19) +ERROR: SQL/MM Spatial exception - other edges connected (19,20) ERROR: Edge 2 is closed, cannot heal to edge 3 ERROR: Edge 2 is closed, cannot heal to edge 3 ERROR: Cannot heal edge 25 with itself, try with another @@ -120,3 +120,22 @@ ERROR: SQL/MM Spatial exception - non-connected edges 2|5 3|-5 Topology 't' dropped +#1955|t +#1955.1|E1 +#1955.1|E2 +#1955.1|2|start nodes +#1955.1|H:1,2|E3|created +#1955.1|1|nodes left +#1955.2|E4 +#1955.2|E5 +#1955.2|E6 +#1955.2|4|start nodes +#1955.2|H:4,5|E7|created +#1955.2|3|nodes left +#1955.3|E8 +#1955.3|E9 +#1955.3|E10 +#1955.3|5|start nodes +#1955.3|H:8,9|E11|created +#1955.3|4|nodes left +#1955|Topology 't' dropped