commonnode int;
caseno int;
sql text;
+ e2sign int;
BEGIN
--
-- toponame and face_id are required
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
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
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.
-- 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;
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 \
--- /dev/null
+\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 !
--- /dev/null
+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