From: Sandro Santilli Date: Thu, 21 Mar 2013 16:15:51 +0000 (+0000) Subject: Fix areal TopoJSON output to group and order polygon rings (#2228) X-Git-Tag: 2.1.0beta2~155 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=59eba85d499b8b56f1f6ff4a4beb38c16e1047fa;p=postgis Fix areal TopoJSON output to group and order polygon rings (#2228) git-svn-id: http://svn.osgeo.org/postgis/trunk@11193 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/doc/extras_topology.xml b/doc/extras_topology.xml index ba1ef75bc..d09f38cdb 100644 --- a/doc/extras_topology.xml +++ b/doc/extras_topology.xml @@ -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]] ]} diff --git a/topology/sql/export/TopoJSON.sql.in b/topology/sql/export/TopoJSON.sql.in index 4f25f5e6b..af367c1b1 100644 --- a/topology/sql/export/TopoJSON.sql.in +++ b/topology/sql/export/TopoJSON.sql.in @@ -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; diff --git a/topology/test/regress/topojson.sql b/topology/test/regress/topojson.sql index f258d790c..82d7675b1 100644 --- a/topology/test/regress/topojson.sql +++ b/topology/test/regress/topojson.sql @@ -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; diff --git a/topology/test/regress/topojson_expected b/topology/test/regress/topojson_expected index 86695250b..53083f811 100644 --- a/topology/test/regress/topojson_expected +++ b/topology/test/regress/topojson_expected @@ -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