From: Sandro Santilli Date: Tue, 3 Jan 2012 23:17:42 +0000 (+0000) Subject: Implement and regress-test TopoGeo_addPolygon X-Git-Tag: 2.0.0alpha1~212 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5f605e40b6960789083aeeb4617b1bf8fd4b7c6c;p=postgis Implement and regress-test TopoGeo_addPolygon git-svn-id: http://svn.osgeo.org/postgis/trunk@8663 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/topology/sql/populate.sql b/topology/sql/populate.sql index 6d0e6c1ba..a4930165e 100644 --- a/topology/sql/populate.sql +++ b/topology/sql/populate.sql @@ -750,6 +750,7 @@ BEGIN set2 := ST_Intersection(snapped, set1); RAISE DEBUG 'Intersection: %', ST_AsText(set2); noded := ST_Union(noded, set2); + -- TODO: linemerge ? END IF; -- 3. For each (now-noded) segment, insert an edge @@ -813,14 +814,45 @@ CREATE OR REPLACE FUNCTION topology.TopoGeo_AddPolygon(atopology varchar, apoly RETURNS SETOF int AS $$ DECLARE + boundary GEOMETRY; + fgeom GEOMETRY; + rec RECORD; + edges INTEGER[]; + sql TEXT; BEGIN -- 0. Check arguments - IF geometrytype(aline) != 'POLYGON' THEN - RAISE EXCEPTION 'Invalid geometry type (%) passed to TopoGeo_AddPolygon, expected POLYGON', geometrytype(aline); + IF geometrytype(apoly) != 'POLYGON' THEN + RAISE EXCEPTION 'Invalid geometry type (%) passed to TopoGeo_AddPolygon, expected POLYGON', geometrytype(apoly); END IF; - RAISE EXCEPTION 'TopoGeo_AddPolygon not implemented yet'; + -- 1. Extract boundary + boundary := ST_Boundary(apoly); + RAISE DEBUG 'Boundary: %', ST_AsText(boundary); + + -- 2. Add boundaries as edges + FOR rec IN SELECT (ST_Dump(boundary)).geom LOOP + edges := array_cat(edges, array_agg(x)) FROM ( select topology.TopoGeo_addLinestring(atopology, rec.geom, tolerance) as x ) as foo; + RAISE DEBUG 'New edges: %', edges; + END LOOP; + + -- 3. Find faces covered by input polygon + -- NOTE: potential snapping changed polygon edges + sql := 'SELECT DISTINCT f.face_id FROM ' || quote_ident(atopology) + || '.face f WHERE f.mbr && ' + || quote_literal(apoly::text) + || '::geometry'; + RAISE DEBUG '%', sql; + FOR rec IN EXECUTE sql LOOP + -- check for actual containment + fgeom := ST_PointOnSurface(ST_GetFaceGeometry(atopology, rec.face_id)); + IF NOT ST_Covers(apoly, fgeom) THEN + RAISE DEBUG 'Face % not covered by input polygon', rec.face_id; + CONTINUE; + END IF; + RETURN NEXT rec.face_id; + END LOOP; + END $$ LANGUAGE 'plpgsql'; diff --git a/topology/test/Makefile b/topology/test/Makefile index 787d7569f..fc2867eef 100644 --- a/topology/test/Makefile +++ b/topology/test/Makefile @@ -42,8 +42,9 @@ TESTS = regress/legacy_validate.sql regress/legacy_predicate.sql \ regress/st_remedgemodface.sql \ regress/topoelement.sql \ regress/topoelementarray_agg.sql \ - regress/topogeo_addpoint.sql \ regress/topogeo_addlinestring \ + regress/topogeo_addpoint.sql \ + regress/topogeo_addpolygon.sql \ regress/topogeometry_type.sql \ regress/topo2.5d.sql \ regress/totopogeom.sql \ diff --git a/topology/test/regress/topogeo_addpolygon.sql b/topology/test/regress/topogeo_addpolygon.sql new file mode 100644 index 000000000..4e415e9a7 --- /dev/null +++ b/topology/test/regress/topogeo_addpolygon.sql @@ -0,0 +1,112 @@ +\set VERBOSITY terse +set client_min_messages to ERROR; + +\i load_topology.sql + +-- Save max node,edge and face ids +select 'node'::text as what, max(node_id) INTO city_data.limits FROM city_data.node; +INSERT INTO city_data.limits select 'edge'::text as what, max(edge_id) FROM city_data.edge; +INSERT INTO city_data.limits select 'face'::text as what, max(face_id) FROM city_data.face; +SELECT 'max',* from city_data.limits; + +-- Check changes since last saving, save more +-- { +CREATE OR REPLACE FUNCTION check_changes() +RETURNS TABLE (o text) +AS $$ +DECLARE + rec RECORD; + sql text; +BEGIN + -- Check effect on nodes + sql := 'SELECT n.node_id, ''N|'' || n.node_id || ''|'' || + COALESCE(n.containing_face::text,'''') || ''|'' || + ST_AsText(ST_SnapToGrid(n.geom, 0.2))::text as xx + FROM city_data.node n WHERE n.node_id > ( + SELECT max FROM city_data.limits WHERE what = ''node''::text ) + ORDER BY n.node_id'; + + FOR rec IN EXECUTE sql LOOP + o := rec.xx; + RETURN NEXT; + END LOOP; + + -- Check effect on edges + sql := ' + WITH node_limits AS ( SELECT max FROM city_data.limits WHERE what = ''node''::text ), + edge_limits AS ( SELECT max FROM city_data.limits WHERE what = ''edge''::text ) + SELECT ''E|'' || e.edge_id || ''|sn'' || e.start_node || ''|en'' || e.end_node :: text as xx + FROM city_data.edge e, node_limits nl, edge_limits el + WHERE e.start_node > nl.max + OR e.end_node > nl.max + OR e.edge_id > el.max + ORDER BY e.edge_id; + '; + + FOR rec IN EXECUTE sql LOOP + o := rec.xx; + RETURN NEXT; + END LOOP; + + -- Check effect on faces + sql := ' + WITH face_limits AS ( SELECT max FROM city_data.limits WHERE what = ''face''::text ) + SELECT ''F|'' || f.face_id ::text as xx + FROM city_data.face f, face_limits fl + WHERE f.face_id > fl.max + ORDER BY f.face_id; + '; + + FOR rec IN EXECUTE sql LOOP + o := rec.xx; + RETURN NEXT; + END LOOP; + + UPDATE city_data.limits SET max = (SELECT max(n.node_id) FROM city_data.node n) WHERE what = 'node'; + UPDATE city_data.limits SET max = (SELECT max(e.edge_id) FROM city_data.edge e) WHERE what = 'edge'; + UPDATE city_data.limits SET max = (SELECT max(f.face_id) FROM city_data.face f) WHERE what = 'face'; + +END; +$$ LANGUAGE 'plpgsql'; +-- } + + +-- Invalid calls +SELECT 'invalid', TopoGeo_addPolygon('city_data', 'MULTILINESTRING((36 26, 38 30))'); +SELECT 'invalid', TopoGeo_addPolygon('city_data', 'POINT(36 26)'); + +-- Isolated face in universal face +SELECT 'iso_uni', TopoGeo_addPolygon('city_data', 'POLYGON((36 26, 38 30, 43 26, 36 26))'); +SELECT check_changes(); + +-- Isolated face in universal face with hole +SELECT 'iso_uni_hole', TopoGeo_addPolygon('city_data', 'POLYGON((9 28, 16 29, 16 23, 10 23, 9 28),(15 25, 13 27, 11 24, 15 25))'); +SELECT check_changes(); + +-- Existing single face +SELECT 'ex', TopoGeo_addPolygon('city_data', 'POLYGON((21 22,35 22,35 14,21 14,21 22))'); +SELECT check_changes(); + +-- Union of existing faces +SELECT 'ex_union', TopoGeo_addPolygon('city_data', 'POLYGON((9 14,21 14,35 14,35 6,21 6,9 6,9 14))') ORDER BY 2; +SELECT check_changes(); + +-- Half an existing face +SELECT 'half', TopoGeo_addPolygon('city_data', 'POLYGON((21 14, 35 22, 35 14, 21 14))'); +SELECT check_changes(); + +-- Split two existing faces +SELECT 'split', TopoGeo_addPolygon('city_data', 'POLYGON((21 14, 21 22, 35 14, 21 14))') ORDER BY 2; +SELECT check_changes(); + +-- Union of existing face, with hole +SELECT 'ex_hole', TopoGeo_addPolygon('city_data', 'POLYGON((9 22,47 22,47 6,9 6,9 22),(21 14,28 18,35 14,21 14))') ORDER BY 2; +SELECT check_changes(); + +-- Union of existing face, with hole and a tolerance +SELECT 'ex_hole_snap', TopoGeo_addPolygon('city_data', 'POLYGON((9 22,35 22.5, 47 22,47 6,9 6,9 22),(21 14,28 17.5,35 14,21 14))', 1) ORDER BY 2; +SELECT check_changes(); + +DROP FUNCTION check_changes(); +SELECT DropTopology('city_data'); + diff --git a/topology/test/regress/topogeo_addpolygon_expected b/topology/test/regress/topogeo_addpolygon_expected new file mode 100644 index 000000000..40a37a8d1 --- /dev/null +++ b/topology/test/regress/topogeo_addpolygon_expected @@ -0,0 +1,54 @@ +BEGIN +t +9 +22 +26 +COMMIT +max|node|22 +max|edge|26 +max|face|9 +ERROR: Invalid geometry type (MULTILINESTRING) passed to TopoGeo_AddPolygon, expected POLYGON +ERROR: Invalid geometry type (POINT) passed to TopoGeo_AddPolygon, expected POLYGON +iso_uni|10 +N|23||POINT(36 26) +E|27|sn23|en23 +F|10 +iso_uni_hole|11 +N|24||POINT(9 28) +N|25||POINT(15 25) +E|28|sn24|en24 +E|29|sn25|en25 +F|11 +F|12 +ex|4 +ex_union|6 +ex_union|7 +half|4 +E|30|sn14|en18 +F|13 +split|4 +split|13 +N|26||POINT(28 18) +E|30|sn14|en26 +E|31|sn26|en18 +E|32|sn17|en26 +E|33|sn26|en13 +F|14 +F|15 +ex_hole|3 +ex_hole|5 +ex_hole|6 +ex_hole|7 +ex_hole|8 +ex_hole|13 +ex_hole|14 +ex_hole|15 +ex_hole_snap|3 +ex_hole_snap|5 +ex_hole_snap|6 +ex_hole_snap|7 +ex_hole_snap|8 +ex_hole_snap|13 +ex_hole_snap|14 +ex_hole_snap|15 +Topology 'city_data' dropped