]> granicus.if.org Git - postgis/commitdiff
Implement topology.ST_CreateTopoGeo (#1190) [RT-SIGTA]
authorSandro Santilli <strk@keybit.net>
Fri, 23 Sep 2011 19:21:08 +0000 (19:21 +0000)
committerSandro Santilli <strk@keybit.net>
Fri, 23 Sep 2011 19:21:08 +0000 (19:21 +0000)
Includes regress testing and documentation update

git-svn-id: http://svn.osgeo.org/postgis/trunk@7885 b70326c6-7e19-0410-871a-916f4a2858ee

doc/extras_topology.xml
topology/sql/sqlmm.sql
topology/test/Makefile
topology/test/regress/st_createtopogeo.sql [new file with mode: 0644]
topology/test/regress/st_createtopogeo_expected [new file with mode: 0644]

index e86d3988b45904cc60e11faf416697b8dddf2366..bbaf786b188c48b0ffd3b0f49f83a6c22fc04259 100644 (file)
@@ -1083,8 +1083,9 @@ UPDATE boston.blockgroups SET new_geom = topo::geometry;
                        <refnamediv>
                                <refname>ST_CreateTopoGeo</refname>
                        
-                               <refpurpose>Adds a collection of geometries to a given topology and returns a message detailing success.  If collection contains anything but points and linestrings, an error is thrown. Will also 
-                               throw an error if there are already edges and nodes in the topology. polygon support not implemented yet.</refpurpose>
+                               <refpurpose>
+Adds a collection of geometries to a given empty topology and returns a message detailing success.  
+                               </refpurpose>
                        </refnamediv>
                
                        <refsynopsisdiv>
@@ -1100,14 +1101,15 @@ UPDATE boston.blockgroups SET new_geom = topo::geometry;
                        <refsection>
                 <title>Description</title>
             
-                <para>Adds a collection of geometries to a given topology and returns a message detailing success.  If collection contains anything but points and linestrings an error is thrown. Will also 
-                               throw an error if there are already edges and nodes in the topology or coincident nodes etc.  Support for polygons not yet implemented.</para>
+                <para>
+Adds a collection of geometries to a given empty topology and returns a message detailing success.  
+                </para>
                                
                                <para>Useful for populating an empty topology.</para>
              
                 
                 <!-- use this format if new function -->
-                <para>Availability: 1.? </para>
+                <para>Availability: 2.0 </para>
                        <para>&sqlmm_compliant; SQL-MM: Topo-Geo and Topo-Net 3: Routine Details -- X.3.18</para>
                        </refsection>
                
index 52b9acdfc9b4401296a6140851b64e52c83b6630..ed086c92996ffa4e9feff3bfb3f0c2405b3fc149 100644 (file)
@@ -3904,94 +3904,166 @@ LANGUAGE 'plpgsql' VOLATILE;
 -- X.3.18
 --
 --  ST_CreateTopoGeo(atopology, acollection)
---
-CREATE OR REPLACE FUNCTION topology.ST_CreateTopoGeo(varchar, geometry)
+--}{
+CREATE OR REPLACE FUNCTION topology.ST_CreateTopoGeo(atopology varchar, acollection geometry)
 RETURNS text
 AS
 $$
 DECLARE
-  atopology alias for $1;
-  acollection alias for $2;
   typ char(4);
   rec RECORD;
   ret int;
-  schemaoid oid;
+  nodededges GEOMETRY;
+  points GEOMETRY;
+  snode_id int;
+  enode_id int;
+  tolerance FLOAT8;
+  topoinfo RECORD;
 BEGIN
+
   IF atopology IS NULL OR acollection IS NULL THEN
     RAISE EXCEPTION 'SQL/MM Spatial exception - null argument';
   END IF;
 
-  -- Verify existance of the topology schema 
-  FOR rec in EXECUTE 'SELECT oid FROM pg_namespace WHERE '
-    || ' nspname = ' || quote_literal(atopology)
-    || ' GROUP BY oid'
-    
-  LOOP
-    schemaoid := rec.oid;
-  END LOOP;
+  -- Get topology information
+  BEGIN
+    SELECT * FROM topology.topology
+      INTO STRICT topoinfo WHERE name = atopology;
+  EXCEPTION
+    WHEN NO_DATA_FOUND THEN
+      RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
+  END;
 
-  IF schemaoid IS NULL THEN
-  RAISE EXCEPTION 'SQL/MM Spatial exception - non-existent schema';
+  -- Check SRID compatibility
+  IF ST_SRID(acollection) != topoinfo.SRID THEN
+    RAISE EXCEPTION 'Geometry SRID (%) does not match topology SRID (%)',
+      ST_SRID(acollection), topoinfo.SRID;
   END IF;
 
-  -- Verify existance of the topology views in the topology schema 
-  FOR rec in EXECUTE 'SELECT count(*) FROM pg_class WHERE '
-    || ' relnamespace = ' || schemaoid 
-    || ' and relname = ''node'''
-    || ' OR relname = ''edge'''
-    || ' OR relname = ''face'''
-  LOOP
-    IF rec.count < 3 THEN
-  RAISE EXCEPTION 'SQL/MM Spatial exception - non-existent view';
-    END IF;
-  END LOOP;
+  -- Verify pre-conditions (valid, empty topology schema exists)
+  BEGIN -- {
 
-  -- Verify the topology views in the topology schema to be empty
-  FOR rec in EXECUTE
-    'SELECT count(*) FROM '
-    || quote_ident(atopology) || '.edge_data '
-    || ' UNION ' ||
-    'SELECT count(*) FROM '
-    || quote_ident(atopology) || '.node '
+    -- Verify the topology views in the topology schema to be empty
+    FOR rec in EXECUTE
+      'SELECT count(*) FROM '
+      || quote_ident(atopology) || '.edge_data '
+      || ' UNION ' ||
+      'SELECT count(*) FROM '
+      || quote_ident(atopology) || '.node '
+    LOOP
+      IF rec.count > 0 THEN
+    RAISE EXCEPTION 'SQL/MM Spatial exception - non-empty view';
+      END IF;
+    END LOOP;
+
+    -- face check is separated as it will contain a single (world)
+    -- face record
+    FOR rec in EXECUTE
+      'SELECT count(*) FROM '
+      || quote_ident(atopology) || '.face '
+    LOOP
+      IF rec.count != 1 THEN
+    RAISE EXCEPTION 'SQL/MM Spatial exception - non-empty face view';
+      END IF;
+    END LOOP;
+
+  EXCEPTION
+    WHEN INVALID_SCHEMA_NAME THEN
+      RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
+    WHEN UNDEFINED_TABLE THEN
+      RAISE EXCEPTION 'SQL/MM Spatial exception - non-existent view';
+
+  END; -- }
+
+  RAISE DEBUG 'Noding input linework';
+
+  --
+  -- Node input linework with itself
+  --
+  WITH components AS ( SELECT geom FROM ST_Dump(acollection) )
+  SELECT ST_UnaryUnion(ST_Collect(geom)) FROM (
+    SELECT geom FROM components
+      WHERE ST_Dimension(geom) = 1
+    UNION 
+    SELECT ST_Boundary(geom) FROM components
+      WHERE ST_Dimension(geom) = 2
+  ) as linework INTO STRICT nodededges;
+
+  RAISE DEBUG 'Computed % noded edges', ST_NumGeometries(nodededges);
+
+  --
+  -- Linemerge the resulting edges, to reduce the working set
+  --
+  SELECT ST_LineMerge(nodededges) INTO STRICT nodededges;
+
+  RAISE DEBUG 'Merged edges: %', ST_NumGeometries(nodededges);
+
+
+  --
+  -- Collect input points 
+  --
+  SELECT ST_Union(geom) FROM (
+    SELECT geom FROM ST_Dump(acollection)
+      WHERE ST_Dimension(geom) = 0
+  ) as nodes INTO STRICT points;
+
+  RAISE DEBUG 'Collected % input points', ST_NumGeometries(points);
+
+  --
+  -- Further split edges by points
+  --
+  FOR rec IN SELECT geom FROM ST_Dump(points)
   LOOP
-    IF rec.count > 0 THEN
-  RAISE EXCEPTION 'SQL/MM Spatial exception - non-empty view';
-    END IF;
+    -- Use the node to split edges
+    SELECT ST_Union(geom) -- TODO: ST_UnaryUnion ?
+    FROM ST_Dump(ST_Split(nodededges, rec.geom))
+    INTO STRICT nodededges;
   END LOOP;
 
-  -- face check is separated as it will contain a single (world)
-  -- face record
-  FOR rec in EXECUTE
-    'SELECT count(*) FROM '
-    || quote_ident(atopology) || '.face '
+  RAISE DEBUG 'Noded edges became % after point-split',
+    ST_NumGeometries(nodededges);
+
+  --
+  -- Collect all nodes (from points and noded linework endpoints)
+  --
+
+  WITH edges AS ( SELECT geom FROM ST_Dump(nodededges) )
+  SELECT ST_Union( -- TODO: ST_UnaryUnion ?
+          COALESCE(ST_UnaryUnion(ST_Collect(geom)), 
+            ST_SetSRID('POINT EMPTY'::geometry, topoinfo.SRID)),
+          COALESCE(points,
+            ST_SetSRID('POINT EMPTY'::geometry, topoinfo.SRID))
+         )
+  FROM (
+    SELECT ST_StartPoint(geom) as geom FROM edges
+      UNION
+    SELECT ST_EndPoint(geom) FROM edges
+  ) as endpoints INTO points;
+
+  RAISE DEBUG 'Total nodes count: %', ST_NumGeometries(points);
+
+  --
+  -- Add all nodes as isolated so that 
+  -- later calls to AddEdgeModFace will tweak their being
+  -- isolated or not...
+  --
+  FOR rec IN SELECT geom FROM ST_Dump(points)
   LOOP
-    IF rec.count != 1 THEN
-  RAISE EXCEPTION 'SQL/MM Spatial exception - non-empty face view';
-    END IF;
+    PERFORM topology.ST_AddIsoNode(atopology, 0, rec.geom);
   END LOOP;
+  
 
-  -- 
-  -- LOOP through the elements invoking the specific function
-  -- 
-  FOR rec IN SELECT geom(ST_Dump(acollection))
+  FOR rec IN SELECT geom FROM ST_Dump(nodededges)
   LOOP
-    typ := substring(geometrytype(rec.geom), 1, 3);
-
-    IF typ = 'LIN' THEN
-  SELECT topology.TopoGeo_addLinestring(atopology, rec.geom) INTO ret;
-    ELSIF typ = 'POI' THEN
-  SELECT topology.TopoGeo_AddPoint(atopology, rec.geom) INTO ret;
-    ELSIF typ = 'POL' THEN
-  SELECT topology.TopoGeo_AddPolygon(atopology, rec.geom) INTO ret;
-    ELSE
-  RAISE EXCEPTION 'ST_CreateTopoGeo got unknown geometry type: %', typ;
-    END IF;
-
+    SELECT topology.GetNodeByPoint(atopology, st_startpoint(rec.geom), 0)
+      INTO STRICT snode_id;
+    SELECT topology.GetNodeByPoint(atopology, st_endpoint(rec.geom), 0)
+      INTO STRICT enode_id;
+    PERFORM topology.ST_AddEdgeModFace(atopology, snode_id, enode_id, rec.geom);
   END LOOP;
 
   RETURN 'Topology ' || atopology || ' populated';
 
-  RAISE EXCEPTION 'ST_CreateTopoGeo not implemente yet';
 END
 $$
 LANGUAGE 'plpgsql' VOLATILE;
index 0dc4acbd1e9031b84e9b31114416142ec340a02e..7f6c7adbf64eb95ebb12bcf6381599de6d111bef 100644 (file)
@@ -33,6 +33,7 @@ TESTS = regress/legacy_validate.sql regress/legacy_predicate.sql \
        regress/st_addedgemodface.sql \
        regress/st_addedgenewfaces.sql \
        regress/st_changeedgegeom.sql \
+       regress/st_createtopogeo.sql \
        regress/st_getfacegeometry.sql \
        regress/st_getfaceedges.sql \
        regress/st_modedgeheal.sql \
diff --git a/topology/test/regress/st_createtopogeo.sql b/topology/test/regress/st_createtopogeo.sql
new file mode 100644 (file)
index 0000000..4d6c70f
--- /dev/null
@@ -0,0 +1,208 @@
+\set VERBOSITY terse
+set client_min_messages to ERROR;
+
+INSERT INTO spatial_ref_sys ( auth_name, auth_srid, srid, proj4text ) VALUES ( 'EPSG', 4326, 4326, '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs' );
+
+-- Invalid topologies
+select topology.st_createtopogeo('', 'GEOMETRYCOLLECTION(POINT(0 0))');
+select topology.st_createtopogeo('t', 'GEOMETRYCOLLECTION(POINT(0 0))');
+select topology.st_createtopogeo(null, 'GEOMETRYCOLLECTION(POINT(0 0))');
+
+CREATE function print_isolated_nodes(lbl text)
+ RETURNS table(lbl text, msg text)
+AS $$
+DECLARE
+ sql text;
+BEGIN
+  sql := 'SELECT ' || quote_literal(lbl) || '::text, count(node_id)
+    || '' isolated nodes in face '' || containing_face
+    FROM t.node WHERE containing_face IS NOT NULL GROUP by containing_face
+    ORDER BY count(node_id), containing_face';
+  RETURN QUERY EXECUTE sql;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE function print_elements_count(lbl text)
+ RETURNS table(lbl text, nodes text, edges text, faces text)
+AS $$
+DECLARE
+ sql text;
+BEGIN
+  sql := 'select ' || quote_literal(lbl) || '::text, 
+       ( select count(node_id) || '' nodes'' from t.node ) as nodes,
+       ( select count(edge_id) || '' edges'' from t.edge ) as edges,
+       ( select count(face_id) || '' faces'' from t.face
+                                    where face_id <> 0 ) as faces';
+  RETURN QUERY EXECUTE sql;
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+-- Invalid geometries
+select null from ( select topology.CreateTopology('t', 4326) > 0 ) as ct;
+select topology.st_createtopogeo('t', null); -- Invalid geometry
+select 'invalid_srid', topology.st_createtopogeo('t', 'POINT(0 0)'); 
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Single point
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T1', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'POINT(0 0)'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T1');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Single line
+select null from ( select topology.CreateTopology('t', 4326) > 0 ) as ct;
+select 'T2', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'SRID=4326;LINESTRING(0 0, 8 -40)'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T2');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Single polygon with no holes
+select null from ( select topology.CreateTopology('t', 4326) > 0 ) as ct;
+select 'T3', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'SRID=4326;POLYGON((0 0, 8 -40, 70 34, 0 0))'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T3');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Single polygon with an hole
+select null from ( select topology.CreateTopology('t', 4326) > 0 ) as ct;
+select 'T4', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'SRID=4326;POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(5 5, 8 9, 4 2, 5 5))'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T4');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Multi point with duplicated points
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T5', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'MULTIPOINT(0 0, 5 5, 0 0, 10 -2, 5 5, 0 0)'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T5');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Multi line with duplicated lines
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T6', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'MULTILINESTRING((0 0, 10 0),(10 0, 0 0))'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T6');
+select * from print_isolated_nodes('T6');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Multi line with crossing lines
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T7', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'MULTILINESTRING((0 0, 10 0),(5 -5, 6 5))'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T7');
+select * from print_isolated_nodes('T7');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Multi polygon with duplicated polygons
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T8', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'MULTIPOLYGON(
+  ((0 0,10 0,10 10,0 10,0 0)),
+  ((0 0,0 10,10 10,10 0,0 0))
+)'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T8');
+select * from print_isolated_nodes('T8');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Multi polygon with overlapping polygons
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T9', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'MULTIPOLYGON(
+  ((0 0,10 0,10 10,0 10,0 0)),
+  ((5 5,5 15,15 15,15 5,5 5))
+)'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T9');
+select * from print_isolated_nodes('T9');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Multi polygon with touching polygons
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T10', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'MULTIPOLYGON(
+  ((0 0,5 10,10 0,0 0)),
+  ((0 20,5 10,10 20,0 20))
+)'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T10');
+select * from print_isolated_nodes('T10');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Collection of line and point within it
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T11', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'GEOMETRYCOLLECTION(LINESTRING(0 0, 10 0),POINT(5 0))'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T11');
+select * from print_isolated_nodes('T11');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Collection of line and points on line's endpoint
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T12', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'GEOMETRYCOLLECTION(LINESTRING(0 0, 10 0),POINT(0 0),POINT(10 0))'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T12');
+select * from print_isolated_nodes('T12');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Collection of line, points and polygons with various crossing and
+-- overlaps
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T13', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'GEOMETRYCOLLECTION(
+  MULTIPOLYGON(
+    ((0 0,10 0,10 10,0 10,0 0)),
+    ((5 5,5 15,15 15,15 5,5 5), (10 10, 12 10, 10 12, 10 10))
+  ),
+  LINESTRING(0 0, 20 0),
+  MULTIPOINT(0 0,10 0,5 0),
+  MULTILINESTRING((0 0, 10 0),(10 0, 15 5)),
+  POINT(5 0),
+  POINT(10.5 10.5),
+  POINT(100 500)
+)'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T13');
+select * from print_isolated_nodes('T13');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+-- Collection of all geometries which can be derivated by the
+-- well-known city_data topology
+select null from ( select topology.CreateTopology('t') > 0 ) as ct;
+select 'T14', st_asewkt(g) FROM (
+SELECT g, topology.st_createtopogeo('t', g) FROM ( SELECT
+'GEOMETRYCOLLECTION(LINESTRING(8 30,16 30,16 38,3 38,3 30,8 30),POINT(4 31),LINESTRING(4 31,7 31,7 34,4 34,4 31),POINT(8 30),POINT(9 6),LINESTRING(9 6,9 14),LINESTRING(9 6,21 6),POLYGON((9 14,21 14,21 6,9 6,9 14)),POINT(9 14),LINESTRING(9 14,9 22),LINESTRING(9 14,21 14),POLYGON((9 22,21 22,21 14,9 14,9 22)),POINT(9 22),LINESTRING(9 22,21 22),POINT(9 35),LINESTRING(9 35,13 35),POINT(13 35),POLYGON((25 30,17 30,17 40,31 40,31 30,25 30)),POINT(20 37),POINT(21 6),LINESTRING(21 6,21 14),LINESTRING(21 6,35 6),POLYGON((21 14,35 14,35 6,21 6,21 14)),POINT(21 14),LINESTRING(21 14,21 22),LINESTRING(35 14,21 14),POLYGON((21 22,35 22,35 14,21 14,21 22)),POINT(21 22),LINESTRING(21 22,35 22),POINT(25 30),LINESTRING(25 30,25 35),POINT(25 35),POINT(35 6),LINESTRING(35 6,35 14),LINESTRING(35 6,47 6),POLYGON((35 14,47 14,47 6,35 6,35 14)),POINT(35 14),LINESTRING(35 14,35 22),LINESTRING(35 14,47 14),POLYGON((35 22,47 22,47 14,35 14,35 22)),POINT(35 22),LINESTRING(35 22,47 22),LINESTRING(36 38,38 35,41 34,42 33,45 32,47 28,50 28,52 32,57 33),POINT(36 38),LINESTRING(41 40,45 40,47 42,62 41,61 38,59 39,57 36,57 33),POINT(41 40),POINT(47 6),LINESTRING(47 6,47 14),POINT(47 14),LINESTRING(47 14,47 22),POINT(47 22),POINT(57 33))'
+::geometry as g ) as i ) as j;
+select * from print_elements_count('T14');
+select * from print_isolated_nodes('T14');
+select null from ( select topology.DropTopology('t') ) as dt;
+
+
+-- clean up
+DELETE FROM spatial_ref_sys where srid = 4326;
+DROP FUNCTION print_isolated_nodes(text);
+DROP FUNCTION print_elements_count(text);
diff --git a/topology/test/regress/st_createtopogeo_expected b/topology/test/regress/st_createtopogeo_expected
new file mode 100644 (file)
index 0000000..33df17f
--- /dev/null
@@ -0,0 +1,36 @@
+ERROR:  SQL/MM Spatial exception - invalid topology name
+ERROR:  SQL/MM Spatial exception - invalid topology name
+ERROR:  SQL/MM Spatial exception - null argument
+ERROR:  SQL/MM Spatial exception - null argument
+ERROR:  Geometry SRID (-1) does not match topology SRID (4326)
+T1|POINT(0 0)
+T1|1 nodes|0 edges|0 faces
+T2|SRID=4326;LINESTRING(0 0,8 -40)
+T2|2 nodes|1 edges|0 faces
+T3|SRID=4326;POLYGON((0 0,8 -40,70 34,0 0))
+T3|1 nodes|1 edges|1 faces
+T4|SRID=4326;POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,8 9,4 2,5 5))
+T4|2 nodes|2 edges|2 faces
+T5|MULTIPOINT(0 0,5 5,0 0,10 -2,5 5,0 0)
+T5|3 nodes|0 edges|0 faces
+T6|MULTILINESTRING((0 0,10 0),(10 0,0 0))
+T6|2 nodes|1 edges|0 faces
+T7|MULTILINESTRING((0 0,10 0),(5 -5,6 5))
+T7|5 nodes|4 edges|0 faces
+T8|MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0)),((0 0,0 10,10 10,10 0,0 0)))
+T8|1 nodes|1 edges|1 faces
+T9|MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0)),((5 5,5 15,15 15,15 5,5 5)))
+T9|2 nodes|4 edges|3 faces
+T10|MULTIPOLYGON(((0 0,5 10,10 0,0 0)),((0 20,5 10,10 20,0 20)))
+T10|1 nodes|2 edges|2 faces
+T11|GEOMETRYCOLLECTION(LINESTRING(0 0,10 0),POINT(5 0))
+T11|3 nodes|2 edges|0 faces
+T12|GEOMETRYCOLLECTION(LINESTRING(0 0,10 0),POINT(0 0),POINT(10 0))
+T12|2 nodes|1 edges|0 faces
+T13|GEOMETRYCOLLECTION(MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0)),((5 5,5 15,15 15,15 5,5 5),(10 10,12 10,10 12,10 10))),LINESTRING(0 0,20 0),MULTIPOINT(0 0,10 0,5 0),MULTILINESTRING((0 0,10 0),(10 0,15 5)),POINT(5 0),POINT(10.5 10.5),POINT(100 500))
+T13|10 nodes|12 edges|5 faces
+T13|1 isolated nodes in face 0
+T13|1 isolated nodes in face 1
+T14|GEOMETRYCOLLECTION(LINESTRING(8 30,16 30,16 38,3 38,3 30,8 30),POINT(4 31),LINESTRING(4 31,7 31,7 34,4 34,4 31),POINT(8 30),POINT(9 6),LINESTRING(9 6,9 14),LINESTRING(9 6,21 6),POLYGON((9 14,21 14,21 6,9 6,9 14)),POINT(9 14),LINESTRING(9 14,9 22),LINESTRING(9 14,21 14),POLYGON((9 22,21 22,21 14,9 14,9 22)),POINT(9 22),LINESTRING(9 22,21 22),POINT(9 35),LINESTRING(9 35,13 35),POINT(13 35),POLYGON((25 30,17 30,17 40,31 40,31 30,25 30)),POINT(20 37),POINT(21 6),LINESTRING(21 6,21 14),LINESTRING(21 6,35 6),POLYGON((21 14,35 14,35 6,21 6,21 14)),POINT(21 14),LINESTRING(21 14,21 22),LINESTRING(35 14,21 14),POLYGON((21 22,35 22,35 14,21 14,21 22)),POINT(21 22),LINESTRING(21 22,35 22),POINT(25 30),LINESTRING(25 30,25 35),POINT(25 35),POINT(35 6),LINESTRING(35 6,35 14),LINESTRING(35 6,47 6),POLYGON((35 14,47 14,47 6,35 6,35 14)),POINT(35 14),LINESTRING(35 14,35 22),LINESTRING(35 14,47 14),POLYGON((35 22,47 22,47 14,35 14,35 22)),POINT(35 22),LINESTRING(35 22,47 22),LINESTRING(36 38,38 35,41 34,42 33,45 32,47 28,50 28,52 32,57 33),POINT(36 38),LINESTRING(41 40,45 40,47 42,62 41,61 38,59 39,57 36,57 33),POINT(41 40),POINT(47 6),LINESTRING(47 6,47 14),POINT(47 14),LINESTRING(47 14,47 22),POINT(47 22),POINT(57 33))
+T14|22 nodes|24 edges|9 faces
+T14|1 isolated nodes in face 3