]> granicus.if.org Git - postgis/commitdiff
Fix areal TopoJSON output to group and order polygon rings (#2228)
authorSandro Santilli <strk@keybit.net>
Thu, 21 Mar 2013 16:15:51 +0000 (16:15 +0000)
committerSandro Santilli <strk@keybit.net>
Thu, 21 Mar 2013 16:15:51 +0000 (16:15 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@11193 b70326c6-7e19-0410-871a-916f4a2858ee

doc/extras_topology.xml
topology/sql/export/TopoJSON.sql.in
topology/test/regress/topojson.sql
topology/test/regress/topojson_expected

index ba1ef75bc2afb22c3d54ce5f7f136f6192a00591..d09f38cdb6b029e6c909612b56ea712f2c8d315b 100644 (file)
@@ -3179,7 +3179,7 @@ SELECT '{ "type": "Topology", "transform": { "scale": [1,1], "translate": [0,0]
 
 -- objects
 SELECT '"' || feature_name || '": ' || AsTopoJSON(feature, 'edgemap')
-FROM features.land_parcels;
+FROM features.big_parcels WHERE feature_name = 'P3P4';
 
 -- arcs
 SELECT '}, "arcs": ['
@@ -3192,35 +3192,15 @@ SELECT ']}'::text as t
 
 -- Result:
 { "type": "Topology", "transform": { "scale": [1,1], "translate": [0,0] }, "objects": {
-"P1": { "type": "Polygon", "arcs": [[5,4,-4,-3,-2,0]]}
-"P2": { "type": "Polygon", "arcs": [[3,9,-9,-8,-7,2]]}
-"P3": { "type": "Polygon", "arcs": [[8,13,-13,-12,-11,7]]}
-"P4": { "type": "Polygon", "arcs": [[-15]]}
-"P5": { "type": "Polygon", "arcs": [[-16],[16]]}
-"F3": { "type": "Polygon", "arcs": [[4,-4,-18,5]]}
-"F6": { "type": "Polygon", "arcs": [[17,-3,-2,0]]}
-"F3F4": { "type": "Polygon", "arcs": [[4,9,-9,18,-18,5]]}
-"F1": { "type": "Polygon", "arcs": [[-16],[16]]}
+"P3P4": { "type": "MultiPolygon", "arcs": [[[-1]],[[6,5,-5,-4,-3,1]]]}
 }, "arcs": [
-[[9,6],[9,14]]
-[[9,6],[21,6]]
-[[21,6],[21,14]]
-[[21,14],[21,22]]
-[[9,22],[21,22]]
-[[9,14],[9,22]]
-[[21,6],[35,6]]
+[[25,30],[31,30],[31,40],[17,40],[17,30],[25,30]]
 [[35,6],[35,14]]
-[[35,14],[35,22]]
-[[21,22],[35,22]]
 [[35,6],[47,6]]
 [[47,6],[47,14]]
 [[47,14],[47,22]]
 [[35,22],[47,22]]
-[[25,30],[31,30],[31,40],[17,40],[17,30],[25,30]]
-[[8,30],[16,30],[16,38],[3,38],[3,30],[8,30]]
-[[4,31],[7,31],[7,34],[4,34],[4,31]]
-[[9,14],[21,14]]
-[[35,14],[21,14]]
+[[35,14],[35,22]]
 ]}
 </programlisting>
             </refsection>
index 4f25f5e6bf9b6aca787bd6dd75e7a201246670cc..af367c1b1ea03aeb16e76444b1a1d99bed3d501a 100644 (file)
@@ -36,10 +36,16 @@ DECLARE
   side int;
   arcid int;
   arcs int[];
-  arctxt TEXT[];
+  ringtxt TEXT[];
+  polytxt TEXT[];
+  edges_found BOOLEAN;
   old_search_path TEXT;
+  all_faces int[];
   faces int[];
+  visited_face int;
+  shell_faces int[];
   visited_edges int[];
+  looking_for_holes BOOLEAN;
 BEGIN
 
   IF tg IS NULL THEN
@@ -103,24 +109,33 @@ BEGIN
 
   ELSIF tg.type = 3 THEN -- areal
 
-    json := '{ "type": "Polygon", "arcs": [';
+    json := '{ "type": "MultiPolygon", "arcs": [';
 
     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;
+    INTO all_faces;
 
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-    RAISE DEBUG 'Faces: %', faces;
+    RAISE DEBUG 'Faces: %', all_faces;
 #endif
 
     visited_edges := ARRAY[]::int[];
+    faces := all_faces;
+    looking_for_holes := false;
+    shell_faces := ARRAY[]::int[];
 
     LOOP -- {
 
       arcs := NULL;
+      edges_found := false;
+
+#ifdef POSTGIS_TOPOLOGY_DEBUG
+      RAISE DEBUG 'LOOP START - looking for next % binding faces %',
+        CASE WHEN looking_for_holes THEN 'hole' ELSE 'shell' END, faces;
+#endif
 
       FOR rec in -- {
 WITH RECURSIVE
@@ -148,6 +163,7 @@ _edgepath AS (
     false as back,
 
     e.lf = e.rf as dangling,
+    e.left_face, e.right_face,
     e.lf, e.rf,
     e.next_right_edge, e.next_left_edge
 
@@ -166,6 +182,7 @@ _edgepath AS (
     END, -- back
 
     e.lf = e.rf, -- dangling
+    e.left_face, e.right_face,
     e.lf, e.rf,
     e.next_right_edge, e.next_left_edge
 
@@ -177,13 +194,37 @@ _edgepath AS (
       ELSE abs(p.next_left_edge)
     END
 )
-SELECT abs(signed_edge_id) as edge_id, signed_edge_id FROM _edgepath WHERE NOT dangling
+SELECT abs(signed_edge_id) as edge_id, signed_edge_id, dangling,
+        lf, rf, left_face, right_face
+FROM _edgepath
       LOOP  -- }{
 
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-        RAISE DEBUG 'Edge id: %' , rec.signed_edge_id;
+        RAISE DEBUG ' edge % lf:%(%) rf:%(%)' , rec.signed_edge_id, rec.lf, rec.left_face, rec.rf, rec.right_face;
 #endif
 
+
+        IF rec.left_face = ANY (all_faces) AND NOT rec.left_face = ANY (shell_faces) THEN
+          shell_faces := shell_faces || rec.left_face;
+        END IF;
+
+        IF rec.right_face = ANY (all_faces) AND NOT rec.right_face = ANY (shell_faces) THEN
+          shell_faces := shell_faces || rec.right_face;
+        END IF;
+
+        visited_edges := visited_edges || rec.edge_id;
+
+        edges_found := true;
+
+        -- TODO: drop ?
+        IF rec.dangling THEN
+          CONTINUE;
+        END IF;
+
+        IF rec.left_face = ANY (all_faces) AND rec.right_face = ANY (all_faces) THEN
+          CONTINUE;
+        END IF;
+
         IF edgeMapTable IS NOT NULL THEN
           sql := 'SELECT arc_id-1 FROM ' || edgeMapTable::text || ' WHERE edge_id = ' || rec.edge_id;
           EXECUTE sql INTO arcid;
@@ -205,24 +246,41 @@ SELECT abs(signed_edge_id) as edge_id, signed_edge_id FROM _edgepath WHERE NOT d
         RAISE DEBUG 'ARC id: %' , arcid;
 #endif
 
-        visited_edges := visited_edges || rec.edge_id;
         arcs := arcid || arcs;
 
       END LOOP; -- }
 
 #ifdef POSTGIS_TOPOLOGY_DEBUG
-      RAISE DEBUG 'ARCS: %' , arcs;
+      --RAISE DEBUG 'Edges found:%, visited faces: %, ARCS: %' , edges_found, shell_faces, arcs;
 #endif
 
-      IF arcs IS NULL THEN
-        EXIT; -- end of loop
+      IF NOT edges_found THEN
+        IF looking_for_holes THEN
+          looking_for_holes := false;
+#ifdef POSTGIS_TOPOLOGY_DEBUG
+          RAISE DEBUG 'NO MORE holes, rings:%', ringtxt;
+#endif
+          polytxt := polytxt || ( '[' || array_to_string(ringtxt, ',') || ']' );
+          ringtxt := NULL;
+          faces := all_faces;
+          shell_faces := ARRAY[]::int[];
+        ELSE
+          EXIT; -- end of loop
+        END IF;
+      ELSE
+        faces := shell_faces;
+        IF arcs IS NOT NULL THEN
+#ifdef POSTGIS_TOPOLOGY_DEBUG
+          RAISE DEBUG ' % arcs: %', CASE WHEN looking_for_holes THEN 'hole' ELSE 'shell' END, arcs;
+#endif
+          ringtxt := ringtxt || ( '[' || array_to_string(arcs,',') || ']' );
+        END IF;
+        looking_for_holes := true;
       END IF;
 
-      arctxt := arctxt || ( '[' || array_to_string(arcs,',') || ']' );
-
     END LOOP; -- }
 
-    json := json || array_to_string(arctxt, ',') || ']}';
+    json := json || array_to_string(polytxt, ',') || ']}';
 
     EXECUTE 'SET search_path TO ' || old_search_path;
 
index f258d790c485ec3351b6533b34601573a536d7a3..82d7675b1eb8d71374f90d107f99a7ec79fcac65 100644 (file)
@@ -4,7 +4,7 @@ set client_min_messages to WARNING;
 \i load_features.sql
 \i hierarchy.sql
 
--- This edge perturbates the topology so that walking around the boundaries
+-- This edges perturbate the topology so that walking around the boundaries
 -- of P1 and P2 may require walking on some of them
 SELECT 'E' || TopoGeo_addLinestring('city_data', 'LINESTRING(9 14, 15 10)');
 SELECT 'E' || TopoGeo_addLinestring('city_data', 'LINESTRING(21 14, 15 18)');
@@ -68,6 +68,29 @@ SELECT 'A2-edgemap', feature_name, topology.AsTopoJSON(feature, 'edgemap')
 DROP TABLE edgemap;
 -- End edge mapping }
 
+-- This edge splits an hole in two faces
+SELECT 'E' || TopoGeo_addLinestring('city_data', 'LINESTRING(4 31, 7 34)');
+
+-- This edge wraps a couple of faces, to test holes at 2 level distance from parent
+SELECT 'E' || TopoGeo_addLinestring('city_data', 'LINESTRING(0 25, 33 25, 33 44, 0 44, 0 25)');
+
+-- Now add a new polygon
+SELECT 'E' || TopoGeo_addLinestring('city_data', 'LINESTRING(3 47, 33 47, 33 52, 3 52, 3 47)');
+SELECT 'E' || TopoGeo_addLinestring('city_data', 'LINESTRING(10 48, 16 48, 16 50, 10 50, 10 48)');
+
+-- And this defines a new feature including both face 1and the new 
+-- wrapping face 11 plus the new (holed) face 12
+INSERT INTO features.land_parcels VALUES ('P6', 
+  topology.CreateTopoGeom(
+    'city_data', -- Topology name
+    3, -- Topology geometry type (polygon/multipolygon)
+    1, -- TG_LAYER_ID for this topology (from topology.layer)
+    '{{1,3},{11,3},{12,3}}'));
+
+SELECT 'A3-vanilla', feature_name, topology.AsTopoJSON(feature, null)
+ FROM features.land_parcels
+ WHERE feature_name IN ('P6')
+ ORDER BY feature_name;
 
 SELECT topology.DropTopology('city_data');
 DROP SCHEMA features CASCADE;
index 86695250bfde60b4f89296ed157cbc9bb7f77043..53083f81169775d8419fd79628d4953b78fbf65a 100644 (file)
@@ -24,24 +24,29 @@ 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]]}
+A1-vanilla|P1|{ "type": "MultiPolygon", "arcs": [[[20,5,-19,-20,-12,21]]]}
+A1-vanilla|P2|{ "type": "MultiPolygon", "arcs": [[[18,6,-17,-18,-13,19]]]}
+A1-vanilla|P3|{ "type": "MultiPolygon", "arcs": [[[16,7,-15,-16,-14,17]]]}
+A1-vanilla|P4|{ "type": "MultiPolygon", "arcs": [[[-2]]]}
+A1-vanilla|P5|{ "type": "MultiPolygon", "arcs": [[[-1],[25]]]}
+A2-vanilla|P1P2|{ "type": "MultiPolygon", "arcs": [[[20,5,6,-17,-18,-13,-12,21]]]}
+A2-vanilla|P3P4|{ "type": "MultiPolygon", "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]]}
+A1-edgemap|P1|{ "type": "MultiPolygon", "arcs": [[[5,4,-4,-3,-2,0]]]}
+A1-edgemap|P2|{ "type": "MultiPolygon", "arcs": [[[3,9,-9,-8,-7,2]]]}
+A1-edgemap|P3|{ "type": "MultiPolygon", "arcs": [[[8,13,-13,-12,-11,7]]]}
+A1-edgemap|P4|{ "type": "MultiPolygon", "arcs": [[[-15]]]}
+A1-edgemap|P5|{ "type": "MultiPolygon", "arcs": [[[-16],[16]]]}
+A2-edgemap|P1P2|{ "type": "MultiPolygon", "arcs": [[[7,6,5,-5,-4,-3,-2,0]]]}
+A2-edgemap|P3P4|{ "type": "MultiPolygon", "arcs": [[[-9]],[[4,12,-12,-11,-10,3]]]}
+E32
+E33
+E34
+E35
+A3-vanilla|P6|{ "type": "MultiPolygon", "arcs": [[[-33],[30,25],[1]],[[-34],[34]]]}
 Topology 'city_data' dropped