1-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2--
3-- PostGIS - Spatial Types for PostgreSQL
4-- http://postgis.net
5--
6-- Copyright (C) 2015 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/* #define POSTGIS_TOPOLOGY_DEBUG 1 */
14
15-- {
16--  Add an element to a TopoGeometry definition
17--
18-- }{
19CREATE OR REPLACE FUNCTION topology.TopoGeom_addElement(tg topology.TopoGeometry, el topology.TopoElement)
20  RETURNS topology.TopoGeometry
21AS
22$$
23DECLARE
24  toponame TEXT;
25  sql TEXT;
26BEGIN
27
28  -- Get topology name
29  BEGIN
30    SELECT name
31    FROM topology.topology
32      INTO STRICT toponame WHERE id = topology_id(tg);
33  EXCEPTION
34    WHEN NO_DATA_FOUND THEN
35      RAISE EXCEPTION 'No topology with name "%" in topology.topology',
36        atopology;
37  END;
38
39  -- Insert new element
40  sql := format('INSERT INTO %s.relation'
41         '(topogeo_id,layer_id,element_id,element_type)'
42         ' VALUES($1,$2,$3,$4)', quote_ident(toponame));
43  BEGIN
44    EXECUTE sql USING id(tg), layer_id(tg), el[1], el[2];
45  EXCEPTION
46    WHEN unique_violation THEN
47      -- already present, let go
48    WHEN OTHERS THEN
49      RAISE EXCEPTION 'Got % (%)', SQLERRM, SQLSTATE;
50  END;
51
52  RETURN tg;
53
54END
55$$
56LANGUAGE 'plpgsql' VOLATILE STRICT;
57-- }
58
59-- {
60--  Remove an element from a TopoGeometry definition
61--
62-- }{
63CREATE OR REPLACE FUNCTION topology.TopoGeom_remElement(tg topology.TopoGeometry, el topology.TopoElement)
64  RETURNS topology.TopoGeometry
65AS
66$$
67DECLARE
68  toponame TEXT;
69  sql TEXT;
70BEGIN
71
72  -- Get topology name
73  BEGIN
74    SELECT name
75    FROM topology.topology
76      INTO STRICT toponame WHERE id = topology_id(tg);
77  EXCEPTION
78    WHEN NO_DATA_FOUND THEN
79      RAISE EXCEPTION 'No topology with name "%" in topology.topology',
80        atopology;
81  END;
82
83  -- Delete the element
84  sql := format('DELETE FROM %s.relation WHERE '
85         'topogeo_id = $1 AND layer_id = $2 AND '
86         'element_id = $3 AND element_type = $4',
87         quote_ident(toponame));
88  EXECUTE sql USING id(tg), layer_id(tg), el[1], el[2];
89
90  RETURN tg;
91
92END
93$$
94LANGUAGE 'plpgsql' VOLATILE STRICT;
95-- }
96
97
98-- {
99-- Add the component of a TopoGeometry to the definition of
100-- another TopoGeometry.
101--
102-- The two TopoGeometry objects need to be defined on the *same*
103-- topology and need to be compatible (both simple or built over
104-- the same child layer, and the target TopoGeometry needs to allow
105-- for holding components of the type found in the source TopoGeometry)
106--
107-- }{
108CREATE OR REPLACE FUNCTION topology.TopoGeom_addTopoGeom(tgt topology.TopoGeometry, src topology.TopoGeometry)
109  RETURNS topology.TopoGeometry
110AS
111$BODY$
112DECLARE
113  sql TEXT;
114  topo topology.topology;
115  srcElementTypes int[];
116  srcLayer topology.layer;
117  tgtLayer topology.layer;
118  maxElemType int;
119BEGIN
120
121  -- Get topology information
122  topo := topology.FindTopology(topology_id(src));
123
124#ifdef POSTGIS_TOPOLOGY_DEBUG
125  RAISE DEBUG 'Source TopoGeometry is "%", its topology_id is "%"', src, topo.id;
126#endif
127
128  IF topology_id(src) != topology_id(tgt) THEN
129    RAISE EXCEPTION 'Source and target TopoGeometry objects need be defined on the same topology';
130  END IF;
131
132#ifdef POSTGIS_TOPOLOGY_DEBUG
133  RAISE DEBUG 'Target TopoGeometry is "%"', tgt;
134#endif
135
136  SELECT * FROM topology.layer
137  WHERE topology_id = topo.id
138    AND layer_id = layer_id(src)
139  INTO srcLayer;
140
141  SELECT * FROM topology.layer
142  WHERE topology_id = topo.id
143    AND layer_id = layer_id(tgt)
144  INTO tgtLayer;
145
146  -- Check simple/hierarchical compatibility
147  IF srcLayer.child_id IS NULL THEN
148    IF srcLayer.child_id IS NOT NULL THEN
149      RAISE EXCEPTION 'Cannot add components of hierarchical TopoGeometry to a non-hierarchical TopoGeometry';
150    END IF;
151  ELSIF tgtLayer.child_id IS NULL THEN
152      RAISE EXCEPTION 'Cannot add components of non-hierarchical TopoGeometry to a hierarchical TopoGeometry';
153  ELSIF tgtLayer.child_id != srcLayer.childId THEN
154      RAISE EXCEPTION 'Cannot add components of hierarchical TopoGeometry to a hierarchical TopoGeometry based on different layer';
155  END IF;
156
157  -- Add every element of the source TopoGeometry to
158  -- the definition of the target TopoGeometry
159  sql := format($$
160WITH inserted AS (
161  INSERT INTO %1$I.relation(
162    topogeo_id,
163    layer_id,
164    element_id,
165    element_type
166  )
167  SELECT %2$s, %3$s, element_id, element_type
168  FROM %1$I.relation
169  WHERE topogeo_id = %4$L
170  AND layer_id = %5$L
171  EXCEPT
172  SELECT %2$s, %3$s, element_id, element_type
173  FROM %1$I.relation
174  WHERE topogeo_id = %2$L
175  AND layer_id = %3$L
176  RETURNING element_type
177)
178SELECT array_agg(DISTINCT element_type) FROM inserted
179    $$,
180    topo.name,      -- %1
181    id(tgt),        -- %2
182    layer_id(tgt),  -- %3
183    id(src),        -- %4
184    layer_id(src)   -- %5
185  );
186
187  RAISE DEBUG 'SQL: %', sql;
188
189  EXECUTE sql INTO srcElementTypes;
190
191  -- TODO: Check layer's feature_type compatibility ?
192  -- or let the relationTrigger take care of it ?
193--  IF tgtLayer.feature_type != 4 THEN -- 'mixed' typed target can accept anything
194--    IF srcLayer.feature_type != tgtLayer.feature_type THEN
195--    END IF;
196--  END IF;
197
198  RAISE DEBUG 'Target type: %', type(tgt);
199  RAISE DEBUG 'Detected source element types: %', srcElementTypes;
200
201  -- Check if target TopoGeometry type needs be changed
202  IF type(tgt) != 4 -- collection TopoGeometry accept anything
203  THEN
204    IF array_upper(srcElementTypes, 1) > 1
205    OR srcElementTypes[1] != tgt.type
206    THEN
207      -- source is mixed-typed or typed differently from
208      -- target, so we turn target type to collection
209      RAISE DEBUG 'Changing target element type to collection';
210      tgt.type = 4;
211    END IF;
212  END IF;
213
214
215
216
217  RETURN tgt;
218END
219$BODY$
220LANGUAGE 'plpgsql' VOLATILE STRICT;
221-- }
222