/src/postgis/liblwgeom/lwgeom_geos.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 2011-2020 Sandro Santilli <strk@kbt.io> |
22 | | * Copyright 2015-2018 Daniel Baston <dbaston@gmail.com> |
23 | | * Copyright 2017-2018 Darafei Praliaskouski <me@komzpa.net> |
24 | | * |
25 | | **********************************************************************/ |
26 | | |
27 | | #include "lwgeom_geos.h" |
28 | | #include "liblwgeom.h" |
29 | | #include "liblwgeom_internal.h" |
30 | | #include "lwgeom_log.h" |
31 | | #include "lwrandom.h" |
32 | | |
33 | | #include <stdarg.h> |
34 | | #include <stdlib.h> |
35 | | #include <string.h> |
36 | | |
37 | | LWTIN* lwtin_from_geos(const GEOSGeometry* geom, uint8_t want3d); |
38 | | |
39 | 0 | #define AUTOFIX LW_TRUE |
40 | 0 | #define LWGEOM_GEOS_ERRMSG_MAXSIZE 256 |
41 | | char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]; |
42 | | |
43 | | const char * |
44 | | lwgeom_geos_compiled_version() |
45 | 0 | { |
46 | 0 | static char ver[64]; |
47 | 0 | sprintf( |
48 | 0 | ver, |
49 | 0 | "%d.%d.%d", |
50 | 0 | (POSTGIS_GEOS_VERSION/10000), |
51 | 0 | ((POSTGIS_GEOS_VERSION%10000)/100), |
52 | 0 | ((POSTGIS_GEOS_VERSION)%100) |
53 | 0 | ); |
54 | 0 | return ver; |
55 | 0 | } |
56 | | |
57 | | extern void |
58 | | lwgeom_geos_error(const char* fmt, ...) |
59 | 0 | { |
60 | 0 | va_list ap; |
61 | 0 | va_start(ap, fmt); |
62 | | |
63 | | /* Call the supplied function */ |
64 | 0 | if (LWGEOM_GEOS_ERRMSG_MAXSIZE - 1 < vsnprintf(lwgeom_geos_errmsg, LWGEOM_GEOS_ERRMSG_MAXSIZE - 1, fmt, ap)) |
65 | 0 | lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE - 1] = '\0'; |
66 | |
|
67 | 0 | va_end(ap); |
68 | 0 | } |
69 | | |
70 | | void |
71 | | lwgeom_geos_error_minversion(const char *functionality, const char *minver) |
72 | 0 | { |
73 | 0 | lwerror( |
74 | 0 | "%s requires a build against GEOS-%s or higher," |
75 | 0 | " this version of PostGIS was built against version %s", |
76 | 0 | functionality, minver, lwgeom_geos_compiled_version() |
77 | 0 | ); |
78 | 0 | } |
79 | | |
80 | | /* Destroy any non-null GEOSGeometry* pointers passed as arguments */ |
81 | 0 | #define GEOS_FREE(...) \ |
82 | 0 | do { \ |
83 | 0 | geos_destroy((sizeof((void*[]){__VA_ARGS__})/sizeof(void*)), __VA_ARGS__); \ |
84 | 0 | } while (0) |
85 | | |
86 | | /* Pass the latest GEOS error to lwerror, then return NULL */ |
87 | 0 | #define GEOS_FAIL() \ |
88 | 0 | do { \ |
89 | 0 | lwerror("%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); \ |
90 | 0 | return NULL; \ |
91 | 0 | } while (0) |
92 | | |
93 | | /* Pass the latest GEOS error to lwdebug, then return NULL */ |
94 | | #define GEOS_FAIL_DEBUG() \ |
95 | 0 | do \ |
96 | 0 | { \ |
97 | 0 | lwdebug(1, "%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); \ |
98 | 0 | return NULL; \ |
99 | 0 | } while (0) |
100 | | |
101 | 0 | #define GEOS_FREE_AND_FAIL(...) \ |
102 | 0 | do { \ |
103 | 0 | GEOS_FREE(__VA_ARGS__); \ |
104 | 0 | GEOS_FAIL(); \ |
105 | 0 | } while (0) |
106 | | |
107 | | #define GEOS_FREE_AND_FAIL_DEBUG(...) \ |
108 | 0 | do \ |
109 | 0 | { \ |
110 | 0 | GEOS_FREE(__VA_ARGS__); \ |
111 | 0 | GEOS_FAIL_DEBUG(); \ |
112 | 0 | } while (0) |
113 | | |
114 | | /* Return the consistent SRID of all inputs, or call lwerror |
115 | | * in case of SRID mismatch. */ |
116 | | #define RESULT_SRID(...) \ |
117 | 0 | (get_result_srid((sizeof((const void*[]){__VA_ARGS__})/sizeof(void*)), __func__, __VA_ARGS__)) |
118 | | |
119 | | /* Free any non-null GEOSGeometry* pointers passed as arguments * |
120 | | * Called by GEOS_FREE, which populates 'count' */ |
121 | 0 | static void geos_destroy(size_t count, ...) { |
122 | 0 | va_list ap; |
123 | 0 | va_start(ap, count); |
124 | 0 | while (count--) |
125 | 0 | { |
126 | 0 | GEOSGeometry* g = va_arg(ap, GEOSGeometry*); |
127 | 0 | if (g) |
128 | 0 | { |
129 | 0 | GEOSGeom_destroy(g); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | va_end(ap); |
133 | 0 | } |
134 | | |
135 | | /* |
136 | | ** GEOS <==> PostGIS conversion functions |
137 | | ** |
138 | | ** Default conversion creates a GEOS point array, then iterates through the |
139 | | ** PostGIS points, setting each value in the GEOS array one at a time. |
140 | | ** |
141 | | */ |
142 | | |
143 | | /* Return a POINTARRAY from a GEOSCoordSeq */ |
144 | | POINTARRAY* |
145 | | ptarray_from_GEOSCoordSeq(const GEOSCoordSequence* cs, uint8_t want3d) |
146 | 0 | { |
147 | 0 | uint32_t dims = 2; |
148 | 0 | POINTARRAY* pa; |
149 | 0 | uint32_t size = 0; |
150 | |
|
151 | 0 | LWDEBUG(2, "ptarray_fromGEOSCoordSeq called"); |
152 | |
|
153 | 0 | if (!GEOSCoordSeq_getSize(cs, &size)) lwerror("Exception thrown"); |
154 | |
|
155 | 0 | LWDEBUGF(4, " GEOSCoordSeq size: %d", size); |
156 | |
|
157 | 0 | if (want3d) |
158 | 0 | { |
159 | 0 | if (!GEOSCoordSeq_getDimensions(cs, &dims)) lwerror("Exception thrown"); |
160 | |
|
161 | 0 | LWDEBUGF(4, " GEOSCoordSeq dimensions: %d", dims); |
162 | | |
163 | | /* forget higher dimensions (if any) */ |
164 | 0 | if (dims > 3) dims = 3; |
165 | 0 | } |
166 | |
|
167 | 0 | LWDEBUGF(4, " output dimensions: %d", dims); |
168 | |
|
169 | 0 | pa = ptarray_construct((dims == 3), 0, size); |
170 | 0 | GEOSCoordSeq_copyToBuffer(cs, (double*) pa->serialized_pointlist, (dims == 3), 0); |
171 | 0 | return pa; |
172 | 0 | } |
173 | | |
174 | | /* Return an LWGEOM from a Geometry */ |
175 | | LWGEOM* |
176 | | GEOS2LWGEOM(const GEOSGeometry* geom, uint8_t want3d) |
177 | 0 | { |
178 | 0 | int type = GEOSGeomTypeId(geom); |
179 | 0 | int SRID = GEOSGetSRID(geom); |
180 | | |
181 | | /* GEOS's 0 is equivalent to our unknown as for SRID values */ |
182 | 0 | if (SRID == 0) SRID = SRID_UNKNOWN; |
183 | |
|
184 | 0 | if (want3d && !GEOSHasZ(geom)) |
185 | 0 | { |
186 | 0 | LWDEBUG(3, "Geometry has no Z, won't provide one"); |
187 | 0 | want3d = 0; |
188 | 0 | } |
189 | |
|
190 | 0 | switch (type) |
191 | 0 | { |
192 | 0 | const GEOSCoordSequence* cs; |
193 | 0 | POINTARRAY *pa, **ppaa; |
194 | 0 | const GEOSGeometry* g; |
195 | 0 | LWGEOM** geoms; |
196 | 0 | uint32_t i, ngeoms; |
197 | | |
198 | 0 | case GEOS_POINT: |
199 | 0 | LWDEBUG(4, "lwgeom_from_geometry: it's a Point"); |
200 | 0 | cs = GEOSGeom_getCoordSeq(geom); |
201 | 0 | if (GEOSisEmpty(geom)) return (LWGEOM*)lwpoint_construct_empty(SRID, want3d, 0); |
202 | 0 | pa = ptarray_from_GEOSCoordSeq(cs, want3d); |
203 | 0 | return (LWGEOM*)lwpoint_construct(SRID, NULL, pa); |
204 | | |
205 | 0 | case GEOS_LINESTRING: |
206 | 0 | case GEOS_LINEARRING: |
207 | 0 | LWDEBUG(4, "lwgeom_from_geometry: it's a LineString or LinearRing"); |
208 | 0 | if (GEOSisEmpty(geom)) return (LWGEOM*)lwline_construct_empty(SRID, want3d, 0); |
209 | | |
210 | 0 | cs = GEOSGeom_getCoordSeq(geom); |
211 | 0 | pa = ptarray_from_GEOSCoordSeq(cs, want3d); |
212 | 0 | return (LWGEOM*)lwline_construct(SRID, NULL, pa); |
213 | | |
214 | 0 | case GEOS_POLYGON: |
215 | 0 | LWDEBUG(4, "lwgeom_from_geometry: it's a Polygon"); |
216 | 0 | if (GEOSisEmpty(geom)) return (LWGEOM*)lwpoly_construct_empty(SRID, want3d, 0); |
217 | 0 | ngeoms = GEOSGetNumInteriorRings(geom); |
218 | 0 | ppaa = lwalloc(sizeof(POINTARRAY*) * (ngeoms + 1)); |
219 | 0 | g = GEOSGetExteriorRing(geom); |
220 | 0 | cs = GEOSGeom_getCoordSeq(g); |
221 | 0 | ppaa[0] = ptarray_from_GEOSCoordSeq(cs, want3d); |
222 | 0 | for (i = 0; i < ngeoms; i++) |
223 | 0 | { |
224 | 0 | g = GEOSGetInteriorRingN(geom, i); |
225 | 0 | cs = GEOSGeom_getCoordSeq(g); |
226 | 0 | ppaa[i + 1] = ptarray_from_GEOSCoordSeq(cs, want3d); |
227 | 0 | } |
228 | 0 | return (LWGEOM*)lwpoly_construct(SRID, NULL, ngeoms + 1, ppaa); |
229 | | |
230 | 0 | case GEOS_MULTIPOINT: |
231 | 0 | case GEOS_MULTILINESTRING: |
232 | 0 | case GEOS_MULTIPOLYGON: |
233 | 0 | case GEOS_GEOMETRYCOLLECTION: |
234 | 0 | LWDEBUG(4, "lwgeom_from_geometry: it's a Collection or Multi"); |
235 | |
|
236 | 0 | ngeoms = GEOSGetNumGeometries(geom); |
237 | 0 | geoms = NULL; |
238 | 0 | if (ngeoms) |
239 | 0 | { |
240 | 0 | geoms = lwalloc(sizeof(LWGEOM*) * ngeoms); |
241 | 0 | for (i = 0; i < ngeoms; i++) |
242 | 0 | { |
243 | 0 | g = GEOSGetGeometryN(geom, i); |
244 | 0 | geoms[i] = GEOS2LWGEOM(g, want3d); |
245 | 0 | } |
246 | 0 | } |
247 | 0 | return (LWGEOM*)lwcollection_construct(type, SRID, NULL, ngeoms, geoms); |
248 | | |
249 | 0 | default: |
250 | 0 | lwerror("GEOS2LWGEOM: unknown geometry type: %d", type); |
251 | 0 | return NULL; |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | GEOSCoordSeq ptarray_to_GEOSCoordSeq(const POINTARRAY*, uint8_t fix_ring); |
256 | | |
257 | | GEOSCoordSeq |
258 | | ptarray_to_GEOSCoordSeq(const POINTARRAY* pa, uint8_t fix_ring) |
259 | 0 | { |
260 | 0 | uint32_t dims = 2; |
261 | 0 | uint32_t i; |
262 | 0 | int append_points = 0; |
263 | 0 | const POINT3D *p3d = NULL; |
264 | 0 | const POINT2D* p2d = NULL; |
265 | 0 | GEOSCoordSeq sq; |
266 | |
|
267 | 0 | if (FLAGS_GET_Z(pa->flags)) dims = 3; |
268 | |
|
269 | 0 | if (fix_ring) |
270 | 0 | { |
271 | 0 | if (pa->npoints < 1) |
272 | 0 | { |
273 | 0 | lwerror("ptarray_to_GEOSCoordSeq called with fix_ring and 0 vertices in ring, cannot fix"); |
274 | 0 | return NULL; |
275 | 0 | } |
276 | 0 | else |
277 | 0 | { |
278 | 0 | if (pa->npoints < 4) append_points = 4 - pa->npoints; |
279 | 0 | if (!ptarray_is_closed_2d(pa) && append_points == 0) append_points = 1; |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | 0 | if (append_points == 0) { |
284 | 0 | sq = GEOSCoordSeq_copyFromBuffer((const double*) pa->serialized_pointlist, pa->npoints, FLAGS_GET_Z(pa->flags), FLAGS_GET_M(pa->flags)); |
285 | 0 | if (!sq) |
286 | 0 | { |
287 | 0 | GEOS_FAIL(); |
288 | 0 | } |
289 | 0 | return sq; |
290 | 0 | } |
291 | 0 | if (!(sq = GEOSCoordSeq_create(pa->npoints + append_points, dims))) |
292 | 0 | { |
293 | 0 | GEOS_FAIL(); |
294 | 0 | } |
295 | | |
296 | 0 | for (i = 0; i < pa->npoints; i++) |
297 | 0 | { |
298 | 0 | if (dims == 3) |
299 | 0 | { |
300 | 0 | p3d = getPoint3d_cp(pa, i); |
301 | 0 | p2d = (const POINT2D*)p3d; |
302 | 0 | LWDEBUGF(4, "Point: %g,%g,%g", p3d->x, p3d->y, p3d->z); |
303 | 0 | } |
304 | 0 | else |
305 | 0 | { |
306 | 0 | p2d = getPoint2d_cp(pa, i); |
307 | 0 | LWDEBUGF(4, "Point: %g,%g", p2d->x, p2d->y); |
308 | 0 | } |
309 | |
|
310 | 0 | if (dims == 3) |
311 | 0 | GEOSCoordSeq_setXYZ(sq, i, p2d->x, p2d->y, p3d->z); |
312 | 0 | else |
313 | 0 | GEOSCoordSeq_setXY(sq, i, p2d->x, p2d->y); |
314 | |
|
315 | 0 | } |
316 | |
|
317 | 0 | if (append_points) |
318 | 0 | { |
319 | 0 | if (dims == 3) |
320 | 0 | { |
321 | 0 | p3d = getPoint3d_cp(pa, 0); |
322 | 0 | p2d = (const POINT2D*)p3d; |
323 | 0 | } |
324 | 0 | else |
325 | 0 | p2d = getPoint2d_cp(pa, 0); |
326 | 0 | for (i = pa->npoints; i < pa->npoints + append_points; i++) |
327 | 0 | { |
328 | |
|
329 | 0 | GEOSCoordSeq_setXY(sq, i, p2d->x, p2d->y); |
330 | |
|
331 | 0 | if (dims == 3) GEOSCoordSeq_setZ(sq, i, p3d->z); |
332 | 0 | } |
333 | 0 | } |
334 | |
|
335 | 0 | return sq; |
336 | |
|
337 | 0 | } |
338 | | |
339 | | static inline GEOSGeometry* |
340 | | ptarray_to_GEOSLinearRing(const POINTARRAY* pa, uint8_t autofix) |
341 | 0 | { |
342 | 0 | GEOSCoordSeq sq; |
343 | 0 | GEOSGeom g; |
344 | 0 | sq = ptarray_to_GEOSCoordSeq(pa, autofix); |
345 | 0 | g = GEOSGeom_createLinearRing(sq); |
346 | 0 | return g; |
347 | 0 | } |
348 | | |
349 | | GEOSGeometry* |
350 | | GBOX2GEOS(const GBOX* box) |
351 | 0 | { |
352 | 0 | GEOSGeometry* envelope; |
353 | 0 | GEOSGeometry* ring; |
354 | 0 | GEOSCoordSequence* seq = GEOSCoordSeq_create(5, 2); |
355 | 0 | if (!seq) return NULL; |
356 | | |
357 | 0 | GEOSCoordSeq_setXY(seq, 0, box->xmin, box->ymin); |
358 | 0 | GEOSCoordSeq_setXY(seq, 1, box->xmax, box->ymin); |
359 | 0 | GEOSCoordSeq_setXY(seq, 2, box->xmax, box->ymax); |
360 | 0 | GEOSCoordSeq_setXY(seq, 3, box->xmin, box->ymax); |
361 | 0 | GEOSCoordSeq_setXY(seq, 4, box->xmin, box->ymin); |
362 | |
|
363 | 0 | ring = GEOSGeom_createLinearRing(seq); |
364 | 0 | if (!ring) |
365 | 0 | { |
366 | 0 | GEOSCoordSeq_destroy(seq); |
367 | 0 | return NULL; |
368 | 0 | } |
369 | | |
370 | 0 | envelope = GEOSGeom_createPolygon(ring, NULL, 0); |
371 | 0 | if (!envelope) |
372 | 0 | { |
373 | 0 | GEOSGeom_destroy(ring); |
374 | 0 | return NULL; |
375 | 0 | } |
376 | | |
377 | 0 | return envelope; |
378 | 0 | } |
379 | | |
380 | | GEOSGeometry* |
381 | | LWGEOM2GEOS(const LWGEOM* lwgeom, uint8_t autofix) |
382 | 0 | { |
383 | 0 | GEOSCoordSeq sq; |
384 | 0 | GEOSGeom g, shell; |
385 | 0 | GEOSGeom* geoms = NULL; |
386 | 0 | uint32_t ngeoms, i, j; |
387 | 0 | int is_empty = LW_FALSE; |
388 | | #if LWDEBUG_LEVEL >= 4 |
389 | | char* wkt; |
390 | | #endif |
391 | |
|
392 | 0 | if (autofix) |
393 | 0 | { |
394 | | /* cross fingers and try without autofix, maybe it'll work? */ |
395 | 0 | g = LWGEOM2GEOS(lwgeom, LW_FALSE); |
396 | 0 | if (g) return g; |
397 | 0 | } |
398 | | |
399 | 0 | LWDEBUGF(4, "LWGEOM2GEOS got a %s", lwtype_name(lwgeom->type)); |
400 | |
|
401 | 0 | if (lwgeom_type_arc(lwgeom)) |
402 | 0 | { |
403 | 0 | LWGEOM* lwgeom_stroked = lwgeom_stroke(lwgeom, 32); |
404 | 0 | GEOSGeometry* g = LWGEOM2GEOS(lwgeom_stroked, autofix); |
405 | 0 | lwgeom_free(lwgeom_stroked); |
406 | 0 | return g; |
407 | 0 | } |
408 | | |
409 | 0 | is_empty = lwgeom_is_empty(lwgeom); |
410 | |
|
411 | 0 | switch (lwgeom->type) |
412 | 0 | { |
413 | 0 | case POINTTYPE: |
414 | 0 | { |
415 | 0 | if (is_empty) |
416 | 0 | g = GEOSGeom_createEmptyPoint(); |
417 | 0 | else |
418 | 0 | { |
419 | 0 | LWPOINT* lwp = (LWPOINT*)lwgeom; |
420 | 0 | if (lwgeom_has_z(lwgeom)) |
421 | 0 | { |
422 | 0 | sq = ptarray_to_GEOSCoordSeq(lwp->point, 0); |
423 | 0 | g = GEOSGeom_createPoint(sq); |
424 | 0 | } |
425 | 0 | else |
426 | 0 | { |
427 | 0 | const POINT2D* p = getPoint2d_cp(lwp->point, 0); |
428 | 0 | g = GEOSGeom_createPointFromXY(p->x, p->y); |
429 | 0 | } |
430 | 0 | } |
431 | 0 | if (!g) return NULL; |
432 | 0 | break; |
433 | 0 | } |
434 | | |
435 | 0 | case LINETYPE: |
436 | 0 | { |
437 | 0 | if (is_empty) |
438 | 0 | g = GEOSGeom_createEmptyLineString(); |
439 | 0 | else |
440 | 0 | { |
441 | 0 | LWLINE* lwl = (LWLINE*)lwgeom; |
442 | | /* TODO: if (autofix) */ |
443 | 0 | if (lwl->points->npoints == 1) |
444 | 0 | { |
445 | | /* Duplicate point, to make geos-friendly */ |
446 | 0 | lwl->points = ptarray_addPoint(lwl->points, |
447 | 0 | getPoint_internal(lwl->points, 0), |
448 | 0 | FLAGS_NDIMS(lwl->points->flags), |
449 | 0 | lwl->points->npoints); |
450 | 0 | } |
451 | 0 | sq = ptarray_to_GEOSCoordSeq(lwl->points, 0); |
452 | 0 | g = GEOSGeom_createLineString(sq); |
453 | 0 | } |
454 | 0 | if (!g) return NULL; |
455 | 0 | break; |
456 | 0 | } |
457 | | |
458 | 0 | case POLYGONTYPE: |
459 | 0 | { |
460 | 0 | LWPOLY* lwpoly = (LWPOLY*)lwgeom; |
461 | 0 | if (is_empty) |
462 | 0 | g = GEOSGeom_createEmptyPolygon(); |
463 | 0 | else |
464 | 0 | { |
465 | 0 | shell = ptarray_to_GEOSLinearRing(lwpoly->rings[0], autofix); |
466 | 0 | if (!shell) return NULL; |
467 | 0 | ngeoms = lwpoly->nrings - 1; |
468 | 0 | if (ngeoms > 0) geoms = lwalloc(sizeof(GEOSGeom) * ngeoms); |
469 | |
|
470 | 0 | for (i = 1; i < lwpoly->nrings; i++) |
471 | 0 | { |
472 | 0 | geoms[i - 1] = ptarray_to_GEOSLinearRing(lwpoly->rings[i], autofix); |
473 | 0 | if (!geoms[i - 1]) |
474 | 0 | { |
475 | 0 | uint32_t k; |
476 | 0 | for (k = 0; k < i - 1; k++) |
477 | 0 | GEOSGeom_destroy(geoms[k]); |
478 | 0 | lwfree(geoms); |
479 | 0 | GEOSGeom_destroy(shell); |
480 | 0 | return NULL; |
481 | 0 | } |
482 | 0 | } |
483 | 0 | g = GEOSGeom_createPolygon(shell, geoms, ngeoms); |
484 | 0 | if (geoms) lwfree(geoms); |
485 | 0 | } |
486 | 0 | if (!g) return NULL; |
487 | 0 | break; |
488 | 0 | } |
489 | | |
490 | 0 | case TRIANGLETYPE: |
491 | 0 | { |
492 | 0 | if (is_empty) |
493 | 0 | g = GEOSGeom_createEmptyPolygon(); |
494 | 0 | else |
495 | 0 | { |
496 | 0 | LWTRIANGLE *lwt = (LWTRIANGLE *)lwgeom; |
497 | 0 | shell = ptarray_to_GEOSLinearRing(lwt->points, autofix); |
498 | 0 | if (!shell) return NULL; |
499 | 0 | g = GEOSGeom_createPolygon(shell, NULL, 0); |
500 | 0 | } |
501 | 0 | if (!g) return NULL; |
502 | 0 | break; |
503 | 0 | } |
504 | 0 | case MULTIPOINTTYPE: |
505 | 0 | case MULTILINETYPE: |
506 | 0 | case MULTIPOLYGONTYPE: |
507 | 0 | case TINTYPE: |
508 | 0 | case POLYHEDRALSURFACETYPE: |
509 | 0 | case COLLECTIONTYPE: |
510 | 0 | { |
511 | 0 | int geostype; |
512 | 0 | if (lwgeom->type == MULTIPOINTTYPE) |
513 | 0 | geostype = GEOS_MULTIPOINT; |
514 | 0 | else if (lwgeom->type == MULTILINETYPE) |
515 | 0 | geostype = GEOS_MULTILINESTRING; |
516 | 0 | else if (lwgeom->type == MULTIPOLYGONTYPE) |
517 | 0 | geostype = GEOS_MULTIPOLYGON; |
518 | 0 | else |
519 | 0 | geostype = GEOS_GEOMETRYCOLLECTION; |
520 | |
|
521 | 0 | LWCOLLECTION* lwc = (LWCOLLECTION*)lwgeom; |
522 | |
|
523 | 0 | ngeoms = lwc->ngeoms; |
524 | 0 | if (ngeoms > 0) geoms = lwalloc(sizeof(GEOSGeom) * ngeoms); |
525 | |
|
526 | 0 | j = 0; |
527 | 0 | for (i = 0; i < ngeoms; ++i) |
528 | 0 | { |
529 | 0 | GEOSGeometry* g; |
530 | | |
531 | | /* if (lwgeom_is_empty(lwc->geoms[i])) continue; */ |
532 | |
|
533 | 0 | g = LWGEOM2GEOS(lwc->geoms[i], 0); |
534 | 0 | if (!g) |
535 | 0 | { |
536 | 0 | uint32_t k; |
537 | 0 | for (k = 0; k < j; k++) |
538 | 0 | GEOSGeom_destroy(geoms[k]); |
539 | 0 | lwfree(geoms); |
540 | 0 | return NULL; |
541 | 0 | } |
542 | 0 | geoms[j++] = g; |
543 | 0 | } |
544 | 0 | g = GEOSGeom_createCollection(geostype, geoms, j); |
545 | 0 | if (ngeoms > 0) lwfree(geoms); |
546 | 0 | if (!g) return NULL; |
547 | 0 | break; |
548 | 0 | } |
549 | | |
550 | 0 | default: |
551 | 0 | { |
552 | 0 | lwerror("Unknown geometry type: %d - %s", lwgeom->type, lwtype_name(lwgeom->type)); |
553 | 0 | return NULL; |
554 | 0 | } |
555 | 0 | } |
556 | | |
557 | 0 | GEOSSetSRID(g, lwgeom->srid); |
558 | |
|
559 | | #if LWDEBUG_LEVEL >= 4 |
560 | | wkt = GEOSGeomToWKT(g); |
561 | | LWDEBUGF(4, "LWGEOM2GEOS: GEOSGeom: %s", wkt); |
562 | | GEOSFree(wkt); |
563 | | #endif |
564 | |
|
565 | 0 | return g; |
566 | 0 | } |
567 | | |
568 | | GEOSGeometry* |
569 | | make_geos_point(double x, double y) |
570 | 0 | { |
571 | 0 | GEOSCoordSequence* seq = GEOSCoordSeq_create(1, 2); |
572 | 0 | GEOSGeometry* geom = NULL; |
573 | |
|
574 | 0 | if (!seq) return NULL; |
575 | | |
576 | 0 | GEOSCoordSeq_setXY(seq, 0, x, y); |
577 | |
|
578 | 0 | geom = GEOSGeom_createPoint(seq); |
579 | 0 | if (!geom) GEOSCoordSeq_destroy(seq); |
580 | 0 | return geom; |
581 | 0 | } |
582 | | |
583 | | GEOSGeometry* |
584 | | make_geos_segment(double x1, double y1, double x2, double y2) |
585 | 0 | { |
586 | 0 | GEOSCoordSequence* seq = GEOSCoordSeq_create(2, 2); |
587 | 0 | GEOSGeometry* geom = NULL; |
588 | |
|
589 | 0 | if (!seq) return NULL; |
590 | | |
591 | 0 | GEOSCoordSeq_setXY(seq, 0, x1, y1); |
592 | 0 | GEOSCoordSeq_setXY(seq, 1, x2, y2); |
593 | |
|
594 | 0 | geom = GEOSGeom_createLineString(seq); |
595 | 0 | if (!geom) GEOSCoordSeq_destroy(seq); |
596 | 0 | return geom; |
597 | 0 | } |
598 | | |
599 | | const char* |
600 | | lwgeom_geos_version() |
601 | 0 | { |
602 | 0 | const char* ver = GEOSversion(); |
603 | 0 | return ver; |
604 | 0 | } |
605 | | |
606 | | /* Return the consistent SRID of all input. |
607 | | * Intended to be called from RESULT_SRID macro */ |
608 | | static int32_t |
609 | | get_result_srid(size_t count, const char* funcname, ...) |
610 | 0 | { |
611 | 0 | va_list ap; |
612 | 0 | va_start(ap, funcname); |
613 | 0 | int32_t srid = SRID_INVALID; |
614 | 0 | size_t i; |
615 | 0 | for(i = 0; i < count; i++) |
616 | 0 | { |
617 | 0 | LWGEOM* g = va_arg(ap, LWGEOM*); |
618 | 0 | if (!g) |
619 | 0 | { |
620 | 0 | lwerror("%s: Geometry is null", funcname); |
621 | 0 | va_end(ap); |
622 | 0 | return SRID_INVALID; |
623 | 0 | } |
624 | 0 | if (i == 0) |
625 | 0 | { |
626 | 0 | srid = g->srid; |
627 | 0 | } |
628 | 0 | else |
629 | 0 | { |
630 | 0 | if (g->srid != srid) |
631 | 0 | { |
632 | 0 | lwerror("%s: Operation on mixed SRID geometries (%d != %d)", funcname, srid, g->srid); |
633 | 0 | va_end(ap); |
634 | 0 | return SRID_INVALID; |
635 | 0 | } |
636 | 0 | } |
637 | 0 | } |
638 | 0 | va_end(ap); |
639 | 0 | return srid; |
640 | 0 | } |
641 | | |
642 | | static int |
643 | | lwgeom_geos_normalize_unsupported_type(uint8_t type) |
644 | 0 | { |
645 | 0 | return type == POLYHEDRALSURFACETYPE || type == TINTYPE; |
646 | 0 | } |
647 | | |
648 | | static int |
649 | | lwgeom_geos_normalize_needs_lwgeom(const LWGEOM *geom) |
650 | 0 | { |
651 | 0 | uint32_t i; |
652 | 0 | LWCOLLECTION *col; |
653 | |
|
654 | 0 | if (lwgeom_geos_normalize_unsupported_type(geom->type)) |
655 | 0 | return LW_TRUE; |
656 | | |
657 | 0 | if (!lwtype_is_collection(geom->type)) |
658 | 0 | return LW_FALSE; |
659 | | |
660 | 0 | col = (LWCOLLECTION *)geom; |
661 | 0 | for (i = 0; i < col->ngeoms; i++) |
662 | 0 | { |
663 | 0 | if (lwgeom_geos_normalize_needs_lwgeom(col->geoms[i])) |
664 | 0 | return LW_TRUE; |
665 | 0 | } |
666 | | |
667 | 0 | return LW_FALSE; |
668 | 0 | } |
669 | | |
670 | | static int |
671 | | lwgeom_compare_by_xdr_ewkb(const void *a, const void *b) |
672 | 0 | { |
673 | 0 | const LWGEOM *ga = *(const LWGEOM *const *)a; |
674 | 0 | const LWGEOM *gb = *(const LWGEOM *const *)b; |
675 | 0 | char *wa = lwgeom_to_hexwkb_buffer(ga, WKB_EXTENDED | WKB_XDR); |
676 | 0 | char *wb = lwgeom_to_hexwkb_buffer(gb, WKB_EXTENDED | WKB_XDR); |
677 | 0 | int cmp = strcmp(wa, wb); |
678 | |
|
679 | 0 | lwfree(wa); |
680 | 0 | lwfree(wb); |
681 | 0 | return cmp; |
682 | 0 | } |
683 | | |
684 | | static LWGEOM * |
685 | | lwgeom_normalized_triangle_or_clone(const LWGEOM *triangle_in, LWGEOM *normalized) |
686 | 0 | { |
687 | 0 | LWPOLY *poly; |
688 | 0 | LWTRIANGLE *triangle; |
689 | 0 | POINTARRAY *points; |
690 | |
|
691 | 0 | if (normalized && normalized->type == TRIANGLETYPE) |
692 | 0 | return normalized; |
693 | | |
694 | 0 | if (!normalized || normalized->type != POLYGONTYPE) |
695 | 0 | { |
696 | 0 | lwgeom_free(normalized); |
697 | 0 | return lwgeom_clone_deep(triangle_in); |
698 | 0 | } |
699 | | |
700 | 0 | poly = lwgeom_as_lwpoly(normalized); |
701 | 0 | if (poly->nrings != 1 || !poly->rings[0]) |
702 | 0 | { |
703 | 0 | lwgeom_free(normalized); |
704 | 0 | return lwgeom_clone_deep(triangle_in); |
705 | 0 | } |
706 | | |
707 | 0 | points = ptarray_clone_deep(poly->rings[0]); |
708 | 0 | triangle = lwtriangle_construct(poly->srid, NULL, points); |
709 | 0 | lwgeom_free(normalized); |
710 | 0 | return lwtriangle_as_lwgeom(triangle); |
711 | 0 | } |
712 | | |
713 | | static LWGEOM * |
714 | | lwgeom_normalize_lwgeom(const LWGEOM *geom) |
715 | 0 | { |
716 | 0 | uint32_t i; |
717 | 0 | LWCOLLECTION *col; |
718 | |
|
719 | 0 | if (!lwtype_is_collection(geom->type)) |
720 | 0 | return lwgeom_clone_deep(geom); |
721 | | |
722 | 0 | col = lwcollection_clone_deep((LWCOLLECTION *)geom); |
723 | 0 | for (i = 0; i < col->ngeoms; i++) |
724 | 0 | { |
725 | 0 | LWGEOM *normalized; |
726 | |
|
727 | 0 | normalized = lwgeom_normalize(col->geoms[i]); |
728 | 0 | if (col->type == TINTYPE && col->geoms[i]->type == TRIANGLETYPE) |
729 | 0 | normalized = lwgeom_normalized_triangle_or_clone(col->geoms[i], normalized); |
730 | |
|
731 | 0 | lwgeom_free(col->geoms[i]); |
732 | 0 | col->geoms[i] = normalized; |
733 | 0 | } |
734 | |
|
735 | 0 | if (col->ngeoms > 1) |
736 | 0 | qsort(col->geoms, col->ngeoms, sizeof(LWGEOM *), lwgeom_compare_by_xdr_ewkb); |
737 | |
|
738 | 0 | lwgeom_drop_bbox((LWGEOM *)col); |
739 | 0 | return (LWGEOM *)col; |
740 | 0 | } |
741 | | |
742 | | LWGEOM* |
743 | | lwgeom_normalize(const LWGEOM* geom) |
744 | 0 | { |
745 | 0 | LWGEOM* result; |
746 | 0 | int32_t srid = RESULT_SRID(geom); |
747 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
748 | 0 | GEOSGeometry* g; |
749 | |
|
750 | 0 | if (srid == SRID_INVALID) return NULL; |
751 | | |
752 | 0 | if (lwgeom_geos_normalize_needs_lwgeom(geom)) |
753 | 0 | return lwgeom_normalize_lwgeom(geom); |
754 | | |
755 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
756 | |
|
757 | 0 | if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
758 | | |
759 | 0 | if (GEOSNormalize(g) == -1) GEOS_FREE_AND_FAIL(g); |
760 | 0 | GEOSSetSRID(g, srid); |
761 | |
|
762 | 0 | if (!(result = GEOS2LWGEOM(g, is3d))) GEOS_FREE_AND_FAIL(g); |
763 | | |
764 | 0 | GEOSGeom_destroy(g); |
765 | 0 | return result; |
766 | 0 | } |
767 | | |
768 | | LWGEOM* |
769 | | lwgeom_intersection(const LWGEOM* g1, const LWGEOM* g2) |
770 | 0 | { |
771 | 0 | return lwgeom_intersection_prec(g1, g2, -1.0); |
772 | 0 | } |
773 | | |
774 | | LWGEOM* |
775 | | lwgeom_intersection_prec(const LWGEOM* geom1, const LWGEOM* geom2, double prec) |
776 | 0 | { |
777 | 0 | LWGEOM* result; |
778 | 0 | int32_t srid = RESULT_SRID(geom1, geom2); |
779 | 0 | uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); |
780 | 0 | GEOSGeometry* g1; |
781 | 0 | GEOSGeometry* g2; |
782 | 0 | GEOSGeometry* g3; |
783 | |
|
784 | 0 | if (srid == SRID_INVALID) return NULL; |
785 | | |
786 | | /* A.Intersection(Empty) == Empty */ |
787 | 0 | if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom2); /* match empty type? */ |
788 | | |
789 | | /* Empty.Intersection(A) == Empty */ |
790 | 0 | if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom1); /* match empty type? */ |
791 | | |
792 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
793 | |
|
794 | 0 | if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); |
795 | 0 | if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); |
796 | | |
797 | 0 | if ( prec >= 0) { |
798 | 0 | g3 = GEOSIntersectionPrec(g1, g2, prec); |
799 | 0 | } |
800 | 0 | else |
801 | 0 | { |
802 | 0 | g3 = GEOSIntersection(g1, g2); |
803 | 0 | } |
804 | |
|
805 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1, g2); |
806 | 0 | GEOSSetSRID(g3, srid); |
807 | |
|
808 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3); |
809 | | |
810 | 0 | GEOS_FREE(g1, g2, g3); |
811 | 0 | return result; |
812 | 0 | } |
813 | | |
814 | | LWGEOM* |
815 | | lwgeom_linemerge(const LWGEOM* geom) |
816 | 0 | { |
817 | 0 | return lwgeom_linemerge_directed(geom, LW_FALSE); |
818 | 0 | } |
819 | | |
820 | | LWGEOM* |
821 | | lwgeom_linemerge_directed(const LWGEOM* geom, int directed) |
822 | 0 | { |
823 | 0 | LWGEOM* result; |
824 | 0 | int32_t srid = RESULT_SRID(geom); |
825 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
826 | 0 | GEOSGeometry* g1; |
827 | 0 | GEOSGeometry* g3; |
828 | |
|
829 | 0 | if (srid == SRID_INVALID) return NULL; |
830 | | |
831 | | /* Empty.Linemerge() == Empty */ |
832 | 0 | if (lwgeom_is_empty(geom)) return lwgeom_clone_deep(geom); /* match empty type to linestring? */ |
833 | | |
834 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
835 | |
|
836 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
837 | | |
838 | 0 | if (directed) |
839 | 0 | { |
840 | | #if POSTGIS_GEOS_VERSION < 31100 |
841 | | lwgeom_geos_error_minversion("Directed line merging", "3.11"); |
842 | | GEOS_FREE_AND_FAIL(g1); |
843 | | return NULL; |
844 | | #else |
845 | 0 | g3 = GEOSLineMergeDirected(g1); |
846 | 0 | #endif |
847 | 0 | } |
848 | 0 | else |
849 | 0 | { |
850 | 0 | g3 = GEOSLineMerge(g1); |
851 | 0 | } |
852 | |
|
853 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1); |
854 | 0 | GEOSSetSRID(g3, srid); |
855 | |
|
856 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
857 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
858 | | |
859 | 0 | GEOS_FREE(g1, g3); |
860 | |
|
861 | 0 | return result; |
862 | 0 | } |
863 | | |
864 | | LWGEOM* |
865 | | lwgeom_unaryunion(const LWGEOM* geom) |
866 | 0 | { |
867 | 0 | return lwgeom_unaryunion_prec(geom, -1.0); |
868 | 0 | } |
869 | | |
870 | | LWGEOM* |
871 | | lwgeom_unaryunion_prec(const LWGEOM* geom, double prec) |
872 | 0 | { |
873 | 0 | LWGEOM* result; |
874 | 0 | int32_t srid = RESULT_SRID(geom); |
875 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
876 | 0 | GEOSGeometry* g1; |
877 | 0 | GEOSGeometry* g3; |
878 | |
|
879 | 0 | if (srid == SRID_INVALID) return NULL; |
880 | | |
881 | | /* Empty.UnaryUnion() == Empty */ |
882 | 0 | if (lwgeom_is_empty(geom)) return lwgeom_clone_deep(geom); |
883 | | |
884 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
885 | |
|
886 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
887 | | |
888 | 0 | if ( prec >= 0) { |
889 | 0 | g3 = GEOSUnaryUnionPrec(g1, prec); |
890 | 0 | } |
891 | 0 | else |
892 | 0 | { |
893 | 0 | g3 = GEOSUnaryUnion(g1); |
894 | 0 | } |
895 | |
|
896 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1); |
897 | 0 | GEOSSetSRID(g3, srid); |
898 | |
|
899 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
900 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
901 | | |
902 | 0 | GEOS_FREE(g1, g3); |
903 | |
|
904 | 0 | return result; |
905 | 0 | } |
906 | | |
907 | | LWGEOM* |
908 | | lwgeom_difference(const LWGEOM* geom1, const LWGEOM* geom2) |
909 | 0 | { |
910 | 0 | return lwgeom_difference_prec(geom1, geom2, -1.0); |
911 | 0 | } |
912 | | |
913 | | LWGEOM* |
914 | | lwgeom_difference_prec(const LWGEOM* geom1, const LWGEOM* geom2, double prec) |
915 | 0 | { |
916 | 0 | LWGEOM* result; |
917 | 0 | int32_t srid = RESULT_SRID(geom1, geom2); |
918 | 0 | uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); |
919 | 0 | GEOSGeometry *g1, *g2, *g3; |
920 | |
|
921 | 0 | if (srid == SRID_INVALID) return NULL; |
922 | | |
923 | | /* A.Intersection(Empty) == Empty */ |
924 | 0 | if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1); /* match empty type? */ |
925 | | |
926 | | /* Empty.Intersection(A) == Empty */ |
927 | 0 | if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom1); /* match empty type? */ |
928 | | |
929 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
930 | |
|
931 | 0 | if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); |
932 | 0 | if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); |
933 | | |
934 | 0 | if ( prec >= 0) { |
935 | 0 | g3 = GEOSDifferencePrec(g1, g2, prec); |
936 | 0 | } |
937 | 0 | else |
938 | 0 | { |
939 | 0 | g3 = GEOSDifference(g1, g2); |
940 | 0 | } |
941 | |
|
942 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1, g2); |
943 | 0 | GEOSSetSRID(g3, srid); |
944 | |
|
945 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
946 | 0 | GEOS_FREE_AND_FAIL(g1, g2, g3); |
947 | | |
948 | 0 | GEOS_FREE(g1, g2, g3); |
949 | 0 | return result; |
950 | 0 | } |
951 | | |
952 | | LWGEOM* |
953 | | lwgeom_symdifference(const LWGEOM* geom1, const LWGEOM* geom2) |
954 | 0 | { |
955 | 0 | return lwgeom_symdifference_prec(geom1, geom2, -1.0); |
956 | 0 | } |
957 | | |
958 | | LWGEOM* |
959 | | lwgeom_symdifference_prec(const LWGEOM* geom1, const LWGEOM* geom2, double prec) |
960 | 0 | { |
961 | 0 | LWGEOM* result; |
962 | 0 | int32_t srid = RESULT_SRID(geom1, geom2); |
963 | 0 | uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); |
964 | 0 | GEOSGeometry *g1, *g2, *g3; |
965 | |
|
966 | 0 | if (srid == SRID_INVALID) return NULL; |
967 | | |
968 | | /* A.SymDifference(Empty) == A */ |
969 | 0 | if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1); |
970 | | |
971 | | /* Empty.DymDifference(B) == B */ |
972 | 0 | if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom2); |
973 | | |
974 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
975 | |
|
976 | 0 | if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); |
977 | 0 | if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); |
978 | | |
979 | 0 | if ( prec >= 0) { |
980 | 0 | g3 = GEOSSymDifferencePrec(g1, g2, prec); |
981 | 0 | } |
982 | 0 | else |
983 | 0 | { |
984 | 0 | g3 = GEOSSymDifference(g1, g2); |
985 | 0 | } |
986 | |
|
987 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1, g2); |
988 | 0 | GEOSSetSRID(g3, srid); |
989 | |
|
990 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
991 | 0 | GEOS_FREE_AND_FAIL(g1, g2, g3); |
992 | | |
993 | 0 | GEOS_FREE(g1, g2, g3); |
994 | 0 | return result; |
995 | 0 | } |
996 | | |
997 | | LWGEOM* |
998 | | lwgeom_centroid(const LWGEOM* geom) |
999 | 0 | { |
1000 | 0 | LWGEOM* result; |
1001 | 0 | int32_t srid = RESULT_SRID(geom); |
1002 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
1003 | 0 | GEOSGeometry *g1, *g3; |
1004 | |
|
1005 | 0 | if (srid == SRID_INVALID) return NULL; |
1006 | | |
1007 | 0 | if (lwgeom_is_empty(geom)) |
1008 | 0 | { |
1009 | 0 | LWPOINT* lwp = lwpoint_construct_empty(srid, is3d, lwgeom_has_m(geom)); |
1010 | 0 | return lwpoint_as_lwgeom(lwp); |
1011 | 0 | } |
1012 | | |
1013 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1014 | |
|
1015 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
1016 | | |
1017 | 0 | g3 = GEOSGetCentroid(g1); |
1018 | |
|
1019 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1); |
1020 | 0 | GEOSSetSRID(g3, srid); |
1021 | |
|
1022 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1023 | 0 | GEOS_FREE_AND_FAIL(g1); |
1024 | | |
1025 | 0 | GEOS_FREE(g1, g3); |
1026 | |
|
1027 | 0 | return result; |
1028 | 0 | } |
1029 | | |
1030 | | LWGEOM* |
1031 | | lwgeom_reduceprecision(const LWGEOM* geom, double gridSize) |
1032 | 0 | { |
1033 | 0 | LWGEOM* result; |
1034 | 0 | int32_t srid = RESULT_SRID(geom); |
1035 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
1036 | 0 | GEOSGeometry *g1, *g3; |
1037 | |
|
1038 | 0 | if (srid == SRID_INVALID) return NULL; |
1039 | | |
1040 | 0 | if (lwgeom_is_empty(geom)) |
1041 | 0 | return lwgeom_clone_deep(geom); |
1042 | | |
1043 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1044 | |
|
1045 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
1046 | | |
1047 | 0 | g3 = GEOSGeom_setPrecision(g1, gridSize, 0); |
1048 | |
|
1049 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1); |
1050 | 0 | GEOSSetSRID(g3, srid); |
1051 | |
|
1052 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1053 | 0 | GEOS_FREE_AND_FAIL(g1); |
1054 | | |
1055 | 0 | GEOS_FREE(g1, g3); |
1056 | |
|
1057 | 0 | return result; |
1058 | 0 | } |
1059 | | |
1060 | | LWGEOM * |
1061 | | lwgeom_pointonsurface(const LWGEOM *geom) |
1062 | 0 | { |
1063 | 0 | LWGEOM *result; |
1064 | 0 | LWGEOM *geos_input = NULL; |
1065 | 0 | int32_t srid = RESULT_SRID(geom); |
1066 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
1067 | 0 | GEOSGeometry *g1, *g3; |
1068 | |
|
1069 | 0 | if (srid == SRID_INVALID) return NULL; |
1070 | | |
1071 | 0 | if (lwgeom_is_empty(geom)) |
1072 | 0 | { |
1073 | 0 | LWPOINT *lwp = lwpoint_construct_empty(srid, is3d, lwgeom_has_m(geom)); |
1074 | 0 | return lwpoint_as_lwgeom(lwp); |
1075 | 0 | } |
1076 | | |
1077 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1078 | |
|
1079 | 0 | if (lwgeom_is_collection(geom)) |
1080 | 0 | { |
1081 | 0 | const LWCOLLECTION *col = (const LWCOLLECTION *)geom; |
1082 | 0 | LWCOLLECTION *clean = lwcollection_construct_empty(geom->type, geom->srid, is3d, lwgeom_has_m(geom)); |
1083 | 0 | for (uint32_t i = 0; i < col->ngeoms; i++) |
1084 | 0 | { |
1085 | 0 | if (!lwgeom_is_empty(col->geoms[i])) |
1086 | 0 | lwcollection_add_lwgeom(clean, lwgeom_clone_deep(col->geoms[i])); |
1087 | 0 | } |
1088 | 0 | geos_input = lwcollection_as_lwgeom(clean); |
1089 | 0 | } |
1090 | |
|
1091 | 0 | if (!(g1 = LWGEOM2GEOS(geos_input ? geos_input : geom, AUTOFIX))) |
1092 | 0 | { |
1093 | 0 | lwgeom_free(geos_input); |
1094 | 0 | GEOS_FAIL(); |
1095 | 0 | } |
1096 | | |
1097 | 0 | g3 = GEOSPointOnSurface(g1); |
1098 | |
|
1099 | 0 | if (!g3) |
1100 | 0 | { |
1101 | 0 | lwgeom_free(geos_input); |
1102 | 0 | GEOS_FREE_AND_FAIL(g1); |
1103 | 0 | } |
1104 | 0 | GEOSSetSRID(g3, srid); |
1105 | |
|
1106 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1107 | 0 | { |
1108 | 0 | lwgeom_free(geos_input); |
1109 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
1110 | 0 | } |
1111 | | |
1112 | 0 | GEOS_FREE(g1, g3); |
1113 | 0 | lwgeom_free(geos_input); |
1114 | |
|
1115 | 0 | return result; |
1116 | 0 | } |
1117 | | |
1118 | | LWGEOM* |
1119 | | lwgeom_union(const LWGEOM* g1, const LWGEOM* g2) |
1120 | 0 | { |
1121 | 0 | return lwgeom_union_prec(g1, g2, -1.0); |
1122 | 0 | } |
1123 | | |
1124 | | LWGEOM* |
1125 | | lwgeom_union_prec(const LWGEOM* geom1, const LWGEOM* geom2, double gridSize) |
1126 | 0 | { |
1127 | 0 | LWGEOM* result; |
1128 | 0 | int32_t srid = RESULT_SRID(geom1, geom2); |
1129 | 0 | uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); |
1130 | 0 | GEOSGeometry *g1, *g2, *g3; |
1131 | |
|
1132 | 0 | if (srid == SRID_INVALID) return NULL; |
1133 | | |
1134 | | /* A.Union(empty) == A */ |
1135 | 0 | if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom2); |
1136 | | |
1137 | | /* B.Union(empty) == B */ |
1138 | 0 | if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1); |
1139 | | |
1140 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1141 | |
|
1142 | 0 | if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); |
1143 | 0 | if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); |
1144 | | |
1145 | 0 | if ( gridSize >= 0) { |
1146 | 0 | g3 = GEOSUnionPrec(g1, g2, gridSize); |
1147 | 0 | } |
1148 | 0 | else |
1149 | 0 | { |
1150 | 0 | g3 = GEOSUnion(g1, g2); |
1151 | 0 | } |
1152 | |
|
1153 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1, g2); |
1154 | 0 | GEOSSetSRID(g3, srid); |
1155 | |
|
1156 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1157 | 0 | GEOS_FREE_AND_FAIL(g1, g2, g3); |
1158 | | |
1159 | 0 | GEOS_FREE(g1, g2, g3); |
1160 | 0 | return result; |
1161 | 0 | } |
1162 | | |
1163 | | LWGEOM * |
1164 | | lwgeom_clip_by_rect(const LWGEOM *geom1, double x1, double y1, double x2, double y2) |
1165 | 0 | { |
1166 | 0 | LWGEOM *result; |
1167 | 0 | GEOSGeometry *g1, *g3; |
1168 | 0 | int is3d; |
1169 | | |
1170 | | /* A.Intersection(Empty) == Empty */ |
1171 | 0 | if ( lwgeom_is_empty(geom1) ) |
1172 | 0 | return lwgeom_clone_deep(geom1); |
1173 | | |
1174 | 0 | is3d = FLAGS_GET_Z(geom1->flags); |
1175 | |
|
1176 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1177 | |
|
1178 | 0 | if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) |
1179 | 0 | GEOS_FAIL_DEBUG(); |
1180 | | |
1181 | 0 | if (!(g3 = GEOSClipByRect(g1, x1, y1, x2, y2))) |
1182 | 0 | GEOS_FREE_AND_FAIL_DEBUG(g1); |
1183 | | |
1184 | 0 | GEOS_FREE(g1); |
1185 | 0 | result = GEOS2LWGEOM(g3, is3d); |
1186 | 0 | GEOS_FREE(g3); |
1187 | |
|
1188 | 0 | if (!result) |
1189 | 0 | GEOS_FAIL_DEBUG(); |
1190 | | |
1191 | 0 | result->srid = geom1->srid; |
1192 | |
|
1193 | 0 | return result; |
1194 | 0 | } |
1195 | | |
1196 | | /* ------------ BuildArea stuff ---------------------------------------------------------------------{ */ |
1197 | | LWGEOM* |
1198 | | lwgeom_buildarea(const LWGEOM* geom) |
1199 | 0 | { |
1200 | 0 | LWGEOM* result; |
1201 | 0 | int32_t srid = RESULT_SRID(geom); |
1202 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
1203 | 0 | GEOSGeometry *g1, *g3; |
1204 | |
|
1205 | 0 | if (srid == SRID_INVALID) return NULL; |
1206 | | |
1207 | | /* Can't build an area from an empty! */ |
1208 | 0 | if (lwgeom_is_empty(geom)) return (LWGEOM*)lwpoly_construct_empty(srid, is3d, 0); |
1209 | | |
1210 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1211 | |
|
1212 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
1213 | | |
1214 | 0 | g3 = GEOSBuildArea(g1); |
1215 | |
|
1216 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1); |
1217 | 0 | GEOSSetSRID(g3, srid); |
1218 | | |
1219 | | /* If no geometries are in result collection, return NULL */ |
1220 | 0 | if (GEOSGetNumGeometries(g3) == 0) |
1221 | 0 | { |
1222 | 0 | GEOS_FREE(g1, g3); |
1223 | 0 | return NULL; |
1224 | 0 | } |
1225 | | |
1226 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1227 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
1228 | | |
1229 | 0 | GEOS_FREE(g1, g3); |
1230 | |
|
1231 | 0 | return result; |
1232 | 0 | } |
1233 | | |
1234 | | /* ------------ end of BuildArea stuff ---------------------------------------------------------------------} */ |
1235 | | |
1236 | | int |
1237 | | lwgeom_is_simple(const LWGEOM* geom) |
1238 | 0 | { |
1239 | 0 | GEOSGeometry* g; |
1240 | 0 | int simple; |
1241 | | |
1242 | | /* Empty is always simple */ |
1243 | 0 | if (lwgeom_is_empty(geom)) return LW_TRUE; |
1244 | | |
1245 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1246 | |
|
1247 | 0 | if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) return -1; |
1248 | | |
1249 | 0 | simple = GEOSisSimple(g); |
1250 | 0 | GEOSGeom_destroy(g); |
1251 | |
|
1252 | 0 | if (simple == 2) /* exception thrown */ |
1253 | 0 | { |
1254 | 0 | lwerror("lwgeom_is_simple: %s", lwgeom_geos_errmsg); |
1255 | 0 | return -1; |
1256 | 0 | } |
1257 | | |
1258 | 0 | return simple ? LW_TRUE : LW_FALSE; |
1259 | 0 | } |
1260 | | |
1261 | | LWGEOM* |
1262 | | lwgeom_geos_noop(const LWGEOM* geom) |
1263 | 0 | { |
1264 | 0 | LWGEOM* result; |
1265 | 0 | int32_t srid = RESULT_SRID(geom); |
1266 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
1267 | 0 | GEOSGeometry* g; |
1268 | |
|
1269 | 0 | if (srid == SRID_INVALID) return NULL; |
1270 | | |
1271 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1272 | |
|
1273 | 0 | if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
1274 | | |
1275 | 0 | if (!g) GEOS_FREE_AND_FAIL(g); |
1276 | 0 | GEOSSetSRID(g, srid); |
1277 | |
|
1278 | 0 | if (!(result = GEOS2LWGEOM(g, is3d))) |
1279 | 0 | GEOS_FREE_AND_FAIL(g); |
1280 | | |
1281 | 0 | GEOS_FREE(g); |
1282 | |
|
1283 | 0 | return result; |
1284 | 0 | } |
1285 | | |
1286 | | LWGEOM* |
1287 | | lwgeom_snap(const LWGEOM* geom1, const LWGEOM* geom2, double tolerance) |
1288 | 0 | { |
1289 | 0 | LWGEOM* result; |
1290 | 0 | int32_t srid = RESULT_SRID(geom1, geom2); |
1291 | 0 | uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); |
1292 | 0 | GEOSGeometry *g1, *g2, *g3; |
1293 | |
|
1294 | 0 | if (srid == SRID_INVALID) return NULL; |
1295 | | |
1296 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1297 | |
|
1298 | 0 | if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); |
1299 | 0 | if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); |
1300 | | |
1301 | 0 | g3 = GEOSSnap(g1, g2, tolerance); |
1302 | |
|
1303 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1, g2); |
1304 | 0 | GEOSSetSRID(g3, srid); |
1305 | |
|
1306 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1307 | 0 | GEOS_FREE_AND_FAIL(g1, g2, g3); |
1308 | | |
1309 | 0 | GEOS_FREE(g1, g2, g3); |
1310 | 0 | return result; |
1311 | 0 | } |
1312 | | |
1313 | | LWGEOM* |
1314 | | lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2) |
1315 | 0 | { |
1316 | 0 | LWGEOM* result; |
1317 | 0 | int32_t srid = RESULT_SRID(geom1, geom2); |
1318 | 0 | uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)); |
1319 | 0 | GEOSGeometry *g1, *g2, *g3; |
1320 | |
|
1321 | 0 | if (srid == SRID_INVALID) return NULL; |
1322 | | |
1323 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1324 | |
|
1325 | 0 | if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL(); |
1326 | 0 | if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1); |
1327 | | |
1328 | 0 | g3 = GEOSSharedPaths(g1, g2); |
1329 | |
|
1330 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1, g2); |
1331 | 0 | GEOSSetSRID(g3, srid); |
1332 | |
|
1333 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1334 | 0 | GEOS_FREE_AND_FAIL(g1, g2, g3); |
1335 | | |
1336 | 0 | GEOS_FREE(g1, g2, g3); |
1337 | 0 | return result; |
1338 | 0 | } |
1339 | | |
1340 | | static LWGEOM * |
1341 | | lwline_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit) |
1342 | 0 | { |
1343 | 0 | LWGEOM* result; |
1344 | 0 | LWGEOM* geom = lwline_as_lwgeom(lwline); |
1345 | 0 | int32_t srid = RESULT_SRID(geom); |
1346 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
1347 | 0 | GEOSGeometry *g1, *g3; |
1348 | |
|
1349 | 0 | if (srid == SRID_INVALID) return NULL; |
1350 | | |
1351 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1352 | |
|
1353 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
1354 | | |
1355 | 0 | g3 = GEOSOffsetCurve(g1, size, quadsegs, joinStyle, mitreLimit); |
1356 | |
|
1357 | 0 | if (!g3) |
1358 | 0 | { |
1359 | 0 | GEOS_FREE(g1); |
1360 | 0 | return NULL; |
1361 | 0 | } |
1362 | | |
1363 | 0 | GEOSSetSRID(g3, srid); |
1364 | |
|
1365 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1366 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
1367 | | |
1368 | 0 | GEOS_FREE(g1, g3); |
1369 | 0 | return result; |
1370 | 0 | } |
1371 | | |
1372 | | static LWGEOM * |
1373 | | lwcollection_offsetcurve(const LWCOLLECTION *col, double size, int quadsegs, int joinStyle, double mitreLimit) |
1374 | 0 | { |
1375 | 0 | const LWGEOM *geom = lwcollection_as_lwgeom(col); |
1376 | 0 | int32_t srid = RESULT_SRID(geom); |
1377 | 0 | uint8_t is3d = FLAGS_GET_Z(col->flags); |
1378 | 0 | LWCOLLECTION *result; |
1379 | 0 | LWGEOM *tmp; |
1380 | 0 | uint32_t i; |
1381 | 0 | if (srid == SRID_INVALID) return NULL; |
1382 | | |
1383 | 0 | result = lwcollection_construct_empty(MULTILINETYPE, srid, is3d, LW_FALSE); |
1384 | |
|
1385 | 0 | for (i = 0; i < col->ngeoms; i++) |
1386 | 0 | { |
1387 | 0 | tmp = lwgeom_offsetcurve(col->geoms[i], size, quadsegs, joinStyle, mitreLimit); |
1388 | |
|
1389 | 0 | if (!tmp) |
1390 | 0 | { |
1391 | 0 | lwcollection_free(result); |
1392 | 0 | return NULL; |
1393 | 0 | } |
1394 | | |
1395 | 0 | if (!lwgeom_is_empty(tmp)) |
1396 | 0 | { |
1397 | 0 | if (lwgeom_is_collection(tmp)) |
1398 | 0 | result = lwcollection_concat_in_place(result, lwgeom_as_lwcollection(tmp)); |
1399 | 0 | else |
1400 | 0 | result = lwcollection_add_lwgeom(result, tmp); |
1401 | |
|
1402 | 0 | if (!result) |
1403 | 0 | { |
1404 | 0 | lwgeom_free(tmp); |
1405 | 0 | return NULL; |
1406 | 0 | } |
1407 | 0 | } |
1408 | 0 | } |
1409 | | |
1410 | 0 | if (result->ngeoms == 1) |
1411 | 0 | { |
1412 | 0 | tmp = result->geoms[0]; |
1413 | 0 | lwcollection_release(result); |
1414 | 0 | return tmp; |
1415 | 0 | } |
1416 | 0 | else |
1417 | 0 | return lwcollection_as_lwgeom(result); |
1418 | 0 | } |
1419 | | |
1420 | | LWGEOM* |
1421 | | lwgeom_offsetcurve(const LWGEOM* geom, double size, int quadsegs, int joinStyle, double mitreLimit) |
1422 | 0 | { |
1423 | 0 | int32_t srid = RESULT_SRID(geom); |
1424 | 0 | LWGEOM *result = NULL; |
1425 | 0 | LWGEOM *noded = NULL; |
1426 | 0 | if (srid == SRID_INVALID) return NULL; |
1427 | | |
1428 | 0 | if (lwgeom_dimension(geom) != 1) |
1429 | 0 | { |
1430 | 0 | lwerror("%s: input is not linear (type %s)", __func__, lwtype_name(geom->type)); |
1431 | 0 | return NULL; |
1432 | 0 | } |
1433 | | |
1434 | 0 | while (!result) |
1435 | 0 | { |
1436 | 0 | switch (geom->type) |
1437 | 0 | { |
1438 | 0 | case LINETYPE: |
1439 | 0 | result = lwline_offsetcurve(lwgeom_as_lwline(geom), size, quadsegs, joinStyle, mitreLimit); |
1440 | 0 | break; |
1441 | 0 | case COLLECTIONTYPE: |
1442 | 0 | case MULTILINETYPE: |
1443 | 0 | result = lwcollection_offsetcurve(lwgeom_as_lwcollection(geom), size, quadsegs, joinStyle, mitreLimit); |
1444 | 0 | break; |
1445 | 0 | default: |
1446 | 0 | lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type)); |
1447 | 0 | return NULL; |
1448 | 0 | } |
1449 | | |
1450 | 0 | if (result) |
1451 | 0 | { |
1452 | 0 | if (noded) lwgeom_free(noded); |
1453 | 0 | return result; |
1454 | 0 | } |
1455 | 0 | else if (!noded) |
1456 | 0 | { |
1457 | 0 | noded = lwgeom_node(geom); |
1458 | 0 | if (!noded) |
1459 | 0 | { |
1460 | 0 | lwerror("lwgeom_offsetcurve: cannot node input"); |
1461 | 0 | return NULL; |
1462 | 0 | } |
1463 | 0 | geom = noded; |
1464 | 0 | } |
1465 | 0 | else |
1466 | 0 | { |
1467 | 0 | lwgeom_free(noded); |
1468 | 0 | lwerror("lwgeom_offsetcurve: noded geometry cannot be offset"); |
1469 | 0 | return NULL; |
1470 | 0 | } |
1471 | 0 | } |
1472 | | |
1473 | 0 | return result; |
1474 | 0 | } |
1475 | | |
1476 | | static GEOSGeometry* |
1477 | | lwpoly_to_points_make_cell(double xmin, double ymin, double cell_size) |
1478 | 0 | { |
1479 | 0 | GEOSCoordSequence *cs = GEOSCoordSeq_create(5, 2); |
1480 | 0 | GEOSCoordSeq_setXY(cs, 0, xmin, ymin); |
1481 | 0 | GEOSCoordSeq_setXY(cs, 1, xmin + cell_size, ymin); |
1482 | 0 | GEOSCoordSeq_setXY(cs, 2, xmin + cell_size, ymin + cell_size); |
1483 | 0 | GEOSCoordSeq_setXY(cs, 3, xmin, ymin + cell_size); |
1484 | 0 | GEOSCoordSeq_setXY(cs, 4, xmin, ymin); |
1485 | 0 | return GEOSGeom_createPolygon(GEOSGeom_createLinearRing(cs), NULL, 0); |
1486 | 0 | } |
1487 | | |
1488 | | LWMPOINT* |
1489 | | lwpoly_to_points(const LWPOLY* lwpoly, uint32_t npoints, int32_t seed) |
1490 | 0 | { |
1491 | |
|
1492 | 0 | typedef struct CellCorner { |
1493 | 0 | double x; |
1494 | 0 | double y; |
1495 | 0 | } CellCorner; |
1496 | |
|
1497 | 0 | CellCorner* cells; |
1498 | 0 | uint32_t num_cells = 0; |
1499 | |
|
1500 | 0 | double area, bbox_area, bbox_width, bbox_height, area_ratio; |
1501 | 0 | GBOX bbox; |
1502 | 0 | const LWGEOM* lwgeom = (LWGEOM*)lwpoly; |
1503 | 0 | uint32_t sample_npoints, sample_sqrt, sample_width, sample_height; |
1504 | 0 | double sample_cell_size; |
1505 | 0 | uint32_t i, j, n; |
1506 | 0 | uint32_t iterations = 0; |
1507 | 0 | uint32_t npoints_generated = 0; |
1508 | 0 | uint32_t npoints_tested = 0; |
1509 | 0 | GEOSGeometry* g; |
1510 | 0 | const GEOSPreparedGeometry* gprep; |
1511 | 0 | LWMPOINT* mpt; |
1512 | 0 | int32_t srid = lwgeom_get_srid(lwgeom); |
1513 | 0 | int done = 0; |
1514 | |
|
1515 | 0 | if (lwgeom_get_type(lwgeom) != POLYGONTYPE) |
1516 | 0 | { |
1517 | 0 | lwerror("%s: only polygons supported", __func__); |
1518 | 0 | return NULL; |
1519 | 0 | } |
1520 | | |
1521 | 0 | if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL; |
1522 | | |
1523 | 0 | if (!lwpoly->bbox) |
1524 | 0 | lwgeom_calculate_gbox(lwgeom, &bbox); |
1525 | 0 | else |
1526 | 0 | bbox = *(lwpoly->bbox); |
1527 | |
|
1528 | 0 | area = lwpoly_area(lwpoly); |
1529 | 0 | bbox_width = bbox.xmax - bbox.xmin; |
1530 | 0 | bbox_height = bbox.ymax - bbox.ymin; |
1531 | 0 | bbox_area = bbox_width * bbox_height; |
1532 | |
|
1533 | 0 | if (area == 0.0 || bbox_area == 0.0) |
1534 | 0 | { |
1535 | 0 | lwerror("%s: zero area input polygon, TBD", __func__); |
1536 | 0 | return NULL; |
1537 | 0 | } |
1538 | | |
1539 | | /* Gross up our test set a bit to increase odds of getting coverage |
1540 | | * in one pass. For narrow inputs, it is possible we will get |
1541 | | * no hits at all. A fall-back approach might use a random stab-line |
1542 | | * to find pairs of edges that bound interior area, and generate a point |
1543 | | * between those edges, in the case that this gridding system |
1544 | | * does not generate the desired number of points */ |
1545 | 0 | area_ratio = FP_MIN(bbox_area / area, 10000.0); |
1546 | 0 | sample_npoints = npoints * area_ratio; |
1547 | | |
1548 | | /* We're going to generate points using a sample grid as described |
1549 | | * http://lin-ear-th-inking.blogspot.ca/2010/05/more-random-points-in-jts.html |
1550 | | * to try and get a more uniform "random" set of points. So we have to figure |
1551 | | * out how to stick a grid into our box */ |
1552 | 0 | sample_sqrt = ceil(sqrt(sample_npoints)); |
1553 | | |
1554 | | /* Calculate the grids we're going to randomize within */ |
1555 | 0 | if (bbox_width > bbox_height) |
1556 | 0 | { |
1557 | 0 | sample_width = sample_sqrt; |
1558 | 0 | sample_height = ceil((double)sample_npoints / (double)sample_width); |
1559 | 0 | sample_cell_size = bbox_width / sample_width; |
1560 | 0 | } |
1561 | 0 | else |
1562 | 0 | { |
1563 | 0 | sample_height = sample_sqrt; |
1564 | 0 | sample_width = ceil((double)sample_npoints / (double)sample_height); |
1565 | 0 | sample_cell_size = bbox_height / sample_height; |
1566 | 0 | } |
1567 | | |
1568 | | /* Prepare the polygon for fast true/false testing */ |
1569 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1570 | 0 | g = (GEOSGeometry*)LWGEOM2GEOS(lwgeom, 0); |
1571 | 0 | if (!g) |
1572 | 0 | { |
1573 | 0 | lwerror("%s: Geometry could not be converted to GEOS: %s", __func__, lwgeom_geos_errmsg); |
1574 | 0 | return NULL; |
1575 | 0 | } |
1576 | 0 | gprep = GEOSPrepare(g); |
1577 | | |
1578 | | /* START_PROFILING(); */ |
1579 | | |
1580 | | /* Now we fill in an array of cells, only using cells that actually |
1581 | | * intersect the geometry of interest, and finally weshuffle that array, |
1582 | | * so we can visit the cells in random order to avoid visual ugliness |
1583 | | * caused by visiting them sequentially */ |
1584 | 0 | cells = lwalloc(sizeof(CellCorner) * sample_height * sample_width); |
1585 | |
|
1586 | 0 | for (i = 0; i < sample_width; i++) |
1587 | 0 | { |
1588 | 0 | for (j = 0; j < sample_height; j++) |
1589 | 0 | { |
1590 | 0 | int intersects = 0; |
1591 | 0 | double xmin = bbox.xmin + i * sample_cell_size; |
1592 | 0 | double ymin = bbox.ymin + j * sample_cell_size; |
1593 | 0 | GEOSGeometry *gcell = lwpoly_to_points_make_cell(xmin, ymin, sample_cell_size); |
1594 | 0 | intersects = GEOSPreparedIntersects(gprep, gcell); |
1595 | 0 | GEOSGeom_destroy(gcell); |
1596 | 0 | if (intersects == 2) |
1597 | 0 | { |
1598 | 0 | GEOSPreparedGeom_destroy(gprep); |
1599 | 0 | GEOSGeom_destroy(g); |
1600 | 0 | lwerror("%s: GEOS exception on GEOSPreparedIntersects: %s", __func__, lwgeom_geos_errmsg); |
1601 | 0 | return NULL; |
1602 | 0 | } |
1603 | 0 | if (intersects == 1) |
1604 | 0 | { |
1605 | 0 | cells[num_cells].x = xmin; |
1606 | 0 | cells[num_cells].y = ymin; |
1607 | 0 | num_cells++; |
1608 | 0 | } |
1609 | 0 | } |
1610 | 0 | } |
1611 | | |
1612 | | /* STOP_PROFILING(CellGeneration); */ |
1613 | | |
1614 | | /* Initiate random number generator. |
1615 | | * Repeatable numbers are generated with seed values >= 1. |
1616 | | * When seed is zero and has not previously been set, it is based on |
1617 | | * Unix time (seconds) and process ID. */ |
1618 | 0 | lwrandom_set_seed(seed); |
1619 | | |
1620 | | /* Fisher-Yates shuffle */ |
1621 | 0 | n = num_cells; |
1622 | 0 | if (n > 1) |
1623 | 0 | { |
1624 | 0 | for (i = n - 1; i > 0; i--) |
1625 | 0 | { |
1626 | 0 | CellCorner tmp; |
1627 | 0 | j = (uint32_t)(lwrandom_uniform() * (i + 1)); |
1628 | 0 | memcpy( &tmp, cells + j, sizeof(CellCorner)); |
1629 | 0 | memcpy(cells + j, cells + i, sizeof(CellCorner)); |
1630 | 0 | memcpy(cells + i, &tmp, sizeof(CellCorner)); |
1631 | 0 | } |
1632 | 0 | } |
1633 | | |
1634 | | /* Get an empty array of points ready to return */ |
1635 | 0 | mpt = lwmpoint_construct_empty(srid, 0, 0); |
1636 | | |
1637 | | /* Start testing points */ |
1638 | 0 | while (npoints_generated < npoints) |
1639 | 0 | { |
1640 | 0 | iterations++; |
1641 | 0 | for (i = 0; i < num_cells; i++) |
1642 | 0 | { |
1643 | 0 | int contains = 0; |
1644 | 0 | double y = cells[i].y; |
1645 | 0 | double x = cells[i].x; |
1646 | 0 | x += lwrandom_uniform() * sample_cell_size; |
1647 | 0 | y += lwrandom_uniform() * sample_cell_size; |
1648 | 0 | if (x >= bbox.xmax || y >= bbox.ymax) continue; |
1649 | | |
1650 | 0 | #if POSTGIS_GEOS_VERSION >= 31200 |
1651 | 0 | contains = GEOSPreparedIntersectsXY(gprep, x, y); |
1652 | | #else |
1653 | | { |
1654 | | GEOSGeometry *gpt; |
1655 | | GEOSCoordSequence *gseq = GEOSCoordSeq_create(1, 2);; |
1656 | | GEOSCoordSeq_setXY(gseq, 0, x, y); |
1657 | | gpt = GEOSGeom_createPoint(gseq); |
1658 | | contains = GEOSPreparedIntersects(gprep, gpt); |
1659 | | GEOSGeom_destroy(gpt); |
1660 | | } |
1661 | | #endif |
1662 | 0 | if (contains == 2) |
1663 | 0 | { |
1664 | 0 | GEOSPreparedGeom_destroy(gprep); |
1665 | 0 | GEOSGeom_destroy(g); |
1666 | 0 | lwerror("%s: GEOS exception on GEOSPreparedIntersects: %s", __func__, lwgeom_geos_errmsg); |
1667 | 0 | return NULL; |
1668 | 0 | } |
1669 | 0 | if (contains == 1) |
1670 | 0 | { |
1671 | 0 | mpt = lwmpoint_add_lwpoint(mpt, lwpoint_make2d(srid, x, y)); |
1672 | 0 | npoints_generated++; |
1673 | 0 | if (npoints_generated == npoints) |
1674 | 0 | { |
1675 | 0 | done = 1; |
1676 | 0 | break; |
1677 | 0 | } |
1678 | 0 | } |
1679 | | |
1680 | | /* Short-circuit check for ctrl-c occasionally */ |
1681 | 0 | npoints_tested++; |
1682 | 0 | if (npoints_tested % 10000 == 0) |
1683 | 0 | LW_ON_INTERRUPT(GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); return NULL); |
1684 | |
|
1685 | 0 | if (done) break; |
1686 | 0 | } |
1687 | 0 | if (done || iterations > 100) break; |
1688 | 0 | } |
1689 | | |
1690 | 0 | GEOSPreparedGeom_destroy(gprep); |
1691 | 0 | GEOSGeom_destroy(g); |
1692 | 0 | lwfree(cells); |
1693 | |
|
1694 | 0 | return mpt; |
1695 | 0 | } |
1696 | | |
1697 | | /* Allocate points to sub-geometries by area, then call lwgeom_poly_to_points and bundle up final result in a single |
1698 | | * multipoint. */ |
1699 | | LWMPOINT* |
1700 | | lwmpoly_to_points(const LWMPOLY* lwmpoly, uint32_t npoints, int32_t seed) |
1701 | 0 | { |
1702 | 0 | const LWGEOM* lwgeom = (LWGEOM*)lwmpoly; |
1703 | 0 | double area; |
1704 | 0 | uint32_t i; |
1705 | 0 | LWMPOINT* mpt = NULL; |
1706 | |
|
1707 | 0 | if (lwgeom_get_type(lwgeom) != MULTIPOLYGONTYPE) |
1708 | 0 | { |
1709 | 0 | lwerror("%s: only multipolygons supported", __func__); |
1710 | 0 | return NULL; |
1711 | 0 | } |
1712 | 0 | if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL; |
1713 | | |
1714 | 0 | area = lwgeom_area(lwgeom); |
1715 | |
|
1716 | 0 | for (i = 0; i < lwmpoly->ngeoms; i++) |
1717 | 0 | { |
1718 | 0 | double sub_area = lwpoly_area(lwmpoly->geoms[i]); |
1719 | 0 | int sub_npoints = lround(npoints * sub_area / area); |
1720 | 0 | if (sub_npoints > 0) |
1721 | 0 | { |
1722 | 0 | LWMPOINT* sub_mpt = lwpoly_to_points(lwmpoly->geoms[i], sub_npoints, seed); |
1723 | 0 | if (!mpt) |
1724 | 0 | mpt = sub_mpt; |
1725 | 0 | else |
1726 | 0 | { |
1727 | 0 | uint32_t j; |
1728 | 0 | for (j = 0; j < sub_mpt->ngeoms; j++) |
1729 | 0 | mpt = lwmpoint_add_lwpoint(mpt, sub_mpt->geoms[j]); |
1730 | | /* Just free the shell, leave the underlying lwpoints alone, as they are now owned by |
1731 | | * the returning multipoint */ |
1732 | 0 | lwfree(sub_mpt->geoms); |
1733 | 0 | lwgeom_release((LWGEOM*)sub_mpt); |
1734 | 0 | } |
1735 | 0 | } |
1736 | 0 | } |
1737 | 0 | return mpt; |
1738 | 0 | } |
1739 | | |
1740 | | LWMPOINT* |
1741 | | lwgeom_to_points(const LWGEOM* lwgeom, uint32_t npoints, int32_t seed) |
1742 | 0 | { |
1743 | 0 | switch (lwgeom_get_type(lwgeom)) |
1744 | 0 | { |
1745 | 0 | case MULTIPOLYGONTYPE: |
1746 | 0 | return lwmpoly_to_points((LWMPOLY*)lwgeom, npoints, seed); |
1747 | 0 | case POLYGONTYPE: |
1748 | 0 | return lwpoly_to_points((LWPOLY*)lwgeom, npoints, seed); |
1749 | 0 | default: |
1750 | 0 | lwerror("%s: unsupported geometry type '%s'", __func__, lwtype_name(lwgeom_get_type(lwgeom))); |
1751 | 0 | return NULL; |
1752 | 0 | } |
1753 | 0 | } |
1754 | | |
1755 | | LWTIN* |
1756 | | lwtin_from_geos(const GEOSGeometry* geom, uint8_t want3d) |
1757 | 0 | { |
1758 | 0 | int type = GEOSGeomTypeId(geom); |
1759 | 0 | int SRID = GEOSGetSRID(geom); |
1760 | | |
1761 | | /* GEOS's 0 is equivalent to our unknown as for SRID values */ |
1762 | 0 | if (SRID == 0) SRID = SRID_UNKNOWN; |
1763 | |
|
1764 | 0 | if (want3d && !GEOSHasZ(geom)) |
1765 | 0 | { |
1766 | 0 | LWDEBUG(3, "Geometry has no Z, won't provide one"); |
1767 | 0 | want3d = 0; |
1768 | 0 | } |
1769 | |
|
1770 | 0 | switch (type) |
1771 | 0 | { |
1772 | 0 | LWTRIANGLE** geoms; |
1773 | 0 | uint32_t i, ngeoms; |
1774 | 0 | case GEOS_GEOMETRYCOLLECTION: |
1775 | 0 | LWDEBUG(4, "lwgeom_from_geometry: it's a Collection or Multi"); |
1776 | |
|
1777 | 0 | ngeoms = GEOSGetNumGeometries(geom); |
1778 | 0 | geoms = NULL; |
1779 | 0 | if (ngeoms) |
1780 | 0 | { |
1781 | 0 | geoms = lwalloc(ngeoms * sizeof *geoms); |
1782 | 0 | if (!geoms) |
1783 | 0 | { |
1784 | 0 | lwerror("lwtin_from_geos: can't allocate geoms"); |
1785 | 0 | return NULL; |
1786 | 0 | } |
1787 | 0 | for (i = 0; i < ngeoms; i++) |
1788 | 0 | { |
1789 | 0 | const GEOSGeometry *poly, *ring; |
1790 | 0 | const GEOSCoordSequence* cs; |
1791 | 0 | POINTARRAY* pa; |
1792 | |
|
1793 | 0 | poly = GEOSGetGeometryN(geom, i); |
1794 | 0 | ring = GEOSGetExteriorRing(poly); |
1795 | 0 | cs = GEOSGeom_getCoordSeq(ring); |
1796 | 0 | pa = ptarray_from_GEOSCoordSeq(cs, want3d); |
1797 | |
|
1798 | 0 | geoms[i] = lwtriangle_construct(SRID, NULL, pa); |
1799 | 0 | } |
1800 | 0 | } |
1801 | 0 | return (LWTIN*)lwcollection_construct(TINTYPE, SRID, NULL, ngeoms, (LWGEOM**)geoms); |
1802 | 0 | case GEOS_POLYGON: |
1803 | 0 | case GEOS_MULTIPOINT: |
1804 | 0 | case GEOS_MULTILINESTRING: |
1805 | 0 | case GEOS_MULTIPOLYGON: |
1806 | 0 | case GEOS_LINESTRING: |
1807 | 0 | case GEOS_LINEARRING: |
1808 | 0 | case GEOS_POINT: |
1809 | 0 | lwerror("lwtin_from_geos: invalid geometry type for tin: %d", type); |
1810 | 0 | break; |
1811 | | |
1812 | 0 | default: |
1813 | 0 | lwerror("GEOS2LWGEOM: unknown geometry type: %d", type); |
1814 | 0 | return NULL; |
1815 | 0 | } |
1816 | | |
1817 | | /* shouldn't get here */ |
1818 | 0 | return NULL; |
1819 | 0 | } |
1820 | | /* |
1821 | | * output = 1 for edges, 2 for TIN, 0 for polygons |
1822 | | */ |
1823 | | LWGEOM* |
1824 | | lwgeom_delaunay_triangulation(const LWGEOM* geom, double tolerance, int32_t output) |
1825 | 0 | { |
1826 | 0 | LWGEOM* result; |
1827 | 0 | int32_t srid = RESULT_SRID(geom); |
1828 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
1829 | 0 | GEOSGeometry *g1, *g3; |
1830 | |
|
1831 | 0 | if (output < 0 || output > 2) |
1832 | 0 | { |
1833 | 0 | lwerror("%s: invalid output type specified %d", __func__, output); |
1834 | 0 | return NULL; |
1835 | 0 | } |
1836 | | |
1837 | 0 | if (srid == SRID_INVALID) return NULL; |
1838 | | |
1839 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1840 | |
|
1841 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
1842 | | |
1843 | | /* if output != 1 we want polys */ |
1844 | 0 | g3 = GEOSDelaunayTriangulation(g1, tolerance, output == 1); |
1845 | |
|
1846 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1); |
1847 | 0 | GEOSSetSRID(g3, srid); |
1848 | |
|
1849 | 0 | if (output == 2) |
1850 | 0 | { |
1851 | 0 | result = (LWGEOM*)lwtin_from_geos(g3, is3d); |
1852 | 0 | if (!result) |
1853 | 0 | { |
1854 | 0 | GEOS_FREE(g1, g3); |
1855 | 0 | lwerror("%s: cannot convert output geometry", __func__); |
1856 | 0 | return NULL; |
1857 | 0 | } |
1858 | 0 | lwgeom_set_srid(result, srid); |
1859 | 0 | } |
1860 | 0 | else if (!(result = GEOS2LWGEOM(g3, is3d))) |
1861 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
1862 | | |
1863 | 0 | GEOS_FREE(g1, g3); |
1864 | 0 | return result; |
1865 | 0 | } |
1866 | | |
1867 | | |
1868 | | static GEOSCoordSequence* |
1869 | | lwgeom_get_geos_coordseq_2d(const LWGEOM* g, uint32_t num_points) |
1870 | 0 | { |
1871 | 0 | uint32_t i = 0; |
1872 | 0 | uint8_t num_dims = 2; |
1873 | 0 | LWPOINTITERATOR* it; |
1874 | 0 | GEOSCoordSequence* coords; |
1875 | 0 | POINT4D tmp; |
1876 | |
|
1877 | 0 | coords = GEOSCoordSeq_create(num_points, num_dims); |
1878 | 0 | if (!coords) return NULL; |
1879 | | |
1880 | 0 | it = lwpointiterator_create(g); |
1881 | 0 | while (lwpointiterator_next(it, &tmp)) |
1882 | 0 | { |
1883 | 0 | if (i >= num_points) |
1884 | 0 | { |
1885 | 0 | lwerror("Incorrect num_points provided to lwgeom_get_geos_coordseq_2d"); |
1886 | 0 | GEOSCoordSeq_destroy(coords); |
1887 | 0 | lwpointiterator_destroy(it); |
1888 | 0 | return NULL; |
1889 | 0 | } |
1890 | | |
1891 | 0 | if (!GEOSCoordSeq_setXY(coords, i, tmp.x, tmp.y)) |
1892 | 0 | { |
1893 | 0 | GEOSCoordSeq_destroy(coords); |
1894 | 0 | lwpointiterator_destroy(it); |
1895 | 0 | return NULL; |
1896 | 0 | } |
1897 | 0 | i++; |
1898 | 0 | } |
1899 | 0 | lwpointiterator_destroy(it); |
1900 | |
|
1901 | 0 | return coords; |
1902 | 0 | } |
1903 | | |
1904 | | LWGEOM* |
1905 | | lwgeom_voronoi_diagram(const LWGEOM* g, const GBOX* env, double tolerance, int output_edges) |
1906 | 0 | { |
1907 | 0 | uint32_t num_points = lwgeom_count_vertices(g); |
1908 | 0 | LWGEOM* lwgeom_result; |
1909 | 0 | uint8_t is_3d = LW_FALSE; |
1910 | 0 | int32_t srid = lwgeom_get_srid(g); |
1911 | 0 | GEOSCoordSequence* coords; |
1912 | 0 | GEOSGeometry* geos_geom; |
1913 | 0 | GEOSGeometry* geos_env = NULL; |
1914 | 0 | GEOSGeometry* geos_result; |
1915 | |
|
1916 | 0 | if (num_points < 2) |
1917 | 0 | { |
1918 | 0 | LWCOLLECTION* empty = lwcollection_construct_empty(COLLECTIONTYPE, lwgeom_get_srid(g), 0, 0); |
1919 | 0 | return lwcollection_as_lwgeom(empty); |
1920 | 0 | } |
1921 | | |
1922 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1923 | | |
1924 | | /* Instead of using the standard LWGEOM2GEOS transformer, we read the vertices of the LWGEOM directly and put |
1925 | | * them into a single GEOS CoordinateSeq that can be used to define a LineString. This allows us to process |
1926 | | * geometry types that may not be supported by GEOS, and reduces the memory requirements in cases of many |
1927 | | * geometries with few points (such as LWMPOINT).*/ |
1928 | 0 | coords = lwgeom_get_geos_coordseq_2d(g, num_points); |
1929 | 0 | if (!coords) return NULL; |
1930 | | |
1931 | 0 | geos_geom = GEOSGeom_createLineString(coords); |
1932 | 0 | if (!geos_geom) |
1933 | 0 | { |
1934 | 0 | GEOSCoordSeq_destroy(coords); |
1935 | 0 | return NULL; |
1936 | 0 | } |
1937 | | |
1938 | 0 | if (env) geos_env = GBOX2GEOS(env); |
1939 | |
|
1940 | 0 | geos_result = GEOSVoronoiDiagram(geos_geom, geos_env, tolerance, output_edges); |
1941 | |
|
1942 | 0 | GEOSGeom_destroy(geos_geom); |
1943 | 0 | if (env) GEOSGeom_destroy(geos_env); |
1944 | |
|
1945 | 0 | if (!geos_result) |
1946 | 0 | { |
1947 | 0 | lwerror("GEOSVoronoiDiagram: %s", lwgeom_geos_errmsg); |
1948 | 0 | return NULL; |
1949 | 0 | } |
1950 | | |
1951 | 0 | lwgeom_result = GEOS2LWGEOM(geos_result, is_3d); |
1952 | 0 | GEOSGeom_destroy(geos_result); |
1953 | |
|
1954 | 0 | lwgeom_set_srid(lwgeom_result, srid); |
1955 | |
|
1956 | 0 | return lwgeom_result; |
1957 | 0 | } |
1958 | | |
1959 | | |
1960 | | #if POSTGIS_GEOS_VERSION >= 31100 |
1961 | | LWGEOM* |
1962 | | lwgeom_concavehull(const LWGEOM* geom, double ratio, uint32_t allow_holes) |
1963 | 0 | { |
1964 | 0 | LWGEOM* result; |
1965 | 0 | int32_t srid = RESULT_SRID(geom); |
1966 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
1967 | 0 | GEOSGeometry *g1, *g3; |
1968 | 0 | int geosGeomType; |
1969 | |
|
1970 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
1971 | |
|
1972 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
1973 | | |
1974 | 0 | geosGeomType = GEOSGeomTypeId(g1); |
1975 | 0 | if (geosGeomType == GEOS_POLYGON || geosGeomType == GEOS_MULTIPOLYGON) { |
1976 | 0 | int is_tight = LW_FALSE; |
1977 | 0 | g3 = GEOSConcaveHullOfPolygons(g1, ratio, is_tight, allow_holes); |
1978 | 0 | } |
1979 | 0 | else { |
1980 | 0 | g3 = GEOSConcaveHull(g1, ratio, allow_holes); |
1981 | 0 | } |
1982 | |
|
1983 | 0 | if (!g3) |
1984 | 0 | GEOS_FREE_AND_FAIL(g1); |
1985 | | |
1986 | 0 | GEOSSetSRID(g3, srid); |
1987 | |
|
1988 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
1989 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
1990 | | |
1991 | 0 | GEOS_FREE(g1, g3); |
1992 | 0 | return result; |
1993 | 0 | } |
1994 | | |
1995 | | LWGEOM* |
1996 | | lwgeom_simplify_polygonal(const LWGEOM* geom, double vertex_fraction, uint32_t is_outer) |
1997 | 0 | { |
1998 | 0 | LWGEOM* result; |
1999 | 0 | int32_t srid = RESULT_SRID(geom); |
2000 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
2001 | 0 | GEOSGeometry *g1, *g3; |
2002 | |
|
2003 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
2004 | |
|
2005 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
2006 | | |
2007 | 0 | g3 = GEOSPolygonHullSimplify(g1, is_outer, vertex_fraction); |
2008 | |
|
2009 | 0 | if (!g3) |
2010 | 0 | GEOS_FREE_AND_FAIL(g1); |
2011 | | |
2012 | 0 | GEOSSetSRID(g3, srid); |
2013 | |
|
2014 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
2015 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
2016 | | |
2017 | 0 | GEOS_FREE(g1, g3); |
2018 | 0 | return result; |
2019 | 0 | } |
2020 | | |
2021 | | LWGEOM* |
2022 | | lwgeom_triangulate_polygon(const LWGEOM* geom) |
2023 | 0 | { |
2024 | 0 | LWGEOM* result; |
2025 | 0 | int32_t srid = RESULT_SRID(geom); |
2026 | 0 | uint8_t is3d = FLAGS_GET_Z(geom->flags); |
2027 | 0 | GEOSGeometry *g1, *g3; |
2028 | |
|
2029 | 0 | if (srid == SRID_INVALID) return NULL; |
2030 | | |
2031 | 0 | initGEOS(lwnotice, lwgeom_geos_error); |
2032 | |
|
2033 | 0 | if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL(); |
2034 | | |
2035 | | /* if output != 1 we want polys */ |
2036 | 0 | g3 = GEOSConstrainedDelaunayTriangulation(g1); |
2037 | |
|
2038 | 0 | if (!g3) GEOS_FREE_AND_FAIL(g1); |
2039 | 0 | GEOSSetSRID(g3, srid); |
2040 | |
|
2041 | 0 | if (!(result = GEOS2LWGEOM(g3, is3d))) |
2042 | 0 | GEOS_FREE_AND_FAIL(g1, g3); |
2043 | | |
2044 | 0 | GEOS_FREE(g1, g3); |
2045 | 0 | return result; |
2046 | 0 | } |
2047 | | |
2048 | | #endif |