1-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2--
3-- PostGIS - Spatial Types for PostgreSQL
4-- http://postgis.net
5--
6-- Copyright (C) 2012 Sandro Santilli <strk@kbt.io>
7--
8-- This is free software; you can redistribute and/or modify it under
9-- the terms of the GNU General Public Licence. See the COPYING file.
10--
11-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
12
13-- {
14--  Get a simplified geometry version from a TopoGeometry
15--
16--  Performs Douglas Peucker algorithm on each edge composing
17--  the given TopoGeometry
18--
19-- }{
20CREATE OR REPLACE FUNCTION topology.ST_Simplify(tg topology.TopoGeometry, tolerance float8)
21  RETURNS geometry
22AS
23$$
24DECLARE
25  topology_info RECORD;
26  layer_info RECORD;
27  child_layer_info RECORD;
28  geom geometry;
29  sql TEXT;
30BEGIN
31
32  -- Get topology information
33  SELECT id, name FROM topology.topology
34    INTO topology_info
35    WHERE id = tg.topology_id;
36  IF NOT FOUND THEN
37      RAISE EXCEPTION 'No topology with id "%" in topology.topology', tg.topology_id;
38  END IF;
39
40  -- Get layer info
41  SELECT * FROM topology.layer
42    WHERE topology_id = tg.topology_id
43    AND layer_id = tg.layer_id
44    INTO layer_info;
45  IF NOT FOUND THEN
46    RAISE EXCEPTION 'Could not find TopoGeometry layer % in topology %', tg.layer_id, tg.topology_id;
47  END IF;
48
49  --
50  -- If this feature layer is on any level > 0 we will
51  -- compute the topological union of all simplified child
52  -- features in fact recursing.
53  --
54  IF layer_info.level > 0 THEN -- {
55
56    -- Get child layer info
57    SELECT * FROM topology.layer WHERE layer_id = layer_info.child_id
58      AND topology_id = tg.topology_id
59      INTO child_layer_info;
60    IF NOT FOUND THEN
61      RAISE EXCEPTION 'Invalid layer % in topology % (unexistent child layer %)', tg.layer_id, tg.topology_id, layer_info.child_id;
62    END IF;
63
64    sql := 'SELECT st_multi(st_union(topology.ST_Simplify('
65      || quote_ident(child_layer_info.feature_column)
66      || ',' || tolerance || '))) as geom FROM '
67      || quote_ident(child_layer_info.schema_name) || '.'
68      || quote_ident(child_layer_info.table_name)
69      || ', ' || quote_ident(topology_info.name) || '.relation pr'
70      || ' WHERE '
71      || ' pr.topogeo_id = ' || tg.id
72      || ' AND '
73      || ' pr.layer_id = ' || tg.layer_id
74      || ' AND '
75      || ' id('||quote_ident(child_layer_info.feature_column)
76      || ') = pr.element_id '
77      || ' AND '
78      || 'layer_id('||quote_ident(child_layer_info.feature_column)
79      || ') = pr.element_type ';
80    RAISE DEBUG '%', sql;
81    EXECUTE sql INTO geom;
82
83  ELSIF tg.type = 3 THEN -- [multi]polygon -- }{
84
85    -- TODO: use ST_GetFaceEdges
86    -- TODO: is st_unaryunion needed?
87    sql := 'SELECT st_multi(st_unaryunion(ST_BuildArea(ST_Node(ST_Collect(ST_Simplify(geom, '
88      || tolerance || ')))))) as geom FROM '
89      || quote_ident(topology_info.name)
90      || '.edge_data e, '
91      || quote_ident(topology_info.name)
92      || '.relation r WHERE ( e.left_face = r.element_id'
93      || ' OR e.right_face = r.element_id )'
94      || ' AND r.topogeo_id = ' || tg.id
95      || ' AND r.layer_id = ' || tg.layer_id
96      || ' AND element_type = 3 ';
97    RAISE DEBUG '%', sql;
98    EXECUTE sql INTO geom;
99
100  ELSIF tg.type = 2 THEN -- [multi]line -- }{
101
102    sql :=
103      'SELECT st_multi(ST_LineMerge(ST_Node(ST_Collect(ST_Simplify(e.geom,'
104      || tolerance || '))))) as g FROM '
105      || quote_ident(topology_info.name) || '.edge e, '
106      || quote_ident(topology_info.name) || '.relation r '
107      || ' WHERE r.topogeo_id = ' || tg.id
108      || ' AND r.layer_id = ' || tg.layer_id
109      || ' AND r.element_type = 2 '
110      || ' AND abs(r.element_id) = e.edge_id';
111    EXECUTE sql INTO geom;
112
113  ELSIF tg.type = 1 THEN -- [multi]point -- }{
114
115    -- Can't simplify points...
116    geom := topology.Geometry(tg);
117
118  ELSIF tg.type = 4 THEN -- mixed collection -- }{
119
120   sql := 'WITH areas AS ( '
121      || 'SELECT st_multi(st_union(ST_BuildArea(ST_Node(ST_Collect(ST_Simplify(geom, '
122      || tolerance || ')))) as geom FROM '
123      || quote_ident(topology_info.name)
124      || '.edge_data e, '
125      || quote_ident(topology_info.name)
126      || '.relation r WHERE ( e.left_face = r.element_id'
127      || ' OR e.right_face = r.element_id )'
128      || ' AND r.topogeo_id = ' || tg.id
129      || ' AND r.layer_id = ' || tg.layer_id
130      || ' AND element_type = 3 ), '
131      || 'lines AS ( '
132      || 'SELECT st_multi(ST_LineMerge(ST_Collect(ST_Simplify(e.geom,'
133      || tolerance || ')))) as g FROM '
134      || quote_ident(topology_info.name) || '.edge e, '
135      || quote_ident(topology_info.name) || '.relation r '
136      || ' WHERE r.topogeo_id = ' || tg.id
137      || ' AND r.layer_id = ' || tg.layer_id
138      || ' AND r.element_type = 2 '
139      || ' AND abs(r.element_id) = e.edge_id ), '
140      || ' points as ( SELECT st_union(n.geom) as g FROM '
141      || quote_ident(topology_info.name) || '.node n, '
142      || quote_ident(topology_info.name) || '.relation r '
143      || ' WHERE r.topogeo_id = ' || tg.id
144      || ' AND r.layer_id = ' || tg.layer_id
145      || ' AND r.element_type = 1 '
146      || ' AND r.element_id = n.node_id ), '
147      || ' un as ( SELECT g FROM areas UNION ALL SELECT g FROM lines '
148      || '          UNION ALL SELECT g FROM points ) '
149      || 'SELECT ST_Multi(ST_Collect(g)) FROM un';
150    EXECUTE sql INTO geom;
151
152  ELSE -- }{
153
154    RAISE EXCEPTION 'Invalid TopoGeometries (unknown type %)', tg.type;
155
156  END IF; -- }
157
158  RETURN geom;
159
160END
161$$
162LANGUAGE 'plpgsql' VOLATILE STRICT;
163-- }
164
165