--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+/* #define POSTGIS_TOPOLOGY_DEBUG 1 */
+
--{
--
-- API FUNCTION
RETURNS text AS
$$
DECLARE
- visited bool;
toponame text;
json text;
sql text;
side int;
arcid int;
arcs int[];
+ arctxt TEXT[];
+ old_search_path TEXT;
+ faces int[];
+ visited_edges int[];
BEGIN
IF tg IS NULL THEN
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
\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;
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;
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