]> granicus.if.org Git - postgis/commitdiff
Add ST_Simplify override for TopoGeometry objects (#1687)
authorSandro Santilli <strk@keybit.net>
Thu, 7 Mar 2013 16:14:45 +0000 (16:14 +0000)
committerSandro Santilli <strk@keybit.net>
Thu, 7 Mar 2013 16:14:45 +0000 (16:14 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@11158 b70326c6-7e19-0410-871a-916f4a2858ee

topology/Makefile.in
topology/sql/topogeometry/simplify.sql.in.c [new file with mode: 0644]
topology/test/Makefile.in
topology/test/regress/st_simplify.sql [new file with mode: 0644]
topology/test/regress/st_simplify_expected [new file with mode: 0644]
topology/topology.sql.in.c

index 8f6cde5d21dbea8d7a2732abd0c2c647b5fa6178..6ad2dcdbc773e96f4e81c6da120f363ee576ba94 100644 (file)
@@ -90,7 +90,7 @@ topology_upgrade.sql:  topology.sql
 topology_upgrade_$(PGIS_MAJ_MIN)_minor.sql:  topology_drop_before.sql topology_upgrade.sql topology_drop_after.sql
        cat $^ > $@
 
-topology.sql.in: sql/sqlmm.sql.in.c sql/populate.sql.in.c sql/polygonize.sql.in.c sql/gml.sql.in.c sql/query/getnodebypoint.sql.in.c sql/query/getedgebypoint.sql.in.c sql/query/getfacebypoint.sql.in.c sql/query/GetRingEdges.sql.in.c sql/query/GetNodeEdges.sql.in.c sql/manage/TopologySummary.sql.in.c sql/manage/CopyTopology.sql.in.c sql/manage/ManageHelper.sql.in.c sql/topoelement/topoelement_agg.sql.in.c sql/topogeometry/type.sql.in.c sql/topogeometry/totopogeom.sql.in.c sql/topogeometry/cleartopogeom.sql.in.c sql/predicates.sql.in.c ../postgis/sqldefines.h ../postgis_svn_revision.h
+topology.sql.in: sql/sqlmm.sql.in.c sql/populate.sql.in.c sql/polygonize.sql.in.c sql/gml.sql.in.c sql/query/getnodebypoint.sql.in.c sql/query/getedgebypoint.sql.in.c sql/query/getfacebypoint.sql.in.c sql/query/GetRingEdges.sql.in.c sql/query/GetNodeEdges.sql.in.c sql/manage/TopologySummary.sql.in.c sql/manage/CopyTopology.sql.in.c sql/manage/ManageHelper.sql.in.c sql/topoelement/topoelement_agg.sql.in.c sql/topogeometry/type.sql.in.c sql/topogeometry/totopogeom.sql.in.c sql/topogeometry/cleartopogeom.sql.in.c sql/topogeometry/simplify.sql.in.c sql/predicates.sql.in.c ../postgis/sqldefines.h ../postgis_svn_revision.h
 
 uninstall_topology.sql: topology.sql ../utils/create_undef.pl 
        $(PERL) ../utils/create_undef.pl $< $(POSTGIS_PGSQL_VERSION) > $@
diff --git a/topology/sql/topogeometry/simplify.sql.in.c b/topology/sql/topogeometry/simplify.sql.in.c
new file mode 100644 (file)
index 0000000..9604310
--- /dev/null
@@ -0,0 +1,165 @@
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+-- 
+-- PostGIS - Spatial Types for PostgreSQL
+-- http://postgis.refractions.net
+--
+-- Copyright (C) 2012 Sandro Santilli <strk@keybit.net>
+--
+-- This is free software; you can redistribute and/or modify it under
+-- the terms of the GNU General Public Licence. See the COPYING file.
+--
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+-- {
+--  Get a simplified geometry version from a TopoGeometry
+--
+--  Performs Douglas Peucker algorithm on each edge composing
+--  the given TopoGeometry
+--
+-- }{
+CREATE OR REPLACE FUNCTION topology.ST_Simplify(tg topology.TopoGeometry, tolerance float8)
+  RETURNS geometry
+AS
+$$
+DECLARE
+  topology_info RECORD;
+  layer_info RECORD;
+  child_layer_info RECORD;
+  geom geometry;
+  sql TEXT;
+BEGIN
+
+  -- Get topology information
+  SELECT id, name FROM topology.topology
+    INTO topology_info
+    WHERE id = tg.topology_id;
+  IF NOT FOUND THEN
+      RAISE EXCEPTION 'No topology with id "%" in topology.topology', tg.topology_id;
+  END IF;
+
+  -- Get layer info
+  SELECT * FROM topology.layer
+    WHERE topology_id = tg.topology_id
+    AND layer_id = tg.layer_id
+    INTO layer_info;
+  IF NOT FOUND THEN
+    RAISE EXCEPTION 'Could not find TopoGeometry layer % in topology %', tg.layer_id, tg.topology_id;
+  END IF;
+
+  --
+  -- If this feature layer is on any level > 0 we will
+  -- compute the topological union of all simplified child
+  -- features in fact recursing.
+  --
+  IF layer_info.level > 0 THEN -- {
+
+    -- Get child layer info
+    SELECT * FROM topology.layer WHERE layer_id = layer_info.child_id
+      AND topology_id = tg.topology_id
+      INTO child_layer_info;
+    IF NOT FOUND THEN
+      RAISE EXCEPTION 'Invalid layer % in topology % (unexistent child layer %)', tg.layer_id, tg.topology_id, layer_info.child_id;
+    END IF;
+
+    sql := 'SELECT st_multi(st_union(topology.ST_Simplify('
+      || quote_ident(child_layer_info.feature_column)
+      || '))) as geom FROM '
+      || quote_ident(child_layer_info.schema_name) || '.'
+      || quote_ident(child_layer_info.table_name)
+      || ', ' || quote_ident(topology_info.name) || '.relation pr'
+      || ' WHERE '
+      || ' pr.topogeo_id = ' || tg.id
+      || ' AND '
+      || ' pr.layer_id = ' || tg.layer_id
+      || ' AND '
+      || ' id('||quote_ident(child_layer_info.feature_column)
+      || ') = pr.element_id '
+      || ' AND '
+      || 'layer_id('||quote_ident(child_layer_info.feature_column)
+      || ') = pr.element_type ';
+    RAISE DEBUG '%', sql;
+    EXECUTE sql INTO geom;
+
+  ELSIF tg.type = 3 THEN -- [multi]polygon -- }{
+
+    -- TODO: use ST_GetFaceEdges
+    -- TODO: is st_unaryunion needed?
+    sql := 'SELECT st_multi(st_unaryunion(ST_BuildArea(ST_Node(ST_Collect(ST_Simplify(geom, '
+      || tolerance || ')))))) as geom FROM '
+      || quote_ident(topology_info.name)
+      || '.edge_data e, '
+      || quote_ident(topology_info.name)
+      || '.relation r WHERE ( e.left_face = r.element_id'
+      || ' OR e.right_face = r.element_id )'
+      || ' AND r.topogeo_id = ' || tg.id
+      || ' AND r.layer_id = ' || tg.layer_id
+      || ' AND element_type = 3 ';
+    RAISE DEBUG '%', sql;
+    EXECUTE sql INTO geom;
+
+
+  ELSIF tg.type = 2 THEN -- [multi]line -- }{
+
+    sql := 
+      'SELECT st_multi(ST_LineMerge(ST_Node(ST_Collect(ST_Simplify(e.geom,'
+      || tolerance || '))))) as g FROM '
+      || quote_ident(topology_info.name) || '.edge e, '
+      || quote_ident(topology_info.name) || '.relation r '
+      || ' WHERE r.topogeo_id = ' || tg.id
+      || ' AND r.layer_id = ' || tg.layer_id
+      || ' AND r.element_type = 2 '
+      || ' AND abs(r.element_id) = e.edge_id';
+    EXECUTE sql INTO geom;
+  
+  ELSIF tg.type = 1 THEN -- [multi]point -- }{
+
+    -- Can't simplify points... 
+    geom := topology.Geometry(tg);
+
+  ELSIF tg.type = 4 THEN -- mixed collection -- }{
+
+   sql := 'WITH areas AS ( '
+      || 'SELECT st_multi(st_union(ST_BuildArea(ST_Node(ST_Collect(ST_Simplify(geom, '
+      || tolerance || ')))) as geom FROM '
+      || quote_ident(topology_info.name)
+      || '.edge_data e, '
+      || quote_ident(topology_info.name)
+      || '.relation r WHERE ( e.left_face = r.element_id'
+      || ' OR e.right_face = r.element_id )'
+      || ' AND r.topogeo_id = ' || tg.id
+      || ' AND r.layer_id = ' || tg.layer_id
+      || ' AND element_type = 3 ), '
+      || 'lines AS ( '
+      || 'SELECT st_multi(ST_LineMerge(ST_Collect(ST_Simplify(e.geom,'
+      || tolerance || ')))) as g FROM '
+      || quote_ident(topology_info.name) || '.edge e, '
+      || quote_ident(topology_info.name) || '.relation r '
+      || ' WHERE r.topogeo_id = ' || tg.id
+      || ' AND r.layer_id = ' || tg.layer_id
+      || ' AND r.element_type = 2 '
+      || ' AND abs(r.element_id) = e.edge_id ), '
+      || ' points as ( SELECT st_union(n.geom) as g FROM '
+      || quote_ident(topology_info.name) || '.node n, '
+      || quote_ident(topology_info.name) || '.relation r '
+      || ' WHERE r.topogeo_id = ' || tg.id
+      || ' AND r.layer_id = ' || tg.layer_id
+      || ' AND r.element_type = 1 '
+      || ' AND r.element_id = n.node_id ), '
+      || ' un as ( SELECT g FROM areas UNION ALL SELECT g FROM lines '
+      || '          UNION ALL SELECT g FROM points ) '
+      || 'SELECT ST_Multi(ST_Collect(g)) FROM un';
+    EXECUTE sql INTO geom;
+
+  ELSE -- }{
+
+    RAISE EXCEPTION 'Invalid TopoGeometries (unknown type %)', tg.type;
+
+  END IF; -- }
+
+  RETURN geom;
+
+END
+$$
+LANGUAGE 'plpgsql' VOLATILE STRICT;
+-- }
+
index b32774f9d8c4b66c88fdd7be711915149eaa2733..bcf81598acb2a7b6265066f0a1cbef09a70a2efa 100644 (file)
@@ -44,6 +44,7 @@ TESTS = regress/legacy_validate.sql regress/legacy_predicate.sql \
        regress/st_newedgessplit.sql \
        regress/st_remedgenewface.sql \
        regress/st_remedgemodface.sql \
+       regress/st_simplify.sql \
        regress/topoelement.sql \
        regress/topoelementarray_agg.sql \
        regress/topogeo_addlinestring \
diff --git a/topology/test/regress/st_simplify.sql b/topology/test/regress/st_simplify.sql
new file mode 100644 (file)
index 0000000..0497b59
--- /dev/null
@@ -0,0 +1,23 @@
+set client_min_messages to WARNING;
+
+SELECT CreateTopology('tt') > 1;
+CREATE TABLE tt.areas(id serial, g geometry);
+INSERT INTO tt.areas(g) VALUES ('POLYGON((0 0,1 1,1 3,0 4,-2 3,-1 1,0 0))'),
+                               ('POLYGON((0 0,1 1,1 3,2 3,2 0,0 0))');
+SELECT 'L' || AddTopoGeometryColumn('tt', 'tt', 'areas', 'tg', 'polygon');
+UPDATE tt.areas SET tg = toTopoGeom(g, 'tt', 1);
+
+-- ensures this point won't be removed
+SELECT 'N' || TopoGeo_addPoint('tt', 'POINT(1 3)');
+
+SELECT 'S1',
+  -- Point 1 3 is removed when simplifying the simple (unconstrained) geometry
+  ST_Equals(ST_Simplify( g, 1), 'POLYGON((0 0,1 3,-2 3,0 0))'),
+  ST_Equals(ST_Simplify(tg, 1), 'POLYGON((0 0,1 3,-2 3,0 0))')
+FROM tt.areas WHERE id = 1;
+SELECT 'S2',
+  ST_Equals(ST_Simplify( g, 1), 'POLYGON((0 0,1 3,2 0,0 0))'),
+  ST_Equals(ST_Simplify(tg, 1), 'POLYGON((0 0,1 3,2 0,0 0))')
+FROM tt.areas WHERE id = 2;
+
+SELECT DropTopology('tt') IS NULL;
diff --git a/topology/test/regress/st_simplify_expected b/topology/test/regress/st_simplify_expected
new file mode 100644 (file)
index 0000000..75152ce
--- /dev/null
@@ -0,0 +1,6 @@
+t
+L1
+N2
+S1|f|t
+S2|f|t
+f
index 5e5c16ff3f62f39d75c158567ce991958771989c..665a6dc689d160393f3a5810e8b2921216e0cb62 100644 (file)
@@ -1984,6 +1984,7 @@ LANGUAGE 'plpgsql' VOLATILE STRICT;
 --  TopoGeometry
 #include "sql/topogeometry/type.sql.in.c"
 #include "sql/topogeometry/cleartopogeom.sql.in.c"
+#include "sql/topogeometry/simplify.sql.in.c"
 #include "sql/topogeometry/totopogeom.sql.in.c"
 
 --  GML