From: Sandro Santilli Date: Wed, 9 Mar 2011 21:59:39 +0000 (+0000) Subject: Ticket #849 by Andrea Peri: topology.GetFaceByPoint implementation and test X-Git-Tag: 2.0.0alpha1~1912 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bff7f6071fbdbf6241a167ad4a02f571b2f83618;p=postgis Ticket #849 by Andrea Peri: topology.GetFaceByPoint implementation and test git-svn-id: http://svn.osgeo.org/postgis/trunk@6889 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/topology/Makefile.in b/topology/Makefile.in index 9e19bc322..d6d036696 100644 --- a/topology/Makefile.in +++ b/topology/Makefile.in @@ -42,7 +42,7 @@ endif $(SQL_OBJS): %.in: %.in.c $(CPP) -traditional-cpp $< | grep -v '^#' > $@ -topology.sql.in: sql/sqlmm.sql sql/populate.sql sql/gml.sql sql/query/getnodebypoint.sql sql/query/getedgebypoint.sql sql/manage/TopologySummary.sql sql/manage/CopyTopology.sql +topology.sql.in: sql/sqlmm.sql sql/populate.sql sql/gml.sql sql/query/getnodebypoint.sql sql/query/getedgebypoint.sql sql/query/getfacebypoint.sql sql/manage/TopologySummary.sql sql/manage/CopyTopology.sql check: topology.sql $(MAKE) -C test $@ diff --git a/topology/sql/query/getfacebypoint.sql b/topology/sql/query/getfacebypoint.sql new file mode 100644 index 000000000..cd4323623 --- /dev/null +++ b/topology/sql/query/getfacebypoint.sql @@ -0,0 +1,158 @@ +-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +-- +-- PostGIS - Spatial Types for PostgreSQL +-- http://postgis.refractions.net +-- +-- Copyright (C) 2011 Andrea Peri +-- +-- This is free software; you can redistribute and/or modify it under +-- the terms of the GNU General Public Licence. See the COPYING file. +-- +-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +--{ +-- +-- Andrea Peri (27 Feb 2011) creation +-- +-- GetFaceByPoint(atopology, point, tol) +-- +-- Retrieve a Face ID given a POINT and a tolerance +-- tolerance = 0 mean exactly intersection +-- +-- Returns return the integer ID if there is a face on the Point. +-- +-- When the Point is even a Node it raise an exception. +-- This case is testable with the GetNodeByPoint(atopology, apoint, tol) +-- +-- If there isn't any face in the Point, GetFaceByPoint return 0. +-- +-- if near the point there are two or more faces it throw an exception. +-- +CREATE OR REPLACE FUNCTION topology.GetFaceByPoint(atopology varchar, apoint geometry, tol1 float8) + RETURNS int +AS +$$ +DECLARE + sql text; + idface int; +BEGIN + + idface := -1; + + -- + -- Atopology and apoint are required + -- + IF atopology IS NULL OR apoint IS NULL THEN + RAISE EXCEPTION 'Invalid null argument'; + END IF; + + -- + -- Apoint must be a point + -- + IF substring(geometrytype(apoint), 1, 5) != 'POINT' + THEN + RAISE EXCEPTION 'Node geometry must be a point'; + END IF; + + -- + -- Tolerance must be >= 0 + -- + IF tol1 < 0 + THEN + RAISE EXCEPTION 'Tolerance must be >=0'; + END IF; + -- + -- first test is to check if there is inside an mbr + -- + if tol1 = 0 then + sql := 'SELECT a.face_id FROM ' + || quote_ident(atopology) + || '.face as a WHERE ' + || '(a.mbr && ' || quote_literal(apoint::text)||'::geometry) ' + || 'LIMIT 1;'; + else + sql := 'SELECT a.face_id FROM ' + || quote_ident(atopology) + || '.face as a WHERE ' + || '(ST_DWithin(a.mbr,' || quote_literal(apoint::text)||'::geometry,' || tol1::text || ') ) ' + || 'LIMIT 1;'; + end if; + + BEGIN + EXECUTE sql INTO STRICT idface; + EXCEPTION + WHEN NO_DATA_FOUND THEN + idface = 0; + END; + + if idface > 0 then + -- + -- probably there is something so now check the exact test + -- + + if tol1 = 0 then + sql := 'SELECT e.face_id FROM (' + || 'SELECT d.face_id,ST_BuildArea(ST_Union(geom)) as geom FROM (' + || 'SELECT b.edge_id as edge_id,b.left_face as face_id,b.geom as geom FROM ' + || quote_ident(atopology) || '.edge_data as b,' + || '(SELECT a.face_id FROM ' + || quote_ident(atopology) || '.face as a ' + || 'WHERE ST_Intersects(a.mbr,' || quote_literal(apoint::text)||'::geometry)=true' + || ') as c ' + || 'WHERE (b.left_face = c.face_id) ' + || ' UNION ALL ' + || 'SELECT b.edge_id as edge_id, b.right_face as face_id, b.geom as geom FROM ' + || quote_ident(atopology) || '.edge_data as b,' + || '(SELECT a.face_id FROM ' + || quote_ident(atopology) || '.face as a ' + || 'WHERE ST_Intersects(a.mbr,' || quote_literal(apoint::text)||'::geometry)=true' + || ') as c ' + || 'WHERE (b.right_face = c.face_id) ' + || ') as d ' + || 'GROUP BY face_id ' + || ') as e ' + || 'WHERE ST_Intersects(e.geom, ' || quote_literal(apoint::text)||'::geometry)=true;'; + else + sql := 'SELECT e.face_id FROM (' + || 'SELECT d.face_id,ST_BuildArea(ST_Union(geom)) as geom FROM (' + || 'SELECT b.edge_id as edge_id,b.left_face as face_id,b.geom as geom FROM ' + || quote_ident(atopology) || '.edge_data as b,' + || '(SELECT a.face_id FROM ' + || quote_ident(atopology) || '.face as a ' + || 'WHERE ST_DWithin(a.mbr,' || quote_literal(apoint::text)||'::geometry,' || tol1::text || ')=true' + || ') as c ' + || 'WHERE (b.left_face = c.face_id) ' + || ' UNION ALL ' + || 'SELECT b.edge_id as edge_id, b.right_face as face_id, b.geom as geom FROM ' + || quote_ident(atopology) || '.edge_data as b,' + || '(SELECT a.face_id FROM ' + || quote_ident(atopology) || '.face as a ' + || 'WHERE ST_DWithin(a.mbr,' || quote_literal(apoint::text)||'::geometry,' || tol1::text || ')=true' + || ') as c ' + || 'WHERE (b.right_face = c.face_id) ' + || ') as d ' + || 'GROUP BY face_id ' + || ') as e ' + || 'WHERE ST_DWithin(e.geom, ' || quote_literal(apoint::text)||'::geometry,' || tol1::text || ')=true;'; + end if; + + raise notice ' ==> %',sql; + + BEGIN + EXECUTE sql INTO STRICT idface; + EXCEPTION + WHEN NO_DATA_FOUND THEN + idface = 0; + WHEN TOO_MANY_ROWS THEN + RAISE EXCEPTION 'Two or more faces found'; + END; + + end if; + + RETURN idface; + +END +$$ +LANGUAGE 'plpgsql' STRICT; +--} GetFaceByPoint + diff --git a/topology/test/Makefile b/topology/test/Makefile index ee384eee1..bbc0f92e6 100644 --- a/topology/test/Makefile +++ b/topology/test/Makefile @@ -34,7 +34,8 @@ TESTS = regress/legacy_validate.sql regress/legacy_predicate.sql \ regress/createtopogeom.sql \ regress/gml.sql \ regress/getnodebypoint.sql \ - regress/getedgebypoint.sql + regress/getedgebypoint.sql \ + regress/getfacebypoint.sql check: topo_predicates.sql load_topology.sql load_topology-4326.sql $(MAKE) -C ../../regress postgis.sql staged-install diff --git a/topology/test/regress/getfacebypoint.sql b/topology/test/regress/getfacebypoint.sql new file mode 100644 index 000000000..b9dd1237c --- /dev/null +++ b/topology/test/regress/getfacebypoint.sql @@ -0,0 +1,54 @@ +set client_min_messages to WARNING; + +SELECT topology.CreateTopology('schema_topo') > 0; + +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(1 2, 1 5)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(1 5, 10 5)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(10 5, 10 2)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(10 2, 1 2)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(10 5, 10 12)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(10 12, 10 14)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(10 14, 10 15)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(10 15, 15 15)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(15 15, 15 2)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(15 2, 10 2)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(1 5, 1 12)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(1 12, 7 12)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(7 12, 8 12)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(8 12, 10 12)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(7 12, 7 15, 10 15)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(8 12, 8 14, 10 14)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(4 7, 4 10)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(4 10, 6 10)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(6 10, 6 7)')); +select topology.AddEdge('schema_topo',ST_GeomFromText('LINESTRING(6 7, 4 7)')); + +select topology.addFace('schema_topo', 'POLYGON((1 2, 1 5, 10 5, 10 2, 1 2 ))'); +select topology.addFace('schema_topo', 'POLYGON((10 2, 10 5, 10 12, 10 14, 10 15, 15 15, 15 2, 10 2))'); +select topology.addFace('schema_topo', 'POLYGON((7 12, 7 15, 10 15, 10 14, 8 14, 8 12, 7 12))'); +select topology.addFace('schema_topo', 'POLYGON((1 5, 1 12, 7 12, 8 12, 10 12, 10 5, 1 5),(4 7, 4 10, 6 10, 6 7, 4 7))'); + + +-- ask for a Point with tolerance zero +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(7 7)'), 0::float8)::int; +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(6 7)'), 0::float8)::int; +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(5 7)'), 0::float8)::int; + +-- ask for a Point where there isn't a Face +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(9 13)'), 0::float8)::int; +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(5 8)'), 0::float8)::int; + +-- Ask for a point outside from an face but with a tolerance sufficient to include one face +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(8.5 13)'), 0.5::float8)::int; +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(5 8)'), 1::float8)::int; + +-- Failing cases (should all raise exceptions) ------- + +-- Ask for Point in a Node (2 or more faces) +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(1 5)'), 0::float8)::int; + +-- Ask for a Point with a tollerance too high (2 or more faces) +select topology.GetFaceByPoint('schema_topo',ST_GeomFromText('POINT(9 13)'), 1::float8)::int; + + +SELECT topology.DropTopology('schema_topo'); diff --git a/topology/test/regress/getfacebypoint_expected b/topology/test/regress/getfacebypoint_expected new file mode 100644 index 000000000..fb4f66cd5 --- /dev/null +++ b/topology/test/regress/getfacebypoint_expected @@ -0,0 +1,43 @@ +t +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +WARNING: Not checking if face contains any edge +WARNING: Not updating next_{left,right}_face fields of face boundary edges +1 +WARNING: Not checking if face contains any edge +WARNING: Not updating next_{left,right}_face fields of face boundary edges +2 +WARNING: Not checking if face contains any edge +WARNING: Not updating next_{left,right}_face fields of face boundary edges +3 +WARNING: Not checking if face contains any edge +WARNING: Not updating next_{left,right}_face fields of face boundary edges +4 +4 +4 +4 +0 +0 +3 +4 +ERROR: Two or more faces found +ERROR: Two or more faces found +Topology 'schema_topo' dropped diff --git a/topology/topology.sql.in.c b/topology/topology.sql.in.c index 01c3a2f01..8db14d3cf 100644 --- a/topology/topology.sql.in.c +++ b/topology/topology.sql.in.c @@ -2417,6 +2417,7 @@ LANGUAGE 'plpgsql' VOLATILE STRICT; -- Querying #include "sql/query/getnodebypoint.sql" #include "sql/query/getedgebypoint.sql" +#include "sql/query/getfacebypoint.sql" -- Editing #include "sql/populate.sql"