From: Sandro Santilli Date: Thu, 7 Mar 2013 16:14:45 +0000 (+0000) Subject: Add ST_Simplify override for TopoGeometry objects (#1687) X-Git-Tag: 2.1.0beta2~178 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6b74c8c9c32e26ca44d46d1fc21539ff2636fc2a;p=postgis Add ST_Simplify override for TopoGeometry objects (#1687) git-svn-id: http://svn.osgeo.org/postgis/trunk@11158 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/topology/Makefile.in b/topology/Makefile.in index 8f6cde5d2..6ad2dcdbc 100644 --- a/topology/Makefile.in +++ b/topology/Makefile.in @@ -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 index 000000000..960431045 --- /dev/null +++ b/topology/sql/topogeometry/simplify.sql.in.c @@ -0,0 +1,165 @@ +-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +-- +-- PostGIS - Spatial Types for PostgreSQL +-- http://postgis.refractions.net +-- +-- Copyright (C) 2012 Sandro Santilli +-- +-- 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; +-- } + diff --git a/topology/test/Makefile.in b/topology/test/Makefile.in index b32774f9d..bcf81598a 100644 --- a/topology/test/Makefile.in +++ b/topology/test/Makefile.in @@ -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 index 000000000..0497b5966 --- /dev/null +++ b/topology/test/regress/st_simplify.sql @@ -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 index 000000000..75152ce96 --- /dev/null +++ b/topology/test/regress/st_simplify_expected @@ -0,0 +1,6 @@ +t +L1 +N2 +S1|f|t +S2|f|t +f diff --git a/topology/topology.sql.in.c b/topology/topology.sql.in.c index 5e5c16ff3..665a6dc68 100644 --- a/topology/topology.sql.in.c +++ b/topology/topology.sql.in.c @@ -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