]> granicus.if.org Git - postgis/commitdiff
Performance improvements in topology.AddFace: consider each ring separately when...
authorSandro Santilli <strk@keybit.net>
Fri, 18 Feb 2011 16:24:39 +0000 (16:24 +0000)
committerSandro Santilli <strk@keybit.net>
Fri, 18 Feb 2011 16:24:39 +0000 (16:24 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@6846 b70326c6-7e19-0410-871a-916f4a2858ee

topology/sql/populate.sql

index e438053839f035e6a7741c76e66881b8c90812ad..4cea30bdc82c60164e30e6c4f241c639e54deacb 100644 (file)
@@ -390,105 +390,114 @@ LANGUAGE 'plpgsql' VOLATILE;
 --  o The polygon overlaps an existing face.
 --
 -- 
-CREATE OR REPLACE FUNCTION topology.AddFace(varchar, geometry)
+CREATE OR REPLACE FUNCTION topology.AddFace(atopology varchar, apoly geometry)
        RETURNS int
 AS
 $$
 DECLARE
-       atopology ALIAS FOR $1;
-       apoly ALIAS FOR $2;
-       bounds geometry;
-       symdif geometry;
-       faceid int;
-       rec RECORD;
-       relate text;
-       right_edges int[];
-       left_edges int[];
-       all_edges geometry;
-       old_faceid int;
-       old_edgeid int;
+  bounds geometry;
+  symdif geometry;
+  faceid int;
+  rec RECORD;
+  rrec RECORD;
+  relate text;
+  right_edges int[];
+  left_edges int[];
+  all_edges geometry;
+  old_faceid int;
+  old_edgeid int;
+  sql text;
+  side int;
 BEGIN
-       --
-       -- Atopology and apoly are required
-       -- 
-       IF atopology IS NULL OR apoly IS NULL THEN
-               RAISE EXCEPTION 'Invalid null argument';
-       END IF;
-
-       --
-       -- Aline must be a polygon
-       --
-       IF substring(geometrytype(apoly), 1, 4) != 'POLY'
-       THEN
-               RAISE EXCEPTION 'Face geometry must be a polygon';
-       END IF;
-
-       --
-       -- Find all bounds edges, forcing right-hand-rule
-       -- to know what's left and what's right...
-       --
-       bounds = ST_Boundary(ST_ForceRHR(apoly));
-
-       FOR rec IN EXECUTE 'SELECT geom, edge_id, '
-               || 'left_face, right_face, '
-               || '(st_dump(st_sharedpaths(geom, '
-               || quote_literal(bounds::text)
-               || '))).path[1] as side FROM '
-               || quote_ident(atopology) || '.edge '
-               || 'WHERE '
-               || quote_literal(bounds::text)
-               || '::geometry && geom AND ST_Relate(geom, '
-               || quote_literal(bounds::text)
-               || ', ''1FF******'')'
-               || ' GROUP BY geom, edge_id, left_face, right_face, side'
-       LOOP
-               RAISE DEBUG 'Edge % (left:%, right:%) - side : %',
-                       rec.edge_id, rec.left_face, rec.right_face, rec.side;
-
-               IF rec.side = 1 THEN
-                       -- This face is on the right
-                       right_edges := array_append(right_edges, rec.edge_id);
-                       old_faceid = rec.right_face;
-               ELSE
-                       -- This face is on the left
-                       left_edges := array_append(left_edges, rec.edge_id);
-                       old_faceid = rec.left_face;
-               END IF;
-
-               IF faceid IS NULL OR faceid = 0 THEN
-                       faceid = old_faceid;
-                       old_edgeid = rec.edge_id;
-               ELSIF faceid != old_faceid THEN
-                       RAISE EXCEPTION 'Edge % has face % registered on the side of this face, while edge % has face % on the same side', rec.edge_id, old_faceid, old_edgeid, faceid;
-               END IF;
-
-               -- Collect all edges for final full coverage check
-               all_edges = ST_Collect(all_edges, rec.geom);
-
-       END LOOP;
-
-       IF all_edges IS NULL THEN
-               RAISE EXCEPTION 'Found no edges on the polygon boundary';
-       END IF;
-
-       RAISE DEBUG 'Left edges: %', left_edges;
-       RAISE DEBUG 'Right edges: %', right_edges;
-
-       --
-       -- Check that all edges found, taken togheter,
-       -- fully match the polygon boundary and nothing more
-       --
-       -- If the test fail either we need to add more edges
-       -- from the polygon boundary or we need to split
-       -- some of the existing ones.
-       -- 
-       IF NOT ST_isEmpty(ST_SymDifference(bounds, all_edges)) THEN
-         IF NOT ST_isEmpty(ST_Difference(bounds, all_edges)) THEN
-           RAISE EXCEPTION 'Polygon boundary is not fully defined by existing edges at or near point %', ST_AsText(ST_PointOnSurface(ST_Difference(bounds, all_edges)));
-         ELSE
-           RAISE EXCEPTION 'Existing edges cover polygon boundary and more at or near point % (invalid topology?)', ST_AsText(ST_PointOnSurface(ST_Difference(all_edges, bounds)));
-         END IF;
-       END IF;
+  --
+  -- Atopology and apoly are required
+  -- 
+  IF atopology IS NULL OR apoly IS NULL THEN
+    RAISE EXCEPTION 'Invalid null argument';
+  END IF;
+
+  --
+  -- Aline must be a polygon
+  --
+  IF substring(geometrytype(apoly), 1, 4) != 'POLY'
+  THEN
+    RAISE EXCEPTION 'Face geometry must be a polygon';
+  END IF;
+
+  for rrec IN SELECT (ST_DumpRings(ST_ForceRHR(apoly))).*
+  LOOP -- {
+    --
+    -- Find all bounds edges, forcing right-hand-rule
+    -- to know what's left and what's right...
+    --
+    bounds = ST_Boundary(rrec.geom);
+
+    sql := 'SELECT e.geom, e.edge_id, '
+      || 'e.left_face, e.right_face FROM '
+      || quote_ident(atopology) || '.edge e, (SELECT '
+      || quote_literal(bounds::text)
+      || '::geometry as geom) r WHERE '
+      || 'r.geom && e.geom AND ST_Relate(e.geom, r.geom'
+      || ', ''1FF******'')'
+    ;
+    -- RAISE DEBUG 'SQL: %', sql;
+    FOR rec IN EXECUTE sql
+    LOOP -- {
+
+      -- Find the side of the edge on the face
+      -- TODO: can probably be optimized further
+      SELECT DISTINCT (st_dump(st_sharedpaths(rec.geom, bounds))).path[1]
+        INTO STRICT side;
+
+      RAISE DEBUG 'Edge % (left:%, right:%) - ring : % - side : %',
+        rec.edge_id, rec.left_face, rec.right_face, rrec.path, side;
+
+      IF side = 1 THEN
+        -- This face is on the right
+        right_edges := array_append(right_edges, rec.edge_id);
+        old_faceid = rec.right_face;
+      ELSE
+        -- This face is on the left
+        left_edges := array_append(left_edges, rec.edge_id);
+        old_faceid = rec.left_face;
+      END IF;
+
+      IF faceid IS NULL OR faceid = 0 THEN
+        faceid = old_faceid;
+        old_edgeid = rec.edge_id;
+      ELSIF faceid != old_faceid THEN
+        RAISE EXCEPTION 'Edge % has face % registered on the side of this face, while edge % has face % on the same side', rec.edge_id, old_faceid, old_edgeid, faceid;
+      END IF;
+
+      -- Collect all edges for final full coverage check
+      all_edges = ST_Collect(all_edges, rec.geom);
+
+    END LOOP; -- }
+  END LOOP; -- }
+
+  IF all_edges IS NULL THEN
+    RAISE EXCEPTION 'Found no edges on the polygon boundary';
+  END IF;
+
+  RAISE DEBUG 'Left edges: %', left_edges;
+  RAISE DEBUG 'Right edges: %', right_edges;
+
+  --
+  -- Check that all edges found, taken togheter,
+  -- fully match the ring boundary and nothing more
+  --
+  -- If the test fail either we need to add more edges
+  -- from the polygon ring or we need to split
+  -- some of the existing ones.
+  -- 
+  bounds = ST_Boundary(apoly);
+  IF NOT ST_isEmpty(ST_SymDifference(bounds, all_edges)) THEN
+    IF NOT ST_isEmpty(ST_Difference(bounds, all_edges)) THEN
+      RAISE EXCEPTION 'Polygon boundary is not fully defined by existing edges at or near point %', ST_AsText(ST_PointOnSurface(ST_Difference(bounds, all_edges)));
+    ELSE
+      RAISE EXCEPTION 'Existing edges cover polygon boundary and more at or near point % (invalid topology?)', ST_AsText(ST_PointOnSurface(ST_Difference(all_edges, bounds)));
+    END IF;
+  END IF;
 
 --     EXECUTE 'SELECT ST_Collect(geom) FROM'
 --             || quote_ident(atopology)
@@ -497,77 +506,76 @@ BEGIN
 --             || quote_literal(array_append(left_edges, right_edges))
 --             || ') ';
 
-       --
-       -- TODO:
-       -- Check that NO edge is contained in the face ?
-       --
-       RAISE WARNING 'Not checking if face contains any edge';
-
-       IF faceid IS NOT NULL AND faceid != 0 THEN
-               RAISE DEBUG 'Face already known as %', faceid;
-               RETURN faceid;
-       END IF;
-
-       --
-       -- Get new face id from sequence
-       --
-       FOR rec IN EXECUTE 'SELECT nextval(' ||
-               quote_literal(
-                       quote_ident(atopology) || '.face_face_id_seq'
-               ) || ')'
-       LOOP
-               faceid = rec.nextval;
-       END LOOP;
-
-       --
-       -- Insert new face 
-       --
-       EXECUTE 'INSERT INTO '
-               || quote_ident(atopology)
-               || '.face(face_id, mbr) VALUES('
-               -- face_id
-               || faceid || ','
-               -- minimum bounding rectangle
-               || quote_literal(ST_Envelope(apoly)::text)
-               || ')';
-
-       --
-       -- Update all edges having this face on the left
-       --
-       IF left_edges IS NOT NULL THEN
-               EXECUTE 'UPDATE '
-                       || quote_ident(atopology)
-                       || '.edge_data SET left_face = '
-                       || quote_literal(faceid)
-                       || ' WHERE edge_id = ANY('
-                       || quote_literal(left_edges)
-                       || ') ';
-       END IF;
-
-       --
-       -- Update all edges having this face on the right
-       --
-       IF right_edges IS NOT NULL THEN
-               EXECUTE 'UPDATE '
-                       || quote_ident(atopology)
-                       || '.edge_data SET right_face = '
-                       || quote_literal(faceid)
-                       || ' WHERE edge_id = ANY('
-                       || quote_literal(right_edges)
-                       || ') ';
-       END IF;
-
-       --
-       -- TODO:
-       -- Set next_left_face and next_right_face !
-       -- These are required by the model, but not really used
-       -- by this implementation...
-       --
-       RAISE WARNING 'Not updating next_{left,right}_face fields of face boundary edges';
-
+  --
+  -- TODO:
+  -- Check that NO edge is contained in the face ?
+  --
+  RAISE WARNING 'Not checking if face contains any edge';
+
+  IF faceid IS NOT NULL AND faceid != 0 THEN
+    RAISE DEBUG 'Face already known as %', faceid;
+    RETURN faceid;
+  END IF;
+
+  --
+  -- Get new face id from sequence
+  --
+  FOR rec IN EXECUTE 'SELECT nextval(' ||
+    quote_literal(
+      quote_ident(atopology) || '.face_face_id_seq'
+    ) || ')'
+  LOOP
+    faceid = rec.nextval;
+  END LOOP;
 
-       RETURN faceid;
+  --
+  -- Insert new face 
+  --
+  EXECUTE 'INSERT INTO '
+    || quote_ident(atopology)
+    || '.face(face_id, mbr) VALUES('
+    -- face_id
+    || faceid || ','
+    -- minimum bounding rectangle
+    || quote_literal(ST_Envelope(apoly)::text)
+    || ')';
+
+  --
+  -- Update all edges having this face on the left
+  --
+  IF left_edges IS NOT NULL THEN
+    EXECUTE 'UPDATE '
+    || quote_ident(atopology)
+    || '.edge_data SET left_face = '
+    || quote_literal(faceid)
+    || ' WHERE edge_id = ANY('
+    || quote_literal(left_edges)
+    || ') ';
+  END IF;
+
+  --
+  -- Update all edges having this face on the right
+  --
+  IF right_edges IS NOT NULL THEN
+    EXECUTE 'UPDATE '
+    || quote_ident(atopology)
+    || '.edge_data SET right_face = '
+    || quote_literal(faceid)
+    || ' WHERE edge_id = ANY('
+    || quote_literal(right_edges)
+    || ') ';
+  END IF;
+
+  --
+  -- TODO:
+  -- Set next_left_face and next_right_face !
+  -- These are required by the model, but not really used
+  -- by this implementation...
+  --
+  RAISE WARNING 'Not updating next_{left,right}_face fields of face boundary edges';
+
+
+  RETURN faceid;
        
 END
 $$