<para>
<xref linkend="ST_NewEdgesSplit"/>
<xref linkend="ST_ModEdgeHeal"/>
+ <xref linkend="ST_NewEdgeHeal"/>
<xref linkend="AddEdge"/>
</para>
</refsection>
</para>
</refsection>
</refentry>
+
+ <refentry id="ST_NewEdgeHeal">
+ <refnamediv>
+ <refname>ST_NewEdgeHeal</refname>
+
+ <refpurpose>
+Heal two edges by deleting the node connecting them, deleting both edges,
+and replacing them with an edge whose direction is the same as the first
+edge provided.
+ </refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>text <function>ST_NewEdgeHeal</function></funcdef>
+ <paramdef><type>varchar </type> <parameter>atopology</parameter></paramdef>
+ <paramdef><type>integer </type> <parameter>anedge</parameter></paramdef>
+ <paramdef><type>integer </type> <parameter>anotheredge</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsection>
+ <title>Description</title>
+
+ <para>
+Heal two edges by deleting the node connecting them, deleting both edges,
+and replacing them with an edge whose direction is the same as the first
+edge provided.
+Returns the id of the new edge replacing the healed ones.
+Updates all existing joined edges and relationships accordingly.
+ </para>
+
+
+ <!-- use this format if new function -->
+ <para>Availability: 2.0</para>
+ <para>&sqlmm_compliant; SQL-MM: Topo-Geo and Topo-Net 3: Routine Details: X.3.9</para>
+ </refsection>
+
+
+ <!-- Optionally add a "See Also" section -->
+ <refsection>
+ <title>See Also</title>
+ <para>
+ <xref linkend="ST_ModEdgeHeal"/>
+ <xref linkend="ST_ModEdgeSplit"/>
+ <xref linkend="ST_NewEdgesSplit"/>
+ </para>
+ </refsection>
+ </refentry>
+
<refentry id="ST_MoveIsoNode">
<refnamediv>
<refname>ST_MoveIsoNode</refname>
<para>
<xref linkend="ST_ModEdgeSplit"/>
<xref linkend="ST_ModEdgeHeal"/>
+ <xref linkend="ST_NewEdgeHeal"/>
<xref linkend="AddEdge"/>
</para>
</refsection>
LANGUAGE 'plpgsql' VOLATILE;
--} ST_GetFaceEdges
+--{
+-- Topo-Geo and Topo-Net 3: Routine Details
+-- X.3.10
+--
+-- ST_NewEdgeHeal(atopology, anedge, anotheredge)
+--
+-- Not in the specs:
+-- * Refuses to heal two edges if any of the two is closed
+-- * Raise an exception when trying to heal an edge with itself
+-- * Raise an exception if any TopoGeometry is defined by only one
+-- of the two edges
+-- * Update references in the Relation table.
+--
+CREATE OR REPLACE FUNCTION topology.ST_NewEdgeHeal(toponame varchar, e1id integer, e2id integer)
+ RETURNS int
+AS
+$$
+DECLARE
+ e1rec RECORD;
+ e2rec RECORD;
+ rec RECORD;
+ newedgeid int;
+ commonnode int;
+ caseno int;
+ topoid int;
+ sql text;
+ e2sign int;
+ eidary int[];
+BEGIN
+ --
+ -- toponame and face_id are required
+ --
+ IF toponame IS NULL OR e1id IS NULL OR e2id IS NULL THEN
+ RAISE EXCEPTION 'SQL/MM Spatial exception - null argument';
+ END IF;
+
+ -- NOT IN THE SPECS: see if the same edge is given twice..
+ IF e1id = e2id THEN
+ RAISE EXCEPTION 'Cannot heal edge % with itself, try with another', e1id;
+ END IF;
+
+ -- Get topology id
+ BEGIN
+ SELECT id FROM topology.topology
+ INTO STRICT topoid WHERE name = toponame;
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
+ END;
+
+ BEGIN
+ EXECUTE 'SELECT * FROM ' || quote_ident(toponame)
+ || '.edge_data WHERE edge_id = ' || e1id
+ INTO STRICT e1rec;
+ EXCEPTION
+ WHEN NO_DATA_FOUND THEN
+ RAISE EXCEPTION 'SQL/MM Spatial exception – non-existent edge %', e1id;
+ WHEN INVALID_SCHEMA_NAME THEN
+ RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
+ WHEN UNDEFINED_TABLE THEN
+ RAISE EXCEPTION 'corrupted topology "%" (missing edge_data table)',
+ toponame;
+ END;
+
+ BEGIN
+ EXECUTE 'SELECT * FROM ' || quote_ident(toponame)
+ || '.edge_data WHERE edge_id = ' || e2id
+ INTO STRICT e2rec;
+ 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.
+ IF e1rec.start_node = e1rec.end_node THEN
+ RAISE EXCEPTION 'Edge % is closed, cannot heal to edge %', e1id, e2id;
+ END IF;
+ IF e2rec.start_node = e2rec.end_node THEN
+ RAISE EXCEPTION 'Edge % is closed, cannot heal to edge %', e2id, e1id;
+ 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;
+
+ -- NOT IN THE SPECS:
+ -- check if any topo_geom is defined only by one of the
+ -- input edges. In such case there would be no way to adapt
+ -- the definition in case of healing, so we'd have to bail out
+ eidary = ARRAY[e1id, e2id];
+ sql := 'SELECT t.* from ('
+ || 'SELECT r.topogeo_id, r.layer_id'
+ || ', l.schema_name, l.table_name, l.feature_column'
+ || ', array_agg(abs(r.element_id)) as elems '
+ || 'FROM topology.layer l INNER JOIN '
+ || quote_ident(toponame)
+ || '.relation r ON (l.layer_id = r.layer_id) '
+ || 'WHERE l.level = 0 AND l.feature_type = 2 '
+ || ' AND l.topology_id = ' || topoid
+ || ' AND abs(r.element_id) IN (' || e1id || ',' || e2id || ') '
+ || 'group by r.topogeo_id, r.layer_id, l.schema_name, l.table_name, '
+ || ' l.feature_column ) t WHERE NOT t.elems @> '
+ || quote_literal(eidary);
+ --RAISE DEBUG 'SQL: %', sql;
+ FOR rec IN EXECUTE sql LOOP
+ RAISE EXCEPTION 'TopoGeom % in layer % (%.%.%) cannot be represented healing edges % and %',
+ rec.topogeo_id, rec.layer_id,
+ rec.schema_name, rec.table_name, rec.feature_column,
+ e1id, e2id;
+ END LOOP;
+
+ -- Create new 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;
+ -- }
+
+ -- Insert new edge {
+ EXECUTE 'SELECT nextval(' || quote_literal(
+ quote_ident(toponame) || '.edge_data_edge_id_seq'
+ ) || ')' INTO STRICT newedgeid;
+ EXECUTE 'INSERT INTO ' || quote_ident(toponame)
+ || '.edge VALUES(' || newedgeid
+ || ',' || rec.start_node
+ || ',' || rec.end_node
+ || ',' || rec.next_left_edge
+ || ',' || rec.next_right_edge
+ || ',' || rec.left_face
+ || ',' || rec.right_face
+ || ',' || quote_literal(rec.geom::text)
+ || ')';
+ -- End of new edge insertion }
+
+ -- Update next_left_edge/next_right_edge for
+ -- any edge having them still pointing at the edges 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 = ' || newedgeid
+ || ', next_left_edge = ' || e2sign*newedgeid
+ || '*(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 = ' || newedgeid
+ || ', next_right_edge = ' || e2sign*newedgeid
+ || '*(next_right_edge/'
+ || e2id || ') WHERE abs_next_right_edge = ' || e2id;
+ --RAISE DEBUG 'SQL: %', sql;
+ EXECUTE sql;
+
+ -- New edge has the same direction as old edge 1
+ sql := 'UPDATE ' || quote_ident(toponame)
+ || '.edge_data SET abs_next_left_edge = ' || newedgeid
+ || ', next_left_edge = ' || newedgeid
+ || '*(next_left_edge/'
+ || e1id || ') WHERE abs_next_left_edge = ' || e1id;
+ --RAISE DEBUG 'SQL: %', sql;
+ EXECUTE sql;
+ sql := 'UPDATE ' || quote_ident(toponame)
+ || '.edge_data SET abs_next_right_edge = ' || newedgeid
+ || ', next_right_edge = ' || newedgeid
+ || '*(next_right_edge/'
+ || e1id || ') WHERE abs_next_right_edge = ' || e1id;
+ --RAISE DEBUG 'SQL: %', sql;
+ EXECUTE sql;
+
+ --
+ -- NOT IN THE SPECS:
+ -- Replace composition rows involving the two
+ -- edges as one involving the new edge.
+ -- It makes a DELETE and an UPDATE to do all
+ sql := 'DELETE FROM ' || quote_ident(toponame)
+ || '.relation r USING topology.layer l '
+ || 'WHERE l.level = 0 AND l.feature_type = 2'
+ || ' AND l.topology_id = ' || topoid
+ || ' AND l.layer_id = r.layer_id AND abs(r.element_id) = '
+ || e2id;
+ --RAISE DEBUG 'SQL: %', sql;
+ EXECUTE sql;
+ sql := 'UPDATE ' || quote_ident(toponame)
+ || '.relation r '
+ || ' SET element_id = ' || newedgeid || '*(element_id/'
+ || e1id
+ || ') FROM topology.layer l WHERE l.level = 0 AND l.feature_type = 2'
+ || ' AND l.topology_id = ' || topoid
+ || ' AND l.layer_id = r.layer_id AND abs(r.element_id) = '
+ || e1id
+ ;
+ RAISE DEBUG 'SQL: %', sql;
+ EXECUTE sql;
+
+
+ -- Delete both edges
+ EXECUTE 'DELETE FROM ' || quote_ident(toponame)
+ || '.edge_data WHERE edge_id = ' || e2id;
+ EXECUTE 'DELETE FROM ' || quote_ident(toponame)
+ || '.edge_data WHERE edge_id = ' || e1id;
+
+ -- Delete the common node
+ BEGIN
+ EXECUTE 'DELETE FROM ' || quote_ident(toponame)
+ || '.node WHERE node_id = ' || commonnode;
+ EXCEPTION
+ WHEN UNDEFINED_TABLE THEN
+ RAISE EXCEPTION 'corrupted topology "%" (missing node table)',
+ toponame;
+ END;
+
+ RETURN newedgeid;
+END
+$$
+LANGUAGE 'plpgsql' VOLATILE;
+--} ST_NewEdgeHeal
+
--{
-- Topo-Geo and Topo-Net 3: Routine Details
-- X.3.11
regress/st_getfacegeometry.sql \
regress/st_getfaceedges.sql \
regress/st_modedgeheal.sql \
+ regress/st_newedgeheal.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_NewEdgeHeal('city_data', 1, null);
+SELECT topology.ST_NewEdgeHeal('city_data', null, 1);
+SELECT topology.ST_NewEdgeHeal(null, 1, 2);
+SELECT topology.ST_NewEdgeHeal('', 1, 2);
+
+-- Not connected edges
+SELECT topology.ST_NewEdgeHeal('city_data', 25, 3);
+
+-- Other connected edges
+SELECT topology.ST_NewEdgeHeal('city_data', 9, 10);
+
+-- Closed edge
+SELECT topology.ST_NewEdgeHeal('city_data', 2, 3);
+SELECT topology.ST_NewEdgeHeal('city_data', 3, 2);
+
+-- Heal to self
+SELECT topology.ST_NewEdgeHeal('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_NewEdgeHeal('city_data', 4, 5);
+
+-- Face and other edges involved, SQL/MM caseno 1, drops node 16
+SELECT 'MH(21,6)', topology.ST_NewEdgeHeal('city_data', 21, 6);
+-- Face and other edges involved, SQL/MM caseno 2, drops node 19
+SELECT 'MH(8,15)', topology.ST_NewEdgeHeal('city_data', 8, 15);
+-- Face and other edges involved, SQL/MM caseno 3, drops node 8
+SELECT 'MH(12,22)', topology.ST_NewEdgeHeal('city_data', 12, 22);
+-- Face and other edges involved, SQL/MM caseno 4, drops node 11
+SELECT 'MH(16,14)', topology.ST_NewEdgeHeal('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');
+
+-------------------------------------------------------------------------
+-------------------------------------------------------------------------
+-------------------------------------------------------------------------
+
+-- Now test in presence of features
+
+SELECT topology.CreateTopology('t') > 1;
+CREATE TABLE t.f(id varchar);
+SELECT topology.AddTopoGeometryColumn('t', 't', 'f','g', 'LINE');
+
+SELECT 'E'||topology.AddEdge('t', 'LINESTRING(2 2, 2 8)'); -- 1
+SELECT 'E'||topology.AddEdge('t', 'LINESTRING(2 8, 8 8)'); -- 2
+
+INSERT INTO t.f VALUES ('F+E1',
+ topology.CreateTopoGeom('t', 2, 1, '{{1,2}}'));
+
+-- This should be forbidden, as F+E1 above could not be
+-- defined w/out one of the edges
+SELECT topology.ST_NewEdgeHeal('t', 1, 2);
+SELECT topology.ST_NewEdgeHeal('t', 2, 1);
+
+-- This is for ticket #941
+SELECT topology.ST_NewEdgeHeal('t', 1, 200);
+SELECT topology.ST_NewEdgeHeal('t', 100, 2);
+
+-- Now see how signed edges are updated
+
+SELECT 'E'||topology.AddEdge('t', 'LINESTRING(0 0, 5 0)'); -- 3
+SELECT 'E'||topology.AddEdge('t', 'LINESTRING(10 0, 5 0)'); -- 4
+
+INSERT INTO t.f VALUES ('F+E3-E4',
+ topology.CreateTopoGeom('t', 2, 1, '{{3,2},{-4,2}}'));
+INSERT INTO t.f VALUES ('F-E3+E4',
+ topology.CreateTopoGeom('t', 2, 1, '{{-3,2},{4,2}}'));
+
+SELECT r.topogeo_id, r.element_id
+ FROM t.relation r, t.f f WHERE
+ r.layer_id = layer_id(f.g) AND r.topogeo_id = id(f.g)
+ AND r.topogeo_id in (2,3)
+ ORDER BY r.layer_id, r.topogeo_id;
+
+-- This is fine, but will have to tweak definition of
+-- 'F+E3-E4' and 'F-E3+E4'
+SELECT 'MH(3,4)', topology.ST_NewEdgeHeal('t', 3, 4);
+
+-- This is for ticket #942
+SELECT topology.ST_NewEdgeHeal('t', 1, 5);
+
+SELECT r.topogeo_id, r.element_id
+ FROM t.relation r, t.f f WHERE
+ r.layer_id = layer_id(f.g) AND r.topogeo_id = id(f.g)
+ AND r.topogeo_id in (2,3)
+ ORDER BY r.layer_id, r.topogeo_id;
+
+SELECT topology.DropTopology('t');
+
+-------------------------------------------------------------------------
+-------------------------------------------------------------------------
+-------------------------------------------------------------------------
+
+-- TODO: test registered but unexistent topology
+-- TODO: test registered but corrupted topology
+-- (missing node, edge, relation...)
--- /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: 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: 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)|27
+MH(21,6)|28
+MH(8,15)|29
+MH(12,22)|30
+MH(16,14)|31
+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
+E7|POINT(21 22)|POINT(35 22)|29|-19|17|18
+E9|POINT(9 14)|POINT(21 14)|19|30|15|14
+E10|POINT(35 14)|POINT(21 14)|-20|17|13|14
+E11|POINT(35 14)|POINT(47 14)|-29|-18|13|12
+E13|POINT(21 6)|POINT(35 6)|18|-30|9|10
+E17|POINT(35 14)|POINT(35 22)|-7|11|13|18
+E18|POINT(35 6)|POINT(35 14)|10|31|10|13
+E19|POINT(21 14)|POINT(21 22)|-28|-10|14|17
+E20|POINT(21 6)|POINT(21 14)|-9|13|9|14
+E25|POINT(9 35)|POINT(13 35)|-25|25|21|22
+E26|POINT(4 31)|POINT(4 31)|26|-26|20|20
+E27|POINT(36 38)|POINT(41 40)|-27|27|5|7
+E28|POINT(9 14)|POINT(21 22)|7|9|15|17
+E29|POINT(35 22)|POINT(47 14)|-31|-17|18|12
+E30|POINT(9 14)|POINT(21 6)|20|28|15|9
+E31|POINT(35 6)|POINT(47 14)|-11|-13|10|12
+N1
+N2
+N3
+N4
+N5
+N7
+N9
+N10
+N12
+N13
+N14
+N15
+N17
+N18
+N20
+N21
+N22
+Topology 'city_data' dropped
+t
+1
+E1
+E2
+ERROR: TopoGeom 1 in layer 1 (t.f.g) cannot be represented healing edges 1 and 2
+ERROR: TopoGeom 1 in layer 1 (t.f.g) cannot be represented healing edges 2 and 1
+ERROR: SQL/MM Spatial exception – non-existent edge 200
+ERROR: SQL/MM Spatial exception – non-existent edge 100
+E3
+E4
+2|-4
+2|3
+3|4
+3|-3
+MH(3,4)|5
+ERROR: SQL/MM Spatial exception – non-connected edges
+2|5
+3|-5
+Topology 't' dropped