]> granicus.if.org Git - postgis/commitdiff
Ticket #849 by Andrea Peri: topology.GetFaceByPoint implementation and test
authorSandro Santilli <strk@keybit.net>
Wed, 9 Mar 2011 21:59:39 +0000 (21:59 +0000)
committerSandro Santilli <strk@keybit.net>
Wed, 9 Mar 2011 21:59:39 +0000 (21:59 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@6889 b70326c6-7e19-0410-871a-916f4a2858ee

topology/Makefile.in
topology/sql/query/getfacebypoint.sql [new file with mode: 0644]
topology/test/Makefile
topology/test/regress/getfacebypoint.sql [new file with mode: 0644]
topology/test/regress/getfacebypoint_expected [new file with mode: 0644]
topology/topology.sql.in.c

index 9e19bc3227c2a1a787c2df1e724cc9baded92d62..d6d03669651a1aa4d8ac1bb9cffc4a32dd27b5bb 100644 (file)
@@ -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 (file)
index 0000000..cd43236
--- /dev/null
@@ -0,0 +1,158 @@
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+-- 
+-- PostGIS - Spatial Types for PostgreSQL
+-- http://postgis.refractions.net
+--
+-- Copyright (C) 2011 Andrea Peri <aperi2007@gmail.com>
+--
+-- 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
+
index ee384eee18d254363e0a1d50e6e22552a419fac1..bbc0f92e612f195331eed977d95a97413d9b0456 100644 (file)
@@ -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 (file)
index 0000000..b9dd123
--- /dev/null
@@ -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 (file)
index 0000000..fb4f66c
--- /dev/null
@@ -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
index 01c3a2f01116eb1a2f5fc2fd080666cd5fa7d46c..8db14d3cf2e1b72869d1e8c1f022345758b1d44d 100644 (file)
@@ -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"