From: Sandro Santilli Date: Wed, 4 May 2011 18:19:51 +0000 (+0000) Subject: Complete primitive (SQL/MM) portion of ST_ModEdgeHeal, regress test [RT-SIGTA] X-Git-Tag: 2.0.0alpha1~1721 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9ca3b1669e430725a1815f3e0f680e73bfacc451;p=postgis Complete primitive (SQL/MM) portion of ST_ModEdgeHeal, regress test [RT-SIGTA] git-svn-id: http://svn.osgeo.org/postgis/trunk@7086 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/topology/sql/sqlmm.sql b/topology/sql/sqlmm.sql index 3368ad9da..6d4617cea 100644 --- a/topology/sql/sqlmm.sql +++ b/topology/sql/sqlmm.sql @@ -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; diff --git a/topology/test/Makefile b/topology/test/Makefile index 946c0d8fe..c3bc7f40c 100644 --- a/topology/test/Makefile +++ b/topology/test/Makefile @@ -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 index 000000000..1b5852fd6 --- /dev/null +++ b/topology/test/regress/st_modedgeheal.sql @@ -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 index 000000000..3a832f3e5 --- /dev/null +++ b/topology/test/regress/st_modedgeheal_expected @@ -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