]> granicus.if.org Git - postgis/commitdiff
Speedup areal TopoJSON output routine to use edge walking
authorSandro Santilli <strk@keybit.net>
Wed, 20 Mar 2013 11:44:37 +0000 (11:44 +0000)
committerSandro Santilli <strk@keybit.net>
Wed, 20 Mar 2013 11:44:37 +0000 (11:44 +0000)
Now it takes 6% of the time to do the same thing !

Tweak tests to expect new arcs numbering and order.
Also fixes missing comma separating polygon ring arcs.

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

topology/sql/export/TopoJSON.sql.in
topology/test/regress/topojson.sql
topology/test/regress/topojson_expected

index f1cd1637e6220eaf1af12960f9d2ef9f51210b7d..fb6c87fb369cf299a84f3bf412b9f2aedad70f04 100644 (file)
@@ -14,6 +14,8 @@
 --
 -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
+/* #define POSTGIS_TOPOLOGY_DEBUG 1 */
+
 --{
 --
 -- API FUNCTION
@@ -25,7 +27,6 @@ CREATE OR REPLACE FUNCTION topology.AsTopoJSON(tg topology.TopoGeometry, edgeMap
   RETURNS text AS
 $$
 DECLARE
-  visited bool;
   toponame text;
   json text;
   sql text;
@@ -35,6 +36,10 @@ DECLARE
   side int;
   arcid int;
   arcs int[];
+  arctxt TEXT[];
+  old_search_path TEXT;
+  faces int[];
+  visited_edges int[];
 BEGIN
 
   IF tg IS NULL THEN
@@ -100,69 +105,133 @@ BEGIN
 
     json := '{ "type": "Polygon", "arcs": [';
 
-    FOR rec IN SELECT (ST_DumpRings((ST_Dump(ST_ForceRHR(
-      topology.Geometry(tg)))).geom)).geom
+    EXECUTE 'SHOW search_path' INTO old_search_path;
+    EXECUTE 'SET search_path TO ' || quote_ident(toponame) || ',' || old_search_path;
+
+    SELECT array_agg(id) as f
+    FROM ( SELECT (GetTopoGeomElements(tg))[1] as id ) as f
+    INTO faces;
+
+#ifdef POSTGIS_TOPOLOGY_DEBUG
+    RAISE DEBUG 'Faces: %', faces;
+#endif
+
+    visited_edges := ARRAY[]::int[];
+
     LOOP -- {
 
       arcs := NULL;
-      bounds = ST_Boundary(rec.geom);
 
-      sql := 'SELECT e.*, ST_Line_Locate_Point('
-        || quote_literal(bounds::text)
-        || ', ST_Line_Interpolate_Point(e.geom, 0.2)) as pos'
-        || ', ST_Line_Locate_Point('
-        || quote_literal(bounds::text)
-        || ', ST_Line_Interpolate_Point(e.geom, 0.8)) as pos2 FROM '
-        || quote_ident(toponame)
-        || '.edge e WHERE ST_Covers('
-        || quote_literal(bounds::text)
-        || ', e.geom) ORDER BY pos';
-
-      -- RAISE DEBUG 'SQL: %', sql;
-
-      FOR rec2 IN EXECUTE sql
-      LOOP
+      FOR rec in -- {
+WITH RECURSIVE
+_edges AS (
+  SELECT e.*, 
+         e.left_face = ANY ( faces ) as lf,
+         e.right_face = ANY ( faces ) as rf
+  FROM edge e
+  WHERE ( e.left_face = ANY ( faces ) OR
+          e.right_face = ANY ( faces ) )
+),
+_leftmost_non_dangling_edge AS (
+  SELECT * FROM _edges e
+    WHERE ( e.lf or e.rf ) AND ( e.lf != e.rf )
+          AND NOT e.edge_id = ANY ( visited_edges )
+      -- TODO: and not in visited ?
+  ORDER BY geom LIMIT 1
+),
+_edgepath AS (
+  SELECT
+    CASE
+      WHEN e.lf THEN lme.edge_id
+      ELSE -lme.edge_id
+    END as signed_edge_id,
+    false as back,
+
+    e.lf = e.rf as dangling,
+    e.lf, e.rf,
+    e.next_right_edge, e.next_left_edge
+
+  FROM _edges e, _leftmost_non_dangling_edge lme
+  WHERE e.edge_id = abs(lme.edge_id)
+    UNION
+  SELECT
+    CASE
+      WHEN p.dangling AND NOT p.back THEN -p.signed_edge_id
+      WHEN p.signed_edge_id < 0 THEN p.next_right_edge
+      ELSE p.next_left_edge
+    END, -- signed_edge_id
+    CASE
+      WHEN p.dangling AND NOT p.back THEN true
+      ELSE false
+    END, -- back
+
+    e.lf = e.rf, -- dangling
+    e.lf, e.rf,
+    e.next_right_edge, e.next_left_edge
+
+  FROM _edges e, _edgepath p
+  WHERE
+    e.edge_id = CASE
+      WHEN p.dangling AND NOT p.back THEN abs(p.signed_edge_id)
+      WHEN p.signed_edge_id < 0 THEN abs(p.next_right_edge)
+      ELSE abs(p.next_left_edge)
+    END
+)
+SELECT abs(signed_edge_id) as edge_id, signed_edge_id FROM _edgepath WHERE NOT dangling
+      LOOP  -- }{
+
+#ifdef POSTGIS_TOPOLOGY_DEBUG
+        RAISE DEBUG 'Edge id: %' , rec.signed_edge_id;
+#endif
 
         IF edgeMapTable IS NOT NULL THEN
-          sql := 'SELECT arc_id-1 FROM ' || edgeMapTable::text || ' WHERE edge_id = ' || rec2.edge_id;
+          sql := 'SELECT arc_id-1 FROM ' || edgeMapTable::text || ' WHERE edge_id = ' || rec.edge_id;
           EXECUTE sql INTO arcid;
           IF arcid IS NULL THEN
             EXECUTE 'INSERT INTO ' || edgeMapTable::text
-              || '(edge_id) VALUES (' || rec2.edge_id || ') RETURNING arc_id-1'
+              || '(edge_id) VALUES (' || rec.edge_id || ') RETURNING arc_id-1'
             INTO arcid;
           END IF;
         ELSE
-          arcid := rec2.edge_id;
+          arcid := rec.edge_id-1;
         END IF;
 
-        -- RAISE DEBUG 'Arc id: %' , arcid;
-
-        -- edge goes in same direction
-        IF rec2.pos2 < rec2.pos THEN
-          arcid := -(arcid+1);
+        -- Swap sign, use two's complement for negative edges
+        IF rec.signed_edge_id >= 0 THEN
+          arcid := - ( arcid + 1 );
         END IF;
 
-        arcs := arcs || arcid;
+#ifdef POSTGIS_TOPOLOGY_DEBUG
+        RAISE DEBUG 'ARC id: %' , arcid;
+#endif
 
-      END LOOP;
+        visited_edges := visited_edges || rec.edge_id;
+        arcs := arcid || arcs;
 
-      --RAISE DEBUG 'Ring arcs: %' , arcs;
+      END LOOP; -- }
+
+#ifdef POSTGIS_TOPOLOGY_DEBUG
+      RAISE DEBUG 'ARCS: %' , arcs;
+#endif
 
-      json := json || '[' || array_to_string(arcs,',') || ']';
+      IF arcs IS NULL THEN
+        EXIT; -- end of loop
+      END IF;
 
-      --RAISE DEBUG 'JSON : %' , json;
+      arctxt := arctxt || ( '[' || array_to_string(arcs,',') || ']' );
 
     END LOOP; -- }
 
-    json := json || ']}';
-    RETURN json;
+    json := json || array_to_string(arctxt, ',') || ']}';
+
+    EXECUTE 'SET search_path TO ' || old_search_path;
+
 
   ELSIF tg.type = 4 THEN -- collection
     RAISE EXCEPTION 'Collection TopoGeometries are not supported by AsTopoJSON';
 
   END IF;
        
-
   RETURN json;
        
 END
index 4c50596c94a690e2a2e56fd925c47ebc05659d00..e723aa779c974232b918c03ea7a46045eafde8a1 100644 (file)
@@ -5,25 +5,25 @@ set client_min_messages to WARNING;
 \i hierarchy.sql
 
 --- Lineal non-hierarchical 
-SELECT feature_name||'-vanilla', topology.AsTopoJSON(feature, NULL)
+SELECT 'L1-vanilla', feature_name, topology.AsTopoJSON(feature, NULL)
  FROM features.city_streets
  WHERE feature_name IN ('R3', 'R4', 'R1', 'R2' )
  ORDER BY feature_name;
 
 --- Lineal hierarchical 
-SELECT feature_name||'-vanilla', topology.AsTopoJSON(feature, NULL)
+SELECT 'L2-vanilla', feature_name, topology.AsTopoJSON(feature, NULL)
  FROM features.big_streets
  WHERE feature_name IN ('R4', 'R1R2' )
  ORDER BY feature_name;
 
 --- Areal non-hierarchical
-SELECT feature_name||'-vanilla', topology.AsTopoJSON(feature, NULL)
+SELECT 'A1-vanilla', feature_name, topology.AsTopoJSON(feature, NULL)
  FROM features.land_parcels
  WHERE feature_name IN ('P1', 'P2', 'P3', 'P4', 'P5' )
  ORDER BY feature_name;
 
 --- Areal hierarchical
-SELECT feature_name||'-vanilla', topology.AsTopoJSON(feature)
+SELECT 'A2-vanilla', feature_name, topology.AsTopoJSON(feature, NULL)
  FROM features.big_parcels
  WHERE feature_name IN ('P1P2', 'P3P4')
  ORDER BY feature_name;
@@ -32,25 +32,28 @@ SELECT feature_name||'-vanilla', topology.AsTopoJSON(feature)
 CREATE TEMP TABLE edgemap (arc_id serial, edge_id int unique);
 
 --- Lineal non-hierarchical 
-SELECT feature_name||'-edgemap', topology.AsTopoJSON(feature, 'edgemap')
+SELECT 'L1-edgemap', feature_name, topology.AsTopoJSON(feature, 'edgemap')
  FROM features.city_streets
  WHERE feature_name IN ('R3', 'R4', 'R1', 'R2' )
  ORDER BY feature_name;
 
 --- Lineal hierarchical 
-SELECT feature_name||'-edgemap', topology.AsTopoJSON(feature, 'edgemap')
+TRUNCATE edgemap; SELECT NULLIF(setval('edgemap_arc_id_seq', 1, false), 1);
+SELECT 'L2-edgemap', feature_name, topology.AsTopoJSON(feature, 'edgemap')
  FROM features.big_streets
  WHERE feature_name IN ('R4', 'R1R2' )
  ORDER BY feature_name;
 
 --- Areal non-hierarchical
-SELECT feature_name||'-edgemap', topology.AsTopoJSON(feature, 'edgemap')
+TRUNCATE edgemap; SELECT NULLIF(setval('edgemap_arc_id_seq', 1, false), 1);
+SELECT 'A1-edgemap', feature_name, topology.AsTopoJSON(feature, 'edgemap')
  FROM features.land_parcels
  WHERE feature_name IN ('P1', 'P2', 'P3', 'P4', 'P5' )
  ORDER BY feature_name;
 
 --- Areal hierarchical
-SELECT feature_name||'-edgemap', topology.AsTopoJSON(feature, 'edgemap')
+TRUNCATE edgemap; SELECT NULLIF(setval('edgemap_arc_id_seq', 1, false), 1);
+SELECT 'A2-edgemap', feature_name, topology.AsTopoJSON(feature, 'edgemap')
  FROM features.big_parcels
  WHERE feature_name IN ('P1P2', 'P3P4')
  ORDER BY feature_name;
index 2ceb66c3954dfa66e1930ed9783aec1b35605563..1037875fe9ade42a354f42a3577250ed78337f0c 100644 (file)
@@ -14,29 +14,30 @@ features.big_parcels.the_geom SRID:0 TYPE:MULTIPOLYGON DIMS:2
 5
 6
 features.big_signs.the_geom SRID:0 TYPE:MULTIPOINT DIMS:2 
-R1-vanilla|{ "type": "LineString", "arcs": [9,-11]}
-R2-vanilla|{ "type": "LineString", "arcs": [4,-6]}
-R3-vanilla|{ "type": "LineString", "arcs": [25]}
-R4-vanilla|{ "type": "LineString", "arcs": [3]}
-R1R2-vanilla|{ "type": "LineString", "arcs": [9,-11,4,-6]}
-R4-vanilla|{ "type": "LineString", "arcs": [3]}
-P1-vanilla|{ "type": "Polygon", "arcs": [[-21,-13,22,21,6,-20]]}
-P2-vanilla|{ "type": "Polygon", "arcs": [[-19,-14,20,19,7,-18]]}
-P3-vanilla|{ "type": "Polygon", "arcs": [[-17,-15,18,17,8,-16]]}
-P4-vanilla|{ "type": "Polygon", "arcs": [[-3]]}
-P5-vanilla|{ "type": "Polygon", "arcs": [[-2][26]]}
-ERROR:  function topology.astopojson(topogeometry) does not exist at character 34
-R1-edgemap|{ "type": "LineString", "arcs": [0,-2]}
-R2-edgemap|{ "type": "LineString", "arcs": [2,-4]}
-R3-edgemap|{ "type": "LineString", "arcs": [4]}
-R4-edgemap|{ "type": "LineString", "arcs": [5]}
-R1R2-edgemap|{ "type": "LineString", "arcs": [0,-2,2,-4]}
-R4-edgemap|{ "type": "LineString", "arcs": [5]}
-P1-edgemap|{ "type": "Polygon", "arcs": [[-7,-8,8,9,10,-12]]}
-P2-edgemap|{ "type": "Polygon", "arcs": [[-13,-14,6,11,14,-16]]}
-P3-edgemap|{ "type": "Polygon", "arcs": [[-17,-18,12,15,18,-20]]}
-P4-edgemap|{ "type": "Polygon", "arcs": [[-21]]}
-P5-edgemap|{ "type": "Polygon", "arcs": [[-22][22]]}
-P1P2-edgemap|{ "type": "Polygon", "arcs": [[-8,8,9,10,14,-16,-13,-14]]}
-P3P4-edgemap|{ "type": "Polygon", "arcs": [[-17,-18,12,15,18,-20][-21]]}
+L1-vanilla|R1|{ "type": "LineString", "arcs": [9,-11]}
+L1-vanilla|R2|{ "type": "LineString", "arcs": [4,-6]}
+L1-vanilla|R3|{ "type": "LineString", "arcs": [25]}
+L1-vanilla|R4|{ "type": "LineString", "arcs": [3]}
+L2-vanilla|R1R2|{ "type": "LineString", "arcs": [9,-11,4,-6]}
+L2-vanilla|R4|{ "type": "LineString", "arcs": [3]}
+A1-vanilla|P1|{ "type": "Polygon", "arcs": [[20,5,-19,-20,-12,21]]}
+A1-vanilla|P2|{ "type": "Polygon", "arcs": [[18,6,-17,-18,-13,19]]}
+A1-vanilla|P3|{ "type": "Polygon", "arcs": [[16,7,-15,-16,-14,17]]}
+A1-vanilla|P4|{ "type": "Polygon", "arcs": [[-2]]}
+A1-vanilla|P5|{ "type": "Polygon", "arcs": [[-1],[25]]}
+A2-vanilla|P1P2|{ "type": "Polygon", "arcs": [[20,5,6,-17,-18,-13,-12,21]]}
+A2-vanilla|P3P4|{ "type": "Polygon", "arcs": [[-2],[16,7,-15,-16,-14,17]]}
+L1-edgemap|R1|{ "type": "LineString", "arcs": [0,-2]}
+L1-edgemap|R2|{ "type": "LineString", "arcs": [2,-4]}
+L1-edgemap|R3|{ "type": "LineString", "arcs": [4]}
+L1-edgemap|R4|{ "type": "LineString", "arcs": [5]}
+L2-edgemap|R1R2|{ "type": "LineString", "arcs": [0,-2,2,-4]}
+L2-edgemap|R4|{ "type": "LineString", "arcs": [4]}
+A1-edgemap|P1|{ "type": "Polygon", "arcs": [[5,4,-4,-3,-2,0]]}
+A1-edgemap|P2|{ "type": "Polygon", "arcs": [[3,9,-9,-8,-7,2]]}
+A1-edgemap|P3|{ "type": "Polygon", "arcs": [[8,13,-13,-12,-11,7]]}
+A1-edgemap|P4|{ "type": "Polygon", "arcs": [[-15]]}
+A1-edgemap|P5|{ "type": "Polygon", "arcs": [[-16],[16]]}
+A2-edgemap|P1P2|{ "type": "Polygon", "arcs": [[7,6,5,-5,-4,-3,-2,0]]}
+A2-edgemap|P3P4|{ "type": "Polygon", "arcs": [[-9],[4,12,-12,-11,-10,3]]}
 Topology 'city_data' dropped