-- objects
SELECT '"' || feature_name || '": ' || AsTopoJSON(feature, 'edgemap')
-FROM features.land_parcels;
+FROM features.big_parcels WHERE feature_name = 'P3P4';
-- arcs
SELECT '}, "arcs": ['
-- 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>
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
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
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
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
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;
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;
\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)');
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;
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