]> granicus.if.org Git - postgis/commitdiff
Complete primitive (SQL/MM) portion of ST_ModEdgeHeal, regress test [RT-SIGTA]
authorSandro Santilli <strk@keybit.net>
Wed, 4 May 2011 18:19:51 +0000 (18:19 +0000)
committerSandro Santilli <strk@keybit.net>
Wed, 4 May 2011 18:19:51 +0000 (18:19 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@7086 b70326c6-7e19-0410-871a-916f4a2858ee

topology/sql/sqlmm.sql
topology/test/Makefile
topology/test/regress/st_modedgeheal.sql [new file with mode: 0644]
topology/test/regress/st_modedgeheal_expected [new file with mode: 0644]

index 3368ad9da7c1a0fb08782068fcf3a2f0226dac70..6d4617ceab03e095e33f374836bd9ddf8b390d1d 100644 (file)
@@ -124,6 +124,7 @@ DECLARE
   commonnode int;
   caseno int;
   sql text;
+  e2sign int;
 BEGIN
   --
   -- toponame and face_id are required
@@ -137,6 +138,11 @@ BEGIN
     RAISE EXCEPTION 'Cannot heal edge % with itself, try with another', e1id;
   END IF;
 
+  -- NOT IN THE SPECS: check toponame is not empty
+  IF toponame = '' THEN
+    RAISE EXCEPTION 'Invalid (empty) topology name';
+  END IF;
+
   BEGIN
     EXECUTE 'SELECT * FROM ' || quote_ident(toponame)
       || '.edge_data WHERE edge_id = ' || e1id
@@ -144,6 +150,11 @@ BEGIN
     EXCEPTION
       WHEN NO_DATA_FOUND THEN
         RAISE EXCEPTION 'SQL/MM Spatial exception – non-existent edge %', e1id;
+      WHEN INVALID_SCHEMA_NAME THEN
+        RAISE EXCEPTION 'non-existent topology "%"', toponame;
+      WHEN UNDEFINED_TABLE THEN
+        RAISE EXCEPTION 'Invalid topology "%" (missing edge_data table)',
+          toponame;
   END;
 
   BEGIN
@@ -153,6 +164,7 @@ BEGIN
     EXCEPTION
       WHEN NO_DATA_FOUND THEN
         RAISE EXCEPTION 'SQL/MM Spatial exception – non-existent edge %', e2id;
+    -- NOTE: checks for INVALID_SCHEMA_NAME or UNDEFINED_TABLE done before
   END;
 
   -- NOT IN THE SPECS: See if any of the two edges are closed.
@@ -183,18 +195,98 @@ BEGIN
   -- 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 = 2 OR end_node = 2 )'
+    || ' 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;
 
-  -- Now: delete the common node (expect panic!)
+  -- Update data of the first edge {
+  rec := e1rec;
+  rec.geom = ST_LineMerge(ST_Collect(e1rec.geom, e2rec.geom));
+  IF caseno = 1 THEN -- e1.end = e2.start
+    IF NOT ST_Equals(ST_StartPoint(rec.geom), ST_StartPoint(e1rec.geom)) THEN
+      RAISE DEBUG 'caseno=1: LineMerge did not maintain startpoint';
+      rec.geom = ST_Reverse(rec.geom);
+    END IF;
+    rec.end_node = e2rec.end_node;
+    rec.next_left_edge = e2rec.next_left_edge;
+    e2sign = 1;
+  ELSIF caseno = 2 THEN -- e1.end = e2.end
+    IF NOT ST_Equals(ST_StartPoint(rec.geom), ST_StartPoint(e1rec.geom)) THEN
+      RAISE DEBUG 'caseno=2: LineMerge did not maintain startpoint';
+      rec.geom = ST_Reverse(rec.geom);
+    END IF;
+    rec.end_node = e2rec.start_node;
+    rec.next_left_edge = e2rec.next_right_edge;
+    e2sign = -1;
+  ELSIF caseno = 3 THEN -- e1.start = e2.start
+    IF NOT ST_Equals(ST_EndPoint(rec.geom), ST_EndPoint(e1rec.geom)) THEN
+      RAISE DEBUG 'caseno=4: LineMerge did not maintain endpoint';
+      rec.geom = ST_Reverse(rec.geom);
+    END IF;
+    rec.start_node = e2rec.end_node;
+    rec.next_right_edge = e2rec.next_left_edge;
+    e2sign = -1;
+  ELSIF caseno = 4 THEN -- e1.start = e2.end
+    IF NOT ST_Equals(ST_EndPoint(rec.geom), ST_EndPoint(e1rec.geom)) THEN
+      RAISE DEBUG 'caseno=4: LineMerge did not maintain endpoint';
+      rec.geom = ST_Reverse(rec.geom);
+    END IF;
+    rec.start_node = e2rec.start_node;
+    rec.next_right_edge = e2rec.next_right_edge;
+    e2sign = 1;
+  END IF;
+  EXECUTE 'UPDATE ' || quote_ident(toponame)
+    || '.edge_data SET geom = ' || quote_literal(rec.geom::text)
+    || ', start_node = ' || rec.start_node
+    || ', end_node = ' || rec.end_node
+    || ', next_left_edge = ' || rec.next_left_edge
+    || ', abs_next_left_edge = ' || abs(rec.next_left_edge)
+    || ', next_right_edge = ' || rec.next_right_edge
+    || ', abs_next_right_edge = ' || abs(rec.next_right_edge)
+    || ' WHERE edge_id = ' || e1id;
+  -- End of first edge update }
+
+  -- Update next_left_edge/next_right_edge for
+  -- any edge having them still pointing at the edge being removed (e2id)
+  --
+  -- NOTE:
+  -- *(next_XXX_edge/e2id) serves the purpose of extracting existing
+  -- sign from the value, while *e2sign changes that sign again if we
+  -- reverted edge2 direction
+  --
+  sql := 'UPDATE ' || quote_ident(toponame)
+    || '.edge_data SET abs_next_left_edge = ' || e1id
+    || ', next_left_edge = ' || e2sign*e1id
+    || '*(next_left_edge/'
+    || e2id || ')  WHERE abs_next_left_edge = ' || e2id;
+  --RAISE DEBUG 'SQL: %', sql;
+  EXECUTE sql;
+  sql := 'UPDATE ' || quote_ident(toponame)
+    || '.edge_data SET abs_next_right_edge = ' || e1id
+    || ', next_right_edge = ' || e2sign*e1id
+    || '*(next_right_edge/'
+    || e2id || ') WHERE abs_next_right_edge = ' || e2id;
+  --RAISE DEBUG 'SQL: %', sql;
+  EXECUTE sql;
+
+  -- Delete the second edge
   EXECUTE 'DELETE FROM ' || quote_ident(toponame)
-          || '.node WHERE node_id = ' || commonnode;
-  
+    || '.edge_data WHERE edge_id = ' || e2id;
 
-       RAISE EXCEPTION 'Not implemented yet';
+  -- Delete the common node 
+  BEGIN
+    EXECUTE 'DELETE FROM ' || quote_ident(toponame)
+            || '.node WHERE node_id = ' || commonnode;
+    EXCEPTION
+      WHEN UNDEFINED_TABLE THEN
+        RAISE EXCEPTION 'Invalid topology "%" (missing node table)',
+          toponame;
+  END;
+
+       RETURN commonnode;
 END
 $$
 LANGUAGE 'plpgsql' VOLATILE;
index 946c0d8fe6550e0a1e13198fc20019c6b841f352..c3bc7f40c5bbd2f130ce96b5928e1625bb1effb6 100644 (file)
@@ -30,6 +30,7 @@ TESTS = regress/legacy_validate.sql regress/legacy_predicate.sql \
        regress/polygonize.sql \
        regress/st_getfacegeometry.sql \
        regress/st_getfaceedges.sql \
+       regress/st_modedgeheal.sql \
        regress/topoelement.sql \
        regress/topoelementarray_agg.sql \
        regress/topo2.5d.sql \
diff --git a/topology/test/regress/st_modedgeheal.sql b/topology/test/regress/st_modedgeheal.sql
new file mode 100644 (file)
index 0000000..1b5852f
--- /dev/null
@@ -0,0 +1,60 @@
+\set VERBOSITY terse
+set client_min_messages to ERROR;
+
+-- Import city_data
+\i load_topology.sql
+
+SELECT topology.ST_ModEdgeHeal('city_data', 1, null);
+SELECT topology.ST_ModEdgeHeal('city_data', null, 1);
+SELECT topology.ST_ModEdgeHeal(null, 1, 2);
+SELECT topology.ST_ModEdgeHeal('', 1, 2);
+SELECT topology.ST_ModEdgeHeal('  ', 1, 2);
+SELECT topology.ST_ModEdgeHeal('public', 1, 2);
+
+-- Not connected edges
+SELECT topology.ST_ModEdgeHeal('city_data', 25, 3);
+
+-- Other connected edges
+SELECT topology.ST_ModEdgeHeal('city_data', 9, 10);
+
+-- Closed edge
+SELECT topology.ST_ModEdgeHeal('city_data', 2, 3);
+SELECT topology.ST_ModEdgeHeal('city_data', 3, 2);
+
+-- Heal to self 
+SELECT topology.ST_ModEdgeHeal('city_data', 25, 25);
+
+-- Good ones {
+
+-- check state before 
+SELECT 'E'||edge_id,
+  ST_AsText(ST_StartPoint(geom)), ST_AsText(ST_EndPoint(geom)),
+  next_left_edge, next_right_edge, start_node, end_node
+  FROM city_data.edge_data ORDER BY edge_id;
+SELECT 'N'||node_id FROM city_data.node;
+
+-- No other edges involved, SQL/MM caseno 2, drops node 6
+SELECT 'MH(4,5)', topology.ST_ModEdgeHeal('city_data', 4, 5);
+
+-- Face and other edges involved, SQL/MM caseno 1, drops node 16
+SELECT 'MH(21,6)', topology.ST_ModEdgeHeal('city_data', 21, 6);
+-- Face and other edges involved, SQL/MM caseno 2, drops node 19
+SELECT 'MH(8,15)', topology.ST_ModEdgeHeal('city_data', 8, 15);
+-- Face and other edges involved, SQL/MM caseno 3, drops node 8
+SELECT 'MH(12,22)', topology.ST_ModEdgeHeal('city_data', 12, 22);
+-- Face and other edges involved, SQL/MM caseno 4, drops node 11
+SELECT 'MH(16,14)', topology.ST_ModEdgeHeal('city_data', 16, 14);
+
+-- check state after
+SELECT 'E'||edge_id,
+  ST_AsText(ST_StartPoint(geom)), ST_AsText(ST_EndPoint(geom)),
+  next_left_edge, next_right_edge, start_node, end_node
+  FROM city_data.edge_data ORDER BY edge_id;
+SELECT 'N'||node_id FROM city_data.node;
+
+-- }
+
+-- clean up
+SELECT topology.DropTopology('city_data');
+
+-- TODO: add TopoGeometry tests !
diff --git a/topology/test/regress/st_modedgeheal_expected b/topology/test/regress/st_modedgeheal_expected
new file mode 100644 (file)
index 0000000..3a832f3
--- /dev/null
@@ -0,0 +1,105 @@
+BEGIN
+t
+8
+22
+26
+COMMIT
+ERROR:  SQL/MM Spatial exception - null argument
+ERROR:  SQL/MM Spatial exception - null argument
+ERROR:  SQL/MM Spatial exception - null argument
+ERROR:  Invalid (empty) topology name
+ERROR:  non-existent topology "  "
+ERROR:  Invalid topology "public" (missing edge_data table)
+ERROR:  SQL/MM Spatial exception – non-connected edges
+ERROR:  SQL/MM Spatial exception – other edges connected (ie: 19)
+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
+E1|POINT(8 30)|POINT(8 30)|1|-1|1|1
+E2|POINT(25 30)|POINT(25 30)|-3|-2|2|2
+E3|POINT(25 30)|POINT(25 35)|-3|2|2|3
+E4|POINT(36 38)|POINT(57 33)|-5|4|5|6
+E5|POINT(41 40)|POINT(57 33)|-4|5|7|6
+E6|POINT(9 22)|POINT(21 22)|7|-21|16|17
+E7|POINT(21 22)|POINT(35 22)|8|-19|17|18
+E8|POINT(35 22)|POINT(47 22)|-15|-17|18|19
+E9|POINT(9 14)|POINT(21 14)|19|-22|15|14
+E10|POINT(35 14)|POINT(21 14)|-20|17|13|14
+E11|POINT(35 14)|POINT(47 14)|15|-18|13|12
+E12|POINT(9 6)|POINT(21 6)|20|22|8|9
+E13|POINT(21 6)|POINT(35 6)|18|-12|9|10
+E14|POINT(35 6)|POINT(47 6)|16|-13|10|11
+E15|POINT(47 14)|POINT(47 22)|-8|-16|12|19
+E16|POINT(47 6)|POINT(47 14)|-11|-14|11|12
+E17|POINT(35 14)|POINT(35 22)|-7|11|13|18
+E18|POINT(35 6)|POINT(35 14)|10|14|10|13
+E19|POINT(21 14)|POINT(21 22)|-6|-10|14|17
+E20|POINT(21 6)|POINT(21 14)|-9|13|9|14
+E21|POINT(9 14)|POINT(9 22)|6|9|15|16
+E22|POINT(9 6)|POINT(9 14)|21|12|8|15
+E25|POINT(9 35)|POINT(13 35)|-25|25|21|22
+E26|POINT(4 31)|POINT(4 31)|26|-26|20|20
+N1
+N2
+N3
+N4
+N5
+N6
+N7
+N8
+N9
+N10
+N11
+N12
+N13
+N14
+N15
+N16
+N17
+N18
+N19
+N20
+N21
+N22
+MH(4,5)|6
+MH(21,6)|16
+MH(8,15)|19
+MH(12,22)|8
+MH(16,14)|11
+E1|POINT(8 30)|POINT(8 30)|1|-1|1|1
+E2|POINT(25 30)|POINT(25 30)|-3|-2|2|2
+E3|POINT(25 30)|POINT(25 35)|-3|2|2|3
+E4|POINT(36 38)|POINT(41 40)|-4|4|5|7
+E7|POINT(21 22)|POINT(35 22)|8|-19|17|18
+E8|POINT(35 22)|POINT(47 14)|-16|-17|18|12
+E9|POINT(9 14)|POINT(21 14)|19|12|15|14
+E10|POINT(35 14)|POINT(21 14)|-20|17|13|14
+E11|POINT(35 14)|POINT(47 14)|-8|-18|13|12
+E12|POINT(9 14)|POINT(21 6)|20|21|15|9
+E13|POINT(21 6)|POINT(35 6)|18|-12|9|10
+E16|POINT(35 6)|POINT(47 14)|-11|-13|10|12
+E17|POINT(35 14)|POINT(35 22)|-7|11|13|18
+E18|POINT(35 6)|POINT(35 14)|10|16|10|13
+E19|POINT(21 14)|POINT(21 22)|-21|-10|14|17
+E20|POINT(21 6)|POINT(21 14)|-9|13|9|14
+E21|POINT(9 14)|POINT(21 22)|7|9|15|17
+E25|POINT(9 35)|POINT(13 35)|-25|25|21|22
+E26|POINT(4 31)|POINT(4 31)|26|-26|20|20
+N1
+N2
+N3
+N4
+N5
+N7
+N9
+N10
+N12
+N13
+N14
+N15
+N17
+N18
+N20
+N21
+N22
+Topology 'city_data' dropped