]> granicus.if.org Git - postgis/commitdiff
Don't let ValidateTopology choke on invalid edges (#1544)
authorSandro Santilli <strk@keybit.net>
Mon, 6 Feb 2012 13:16:02 +0000 (13:16 +0000)
committerSandro Santilli <strk@keybit.net>
Mon, 6 Feb 2012 13:16:02 +0000 (13:16 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@9038 b70326c6-7e19-0410-871a-916f4a2858ee

topology/test/invalid_topology.sql
topology/test/regress/legacy_invalid_expected
topology/topology.sql.in.c

index 4d051463fa1afd1d5b033dcf243c2f3229b2ddfd..5695c0e198ee69e51376ea2296d6182266ed0b2d 100644 (file)
@@ -23,6 +23,8 @@ INSERT INTO invalid_topology.face(face_id) VALUES(10); -- F10
 INSERT INTO invalid_topology.face(face_id) VALUES(11); -- F11
 -- Next face overlaps F2
 INSERT INTO invalid_topology.face(face_id) VALUES(12); -- F12
+-- Next face is here only to serve as a placeholder for broken edges
+INSERT INTO invalid_topology.face(face_id) VALUES(13); -- F13
 
 -- Insert nodes
 INSERT INTO invalid_topology.node(node_id,geom,containing_face)
@@ -144,6 +146,10 @@ INSERT INTO invalid_topology.edge VALUES(31, 3, 3, 31, -31, 11, 11,
 INSERT INTO invalid_topology.edge VALUES(32, 4, 4, 31, -31, 12, 12,
   'LINESTRING(20 37, 20 42, 21 42, 21 37, 20 37)');
 
+-- Next edge is not valid 
+INSERT INTO invalid_topology.edge VALUES(33, 3, 3, 28, 28, 13, 13,
+  '01020000000100000000000000000039400000000000804140');
+
 -- Validate topology
 SELECT * from topology.validatetopology('invalid_topology');
 
index 5ae7d22785b699b6081d4c5e618e32669366c176..67e0404a44edbf0c63fe6ef1dc9b6ed4a73532ec 100644 (file)
@@ -4,6 +4,7 @@ coincident nodes|1|23
 edge crosses node|23|1
 edge crosses node|14|27
 edge not simple|28|
+invalid edge|33|
 edge crosses edge|2|28
 edge crosses edge|2|29
 edge crosses edge|2|32
index 0e4dc77792baea308b8a88bae262f5d4d654edc1..c7fb96accc1740e04649a23ed4f96b0841912841 100644 (file)
 --    Also updates the Relation table
 --  
 --   ST_GetFaceGeometry
---     Implemented using polygonize()
+--     Implemented using ST_BuildArea()
 --  
 --   ST_RemEdgeNewFace
 --    Complete
@@ -1318,6 +1318,9 @@ DECLARE
        rec RECORD;
        rec2 RECORD;
        i integer;
+       invalid_edges integer[];
+       invalid_faces integer[];
+       sql text;
 BEGIN
 
        -- Check for coincident nodes
@@ -1347,25 +1350,60 @@ BEGIN
                RETURN NEXT retrec;
        END LOOP;
 
-       -- Check for non-simple edges
-       FOR rec IN EXECUTE 'SELECT e.edge_id as id1 FROM '
-               || quote_ident(toponame) || '.edge e '
-               || 'WHERE not ST_IsSimple(e.geom)'
+       -- Check for invalid or non-simple edges
+       FOR rec IN EXECUTE 'SELECT e.geom, e.edge_id as id1, e.left_face, e.right_face FROM '
+               || quote_ident(toponame) || '.edge e ORDER BY edge_id'
        LOOP
-               retrec.error = 'edge not simple';
-               retrec.id1 = rec.id1;
-               retrec.id2 = NULL;
-               RETURN NEXT retrec;
+
+               -- Any invalid edge becomes a cancer for higher level complexes
+               IF NOT ST_IsValid(rec.geom) THEN
+
+                 retrec.error = 'invalid edge';
+                 retrec.id1 = rec.id1;
+                 retrec.id2 = NULL;
+                 RETURN NEXT retrec;
+                       invalid_edges := array_append(invalid_edges, rec.id1);
+
+      IF invalid_faces IS NULL OR NOT rec.left_face = ANY ( invalid_faces )
+                       THEN
+                               invalid_faces := array_append(invalid_faces, rec.left_face);
+                       END IF;
+
+      IF rec.right_face != rec.left_face AND ( invalid_faces IS NULL OR
+                                               NOT rec.right_face = ANY ( invalid_faces ) )
+                       THEN
+                               invalid_faces := array_append(invalid_faces, rec.right_face);
+                       END IF;
+
+      CONTINUE;
+
+               END IF;
+
+               IF NOT ST_IsSimple(rec.geom) THEN
+                 retrec.error = 'edge not simple';
+                 retrec.id1 = rec.id1;
+                 retrec.id2 = NULL;
+                 RETURN NEXT retrec;
+    END IF;
+
        END LOOP;
 
        -- Check for edge crossing
-       FOR rec IN EXECUTE 'SELECT e1.edge_id as id1, e2.edge_id as id2, '
+       sql := 'SELECT e1.edge_id as id1, e2.edge_id as id2, '
                || ' e1.geom as g1, e2.geom as g2, '
                || 'ST_Relate(e1.geom, e2.geom) as im FROM '
                || quote_ident(toponame) || '.edge e1, '
                || quote_ident(toponame) || '.edge e2 '
                || 'WHERE e1.edge_id < e2.edge_id '
-               || 'AND e1.geom && e2.geom '
+               || ' AND e1.geom && e2.geom ';
+       IF invalid_edges IS NOT NULL THEN
+               sql := sql || ' AND NOT e1.edge_id = ANY ('
+                          || quote_literal(invalid_edges) || ')'
+                          || ' AND NOT e2.edge_id = ANY ('
+                          || quote_literal(invalid_edges) || ')';
+       END IF;
+
+       FOR rec IN EXECUTE sql
        LOOP
          IF ST_RelateMatch(rec.im, 'FF1F**1*2') THEN
            CONTINUE; -- no interior intersection
@@ -1423,6 +1461,7 @@ BEGIN
        END LOOP;
 
        -- Check for edge start_node geometry mis-match
+       -- TODO: move this in the first edge table scan 
        FOR rec IN EXECUTE 'SELECT e.edge_id as id1, n.node_id as id2 FROM '
                || quote_ident(toponame) || '.edge e, '
                || quote_ident(toponame) || '.node n '
@@ -1436,6 +1475,7 @@ BEGIN
        END LOOP;
 
        -- Check for edge end_node geometry mis-match
+       -- TODO: move this in the first edge table scan 
        FOR rec IN EXECUTE 'SELECT e.edge_id as id1, n.node_id as id2 FROM '
                || quote_ident(toponame) || '.edge e, '
                || quote_ident(toponame) || '.node n '
@@ -1466,10 +1506,15 @@ BEGIN
        -- Now create a temporary table to construct all face geometries
        -- for checking their consistency
 
-       EXECUTE 'CREATE TEMP TABLE face_check ON COMMIT DROP AS '
+       sql := 'CREATE TEMP TABLE face_check ON COMMIT DROP AS '
          || 'SELECT face_id, topology.ST_GetFaceGeometry('
          || quote_literal(toponame) || ', face_id) as geom, mbr FROM '
          || quote_ident(toponame) || '.face WHERE face_id > 0';
+       IF invalid_faces IS NOT NULL THEN
+               sql := sql || ' AND NOT face_id = ANY ('
+                          || quote_literal(invalid_faces) || ')';
+       END IF;
+       EXECUTE sql;
 
        -- Build a gist index on geom
        EXECUTE 'CREATE INDEX "face_check_gist" ON '