/src/postgis/liblwgeom/lwhomogenize.c
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * PostGIS - Spatial Types for PostgreSQL |
4 | | * http://postgis.net |
5 | | * |
6 | | * PostGIS is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation, either version 2 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * PostGIS is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with PostGIS. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | ********************************************************************** |
20 | | * |
21 | | * Copyright 2010 Olivier Courtin <olivier.courtin@oslandia.com> |
22 | | * |
23 | | **********************************************************************/ |
24 | | |
25 | | |
26 | | #include <stdlib.h> |
27 | | #include "liblwgeom_internal.h" |
28 | | #include "lwgeom_log.h" |
29 | | |
30 | | |
31 | | typedef struct { |
32 | | int cnt[NUMTYPES]; |
33 | | LWCOLLECTION* buf[NUMTYPES]; |
34 | | } HomogenizeBuffer; |
35 | | |
36 | | static void |
37 | | init_homogenizebuffer(HomogenizeBuffer *buffer) |
38 | 0 | { |
39 | 0 | int i; |
40 | 0 | for ( i = 0; i < NUMTYPES; i++ ) |
41 | 0 | { |
42 | 0 | buffer->cnt[i] = 0; |
43 | 0 | buffer->buf[i] = NULL; |
44 | 0 | } |
45 | 0 | } |
46 | | |
47 | | /* |
48 | | static void |
49 | | free_homogenizebuffer(HomogenizeBuffer *buffer) |
50 | | { |
51 | | int i; |
52 | | for ( i = 0; i < NUMTYPES; i++ ) |
53 | | { |
54 | | if ( buffer->buf[i] ) |
55 | | { |
56 | | lwcollection_free(buffer->buf[i]); |
57 | | } |
58 | | } |
59 | | } |
60 | | */ |
61 | | |
62 | | /* |
63 | | ** Given a generic collection, return the "simplest" form. |
64 | | ** |
65 | | ** eg: GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() |
66 | | ** |
67 | | ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) |
68 | | ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) |
69 | | ** |
70 | | ** In general, if the subcomponents are homogeneous, return a properly |
71 | | ** typed collection. |
72 | | ** Otherwise, return a generic collection, with the subtypes in minimal |
73 | | ** typed collections. |
74 | | */ |
75 | | static void |
76 | | lwcollection_build_buffer(const LWCOLLECTION *col, HomogenizeBuffer *buffer) |
77 | 0 | { |
78 | 0 | uint32_t i; |
79 | |
|
80 | 0 | if (!col || lwcollection_is_empty(col)) |
81 | 0 | return; |
82 | | |
83 | 0 | for (i = 0; i < col->ngeoms; i++) |
84 | 0 | { |
85 | 0 | LWGEOM *geom = col->geoms[i]; |
86 | 0 | switch (geom->type) |
87 | 0 | { |
88 | 0 | case POINTTYPE: |
89 | 0 | case LINETYPE: |
90 | 0 | case CIRCSTRINGTYPE: |
91 | 0 | case COMPOUNDTYPE: |
92 | 0 | case NURBSCURVETYPE: |
93 | 0 | case TRIANGLETYPE: |
94 | 0 | case CURVEPOLYTYPE: |
95 | 0 | case POLYGONTYPE: |
96 | | /* Init if necessary */ |
97 | 0 | if (!buffer->buf[geom->type]) |
98 | 0 | { |
99 | 0 | LWCOLLECTION *bufcol = lwcollection_construct_empty( |
100 | 0 | COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); |
101 | 0 | bufcol->type = lwtype_get_collectiontype(geom->type); |
102 | 0 | buffer->buf[geom->type] = bufcol; |
103 | 0 | } |
104 | | /* Add sub-geom to buffer */ |
105 | 0 | lwcollection_add_lwgeom(buffer->buf[geom->type], lwgeom_clone_deep(geom)); |
106 | | /* Increment count for this singleton type */ |
107 | 0 | buffer->cnt[geom->type]++; |
108 | 0 | break; |
109 | 0 | default: |
110 | 0 | lwcollection_build_buffer(lwgeom_as_lwcollection(geom), buffer); |
111 | 0 | break; |
112 | 0 | } |
113 | 0 | } |
114 | 0 | } |
115 | | |
116 | | static LWGEOM* |
117 | | lwcollection_homogenize(const LWCOLLECTION *col) |
118 | 0 | { |
119 | 0 | int i; |
120 | 0 | int ntypes = 0; |
121 | 0 | int type = 0; |
122 | 0 | LWGEOM *outgeom = NULL; |
123 | |
|
124 | 0 | HomogenizeBuffer buffer; |
125 | | |
126 | | /* Sort all the parts into a buffer */ |
127 | 0 | init_homogenizebuffer(&buffer); |
128 | 0 | lwcollection_build_buffer(col, &buffer); |
129 | | |
130 | | /* Check for homogeneity */ |
131 | 0 | for ( i = 0; i < NUMTYPES; i++ ) |
132 | 0 | { |
133 | 0 | if ( buffer.cnt[i] > 0 ) |
134 | 0 | { |
135 | 0 | ntypes++; |
136 | 0 | type = i; |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | /* No types? Huh. Return empty. */ |
141 | 0 | if ( ntypes == 0 ) |
142 | 0 | { |
143 | 0 | LWCOLLECTION *outcol; |
144 | 0 | outcol = lwcollection_construct_empty(COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); |
145 | 0 | outgeom = lwcollection_as_lwgeom(outcol); |
146 | 0 | } |
147 | | /* One type, return homogeneous collection */ |
148 | 0 | else if ( ntypes == 1 ) |
149 | 0 | { |
150 | 0 | LWCOLLECTION *outcol; |
151 | 0 | outcol = buffer.buf[type]; |
152 | 0 | if ( outcol->ngeoms == 1 ) |
153 | 0 | { |
154 | 0 | outgeom = outcol->geoms[0]; |
155 | 0 | outcol->ngeoms=0; lwcollection_free(outcol); |
156 | 0 | } |
157 | 0 | else |
158 | 0 | { |
159 | 0 | outgeom = lwcollection_as_lwgeom(outcol); |
160 | 0 | } |
161 | 0 | outgeom->srid = col->srid; |
162 | 0 | } |
163 | | /* Bah, more than out type, return anonymous collection */ |
164 | 0 | else if ( ntypes > 1 ) |
165 | 0 | { |
166 | 0 | int j; |
167 | 0 | LWCOLLECTION *outcol; |
168 | 0 | outcol = lwcollection_construct_empty(COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); |
169 | 0 | for ( j = 0; j < NUMTYPES; j++ ) |
170 | 0 | { |
171 | 0 | if ( buffer.buf[j] ) |
172 | 0 | { |
173 | 0 | LWCOLLECTION *bcol = buffer.buf[j]; |
174 | 0 | if ( bcol->ngeoms == 1 ) |
175 | 0 | { |
176 | 0 | lwcollection_add_lwgeom(outcol, bcol->geoms[0]); |
177 | 0 | bcol->ngeoms=0; lwcollection_free(bcol); |
178 | 0 | } |
179 | 0 | else |
180 | 0 | { |
181 | 0 | lwcollection_add_lwgeom(outcol, lwcollection_as_lwgeom(bcol)); |
182 | 0 | } |
183 | 0 | } |
184 | 0 | } |
185 | 0 | outgeom = lwcollection_as_lwgeom(outcol); |
186 | 0 | } |
187 | |
|
188 | 0 | return outgeom; |
189 | 0 | } |
190 | | |
191 | | |
192 | | |
193 | | |
194 | | |
195 | | /* |
196 | | ** Given a generic geometry, return the "simplest" form. |
197 | | ** |
198 | | ** eg: |
199 | | ** LINESTRING() => LINESTRING() |
200 | | ** |
201 | | ** MULTILINESTRING(with a single line) => LINESTRING() |
202 | | ** |
203 | | ** GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() |
204 | | ** |
205 | | ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) |
206 | | ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) |
207 | | */ |
208 | | LWGEOM * |
209 | | lwgeom_homogenize(const LWGEOM *geom) |
210 | 0 | { |
211 | 0 | LWGEOM *hgeom; |
212 | | |
213 | | /* EMPTY Geometry */ |
214 | 0 | if (lwgeom_is_empty(geom)) |
215 | 0 | { |
216 | 0 | if( lwgeom_is_collection(geom) ) |
217 | 0 | { |
218 | 0 | return lwcollection_as_lwgeom(lwcollection_construct_empty(geom->type, geom->srid, lwgeom_has_z(geom), lwgeom_has_m(geom))); |
219 | 0 | } |
220 | | |
221 | 0 | return lwgeom_clone_deep(geom); |
222 | 0 | } |
223 | | |
224 | 0 | switch (geom->type) |
225 | 0 | { |
226 | | |
227 | | /* Return simple geometries untouched */ |
228 | 0 | case POINTTYPE: |
229 | 0 | case LINETYPE: |
230 | 0 | case CIRCSTRINGTYPE: |
231 | 0 | case COMPOUNDTYPE: |
232 | 0 | case NURBSCURVETYPE: |
233 | 0 | case TRIANGLETYPE: |
234 | 0 | case CURVEPOLYTYPE: |
235 | 0 | case POLYGONTYPE: |
236 | 0 | return lwgeom_clone_deep(geom); |
237 | | |
238 | | /* Process homogeneous geometries lightly */ |
239 | 0 | case MULTIPOINTTYPE: |
240 | 0 | case MULTILINETYPE: |
241 | 0 | case MULTIPOLYGONTYPE: |
242 | 0 | case MULTICURVETYPE: |
243 | 0 | case MULTISURFACETYPE: |
244 | 0 | case POLYHEDRALSURFACETYPE: |
245 | 0 | case TINTYPE: |
246 | 0 | { |
247 | 0 | LWCOLLECTION *col = (LWCOLLECTION*)geom; |
248 | | |
249 | | /* Strip single-entry multi-geometries down to singletons */ |
250 | 0 | if ( col->ngeoms == 1 ) |
251 | 0 | { |
252 | 0 | hgeom = lwgeom_clone_deep((LWGEOM *)(col->geoms[0])); |
253 | 0 | hgeom->srid = geom->srid; |
254 | 0 | if (geom->bbox) |
255 | 0 | hgeom->bbox = gbox_copy(geom->bbox); |
256 | 0 | return hgeom; |
257 | 0 | } |
258 | | |
259 | | /* Return proper multigeometry untouched */ |
260 | 0 | return lwgeom_clone_deep(geom); |
261 | 0 | } |
262 | | |
263 | | /* Work on anonymous collections separately */ |
264 | 0 | case COLLECTIONTYPE: |
265 | 0 | return lwcollection_homogenize((LWCOLLECTION *) geom); |
266 | 0 | } |
267 | | |
268 | | /* Unknown type */ |
269 | 0 | lwerror("lwgeom_homogenize: Geometry Type not supported (%s)", |
270 | 0 | lwtype_name(geom->type)); |
271 | |
|
272 | | return NULL; /* Never get here! */ |
273 | 0 | } |