PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
liblwgeom/lwgeom_geos_clean.c
Go to the documentation of this file.
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 2009-2010 Sandro Santilli <strk@kbt.io>
22 *
23 **********************************************************************/
24
25#include "../postgis_config.h"
26
27#include "liblwgeom.h"
28#include "lwgeom_geos.h"
29#include "liblwgeom_internal.h"
30#include "lwgeom_log.h"
31#include "optionlist.h"
32
33#include <string.h>
34#include <stdlib.h>
35#include <assert.h>
36
41
42static void
44{
45 uint32_t i, j = 0;
46 POINT4D *p, *np;
47 int ndims = FLAGS_NDIMS(pa->flags);
48 for ( i = 0; i < pa->npoints; i++ )
49 {
50 int isnan = 0;
51 p = (POINT4D *)(getPoint_internal(pa, i));
52 if ( isnan(p->x) || isnan(p->y) ) isnan = 1;
53 else if (ndims > 2 && isnan(p->z) ) isnan = 1;
54 else if (ndims > 3 && isnan(p->m) ) isnan = 1;
55 if ( isnan ) continue;
56
57 np = (POINT4D *)(getPoint_internal(pa, j++));
58 if ( np != p ) {
59 np->x = p->x;
60 np->y = p->y;
61 if (ndims > 2)
62 np->z = p->z;
63 if (ndims > 3)
64 np->m = p->m;
65 }
66 }
67 pa->npoints = j;
68}
69
70
71
72/*
73 * Ensure the geometry is "structurally" valid
74 * (enough for GEOS to accept it)
75 * May return the input untouched (if already valid).
76 * May return geometries of lower dimension (on collapses)
77 */
78static LWGEOM*
80{
81 LWDEBUGF(2, "lwgeom_make_geos_friendly enter (type %d)", geom->type);
82 switch (geom->type)
83 {
84 case POINTTYPE:
86 return geom;
87
88 case LINETYPE:
89 /* lines need at least 2 points */
90 return lwline_make_geos_friendly((LWLINE*)geom);
91 break;
92
93 case POLYGONTYPE:
94 /* polygons need all rings closed and with npoints > 3 */
95 return lwpoly_make_geos_friendly((LWPOLY*)geom);
96 break;
97
98 case MULTILINETYPE:
100 case COLLECTIONTYPE:
101 case MULTIPOINTTYPE:
103 break;
104
105 case CIRCSTRINGTYPE:
106 case COMPOUNDTYPE:
107 case CURVEPOLYTYPE:
108 case MULTISURFACETYPE:
109 case MULTICURVETYPE:
110 default:
111 lwerror("lwgeom_make_geos_friendly: unsupported input geometry type: %s (%d)",
112 lwtype_name(geom->type),
113 geom->type);
114 break;
115 }
116 return 0;
117}
118
119/*
120 * Close the point array, if not already closed in 2d.
121 * Returns the input if already closed in 2d, or a newly
122 * constructed POINTARRAY.
123 * TODO: move in ptarray.c
124 */
125static POINTARRAY*
127{
128 POINTARRAY* newring;
129
130 /* close the ring if not already closed (2d only) */
131 if (!ptarray_is_closed_2d(ring))
132 {
133 /* close it up */
134 newring = ptarray_addPoint(ring, getPoint_internal(ring, 0), FLAGS_NDIMS(ring->flags), ring->npoints);
135 ring = newring;
136 }
137 return ring;
138}
139
140/* May return the same input or a new one (never zero) */
143{
144 POINTARRAY* closedring;
145 POINTARRAY* ring_in = ring;
146
148
149 /* close the ring if not already closed (2d only) */
150 closedring = ptarray_close2d(ring);
151 if (closedring != ring) ring = closedring;
152
153 /* return 0 for collapsed ring (after closeup) */
154
155 while (ring->npoints < 4)
156 {
157 POINTARRAY* oring = ring;
158 LWDEBUGF(4, "ring has %d points, adding another", ring->npoints);
159 /* let's add another... */
160 ring = ptarray_addPoint(ring, getPoint_internal(ring, 0), FLAGS_NDIMS(ring->flags), ring->npoints);
161 if (oring != ring_in) ptarray_free(oring);
162 }
163
164 return ring;
165}
166
167/* Make sure all rings are closed and have > 3 points.
168 * May return the input untouched.
169 */
170LWGEOM*
172{
173 LWGEOM* ret;
174 POINTARRAY** new_rings;
175 uint32_t i;
176
177 /* If the polygon has no rings there's nothing to do */
178 if (!poly->nrings) return (LWGEOM*)poly;
179
180 /* Allocate enough pointers for all rings */
181 new_rings = lwalloc(sizeof(POINTARRAY*) * poly->nrings);
182
183 /* All rings must be closed and have > 3 points */
184 for (i = 0; i < poly->nrings; i++)
185 {
186 POINTARRAY* ring_in = poly->rings[i];
187 POINTARRAY* ring_out = ring_make_geos_friendly(ring_in);
188
189 if (ring_in != ring_out)
190 {
191 LWDEBUGF(
192 3, "lwpoly_make_geos_friendly: ring %d cleaned, now has %d points", i, ring_out->npoints);
193 ptarray_free(ring_in);
194 }
195 else
196 LWDEBUGF(3, "lwpoly_make_geos_friendly: ring %d untouched", i);
197
198 assert(ring_out);
199 new_rings[i] = ring_out;
200 }
201
202 lwfree(poly->rings);
203 poly->rings = new_rings;
204 ret = (LWGEOM*)poly;
205
206 return ret;
207}
208
209/* Need NO or >1 points. Duplicate first if only one. */
210LWGEOM*
212{
213 LWGEOM* ret;
214
216
217 if (line->points->npoints == 1) /* 0 is fine, 2 is fine */
218 {
219#if 1
220 /* Duplicate point */
221 line->points = ptarray_addPoint(line->points,
222 getPoint_internal(line->points, 0),
223 FLAGS_NDIMS(line->points->flags),
224 line->points->npoints);
225 ret = (LWGEOM*)line;
226#else
227 /* Turn into a point */
228 ret = (LWGEOM*)lwpoint_construct(line->srid, 0, line->points);
229#endif
230 return ret;
231 }
232 else
233 {
234 return (LWGEOM*)line;
235 /* return lwline_clone(line); */
236 }
237}
238
239LWGEOM*
241{
242 LWGEOM** new_geoms;
243 uint32_t i, new_ngeoms = 0;
244 LWCOLLECTION* ret;
245
246 if ( ! g->ngeoms ) {
247 LWDEBUG(3, "lwcollection_make_geos_friendly: returning input untouched");
248 return lwcollection_as_lwgeom(g);
249 }
250
251 /* enough space for all components */
252 new_geoms = lwalloc(sizeof(LWGEOM*) * g->ngeoms);
253
254 ret = lwalloc(sizeof(LWCOLLECTION));
255 memcpy(ret, g, sizeof(LWCOLLECTION));
256 ret->maxgeoms = g->ngeoms;
257
258 for (i = 0; i < g->ngeoms; i++)
259 {
261 if (!newg) continue;
262 if ( newg != g->geoms[i] ) {
263 new_geoms[new_ngeoms++] = newg;
264 } else {
265 new_geoms[new_ngeoms++] = lwgeom_clone(newg);
266 }
267 }
268
269 ret->bbox = NULL; /* recompute later... */
270
271 ret->ngeoms = new_ngeoms;
272 if (new_ngeoms)
273 ret->geoms = new_geoms;
274 else
275 {
276 free(new_geoms);
277 ret->geoms = NULL;
278 ret->maxgeoms = 0;
279 }
280
281 return (LWGEOM*)ret;
282}
283
284/* Exported. Uses GEOS internally */
285LWGEOM*
287{
288 return lwgeom_make_valid_params(lwgeom_in, NULL);
289}
290
291/* Exported. Uses GEOS internally */
292LWGEOM*
293lwgeom_make_valid_params(LWGEOM* lwgeom_in, char* make_valid_params)
294{
295 int is3d;
296 GEOSGeom geosgeom;
297 GEOSGeometry* geosout;
298 LWGEOM* lwgeom_out;
299
300 LWDEBUG(1, "lwgeom_make_valid enter");
301
302 is3d = FLAGS_GET_Z(lwgeom_in->flags);
303
304 /*
305 * Step 1 : try to convert to GEOS, if impossible, clean that up first
306 * otherwise (adding only duplicates of existing points)
307 */
308
310
311 lwgeom_out = lwgeom_make_geos_friendly(lwgeom_in);
312 if (!lwgeom_out) lwerror("Could not make a geos friendly geometry out of input");
313
314 LWDEBUGF(4, "Input geom %p made GEOS-valid as %p", lwgeom_in, lwgeom_out);
315
316 geosgeom = LWGEOM2GEOS(lwgeom_out, 1);
317 if ( lwgeom_in != lwgeom_out ) {
318 lwgeom_free(lwgeom_out);
319 }
320 if (!geosgeom)
321 {
322 lwerror("Couldn't convert POSTGIS geom to GEOS: %s", lwgeom_geos_errmsg);
323 return NULL;
324 }
325 else
326 {
327 LWDEBUG(4, "geom converted to GEOS");
328 }
329
330#if POSTGIS_GEOS_VERSION < 31000
331 geosout = GEOSMakeValid(geosgeom);
332#else
333 if (!make_valid_params) {
334 geosout = GEOSMakeValid(geosgeom);
335 }
336 else {
337 /*
338 * Set up a parameters object for this
339 * make valid operation before calling
340 * it
341 */
342 const char *value;
343 char *param_list[OPTION_LIST_SIZE];
344 char param_list_text[OPTION_LIST_SIZE];
345 strncpy(param_list_text, make_valid_params, OPTION_LIST_SIZE-1);
346 param_list_text[OPTION_LIST_SIZE-1] = '\0'; /* ensure null-termination */
347 memset(param_list, 0, sizeof(param_list));
348 option_list_parse(param_list_text, param_list);
349 GEOSMakeValidParams *params = GEOSMakeValidParams_create();
350 value = option_list_search(param_list, "method");
351 if (value) {
352 if (strcasecmp(value, "linework") == 0) {
353 GEOSMakeValidParams_setMethod(params, GEOS_MAKE_VALID_LINEWORK);
354 }
355 else if (strcasecmp(value, "structure") == 0) {
356 GEOSMakeValidParams_setMethod(params, GEOS_MAKE_VALID_STRUCTURE);
357 }
358 else
359 {
360 GEOSMakeValidParams_destroy(params);
361 lwerror("Unsupported value for 'method', '%s'. Use 'linework' or 'structure'.", value);
362 }
363 }
364 value = option_list_search(param_list, "keepcollapsed");
365 if (value) {
366 if (strcasecmp(value, "true") == 0) {
367 GEOSMakeValidParams_setKeepCollapsed(params, 1);
368 }
369 else if (strcasecmp(value, "false") == 0) {
370 GEOSMakeValidParams_setKeepCollapsed(params, 0);
371 }
372 else
373 {
374 GEOSMakeValidParams_destroy(params);
375 lwerror("Unsupported value for 'keepcollapsed', '%s'. Use 'true' or 'false'", value);
376 }
377 }
378 geosout = GEOSMakeValidWithParams(geosgeom, params);
379 GEOSMakeValidParams_destroy(params);
380 }
381#endif
382 GEOSGeom_destroy(geosgeom);
383 if (!geosout) return NULL;
384
385 lwgeom_out = GEOS2LWGEOM(geosout, is3d);
386 GEOSGeom_destroy(geosout);
387
388 if (lwgeom_is_collection(lwgeom_in) && !lwgeom_is_collection(lwgeom_out))
389 {
390 LWGEOM** ogeoms = lwalloc(sizeof(LWGEOM*));
391 LWGEOM* ogeom;
392 LWDEBUG(3, "lwgeom_make_valid: forcing multi");
393 /* NOTE: this is safe because lwgeom_out is surely not lwgeom_in or
394 * otherwise we couldn't have a collection and a non-collection */
395 assert(lwgeom_in != lwgeom_out);
396 ogeoms[0] = lwgeom_out;
398 MULTITYPE[lwgeom_out->type], lwgeom_out->srid, lwgeom_out->bbox, 1, ogeoms);
399 lwgeom_out->bbox = NULL;
400 lwgeom_out = ogeom;
401 }
402
403 lwgeom_out->srid = lwgeom_in->srid;
404 return lwgeom_out;
405}
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
void lwgeom_geos_error(const char *fmt,...)
void(*) LWGEOM GEOS2LWGEOM)(const GEOSGeometry *geom, uint8_t want3d)
static POINTARRAY * ptarray_close2d(POINTARRAY *ring)
LWGEOM * lwline_make_geos_friendly(LWLINE *line)
LWGEOM * lwpoly_make_geos_friendly(LWPOLY *poly)
LWGEOM * lwcollection_make_geos_friendly(LWCOLLECTION *g)
LWGEOM * lwgeom_make_valid(LWGEOM *lwgeom_in)
Attempts to make an invalid geometries valid w/out losing points.
POINTARRAY * ring_make_geos_friendly(POINTARRAY *ring)
static void ptarray_strip_nan_coords_in_place(POINTARRAY *pa)
static LWGEOM * lwgeom_make_geos_friendly(LWGEOM *geom)
LWGEOM * lwgeom_make_valid_params(LWGEOM *lwgeom_in, char *make_valid_params)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
#define COLLECTIONTYPE
Definition liblwgeom.h:108
#define COMPOUNDTYPE
Definition liblwgeom.h:110
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
#define CURVEPOLYTYPE
Definition liblwgeom.h:111
#define MULTILINETYPE
Definition liblwgeom.h:106
#define MULTISURFACETYPE
Definition liblwgeom.h:113
#define LINETYPE
Definition liblwgeom.h:103
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition lwpoint.c:129
#define MULTIPOINTTYPE
Definition liblwgeom.h:105
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Definition lwgeom.c:519
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
#define FLAGS_GET_Z(flags)
Definition liblwgeom.h:165
void * lwalloc(size_t size)
Definition lwutil.c:227
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
void lwfree(void *mem)
Definition lwutil.c:248
#define FLAGS_NDIMS(flags)
Definition liblwgeom.h:179
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM contains sub-geometries or not This basically just checks that the struct ...
Definition lwgeom.c:1125
#define POLYGONTYPE
Definition liblwgeom.h:104
#define CIRCSTRINGTYPE
Definition liblwgeom.h:109
void ptarray_free(POINTARRAY *pa)
Definition ptarray.c:327
POINTARRAY * ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where)
Add a point in a pointarray.
Definition ptarray.c:530
#define MULTICURVETYPE
Definition liblwgeom.h:112
int ptarray_is_closed_2d(const POINTARRAY *pa)
Definition ptarray.c:710
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition lwgeom.c:337
This library is the generic geometry handling section of PostGIS.
uint8_t MULTITYPE[NUMTYPES]
Look-up for the correct MULTI* type promotion for singleton types.
Definition lwgeom.c:382
#define LWDEBUG(level, msg)
Definition lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition lwgeom_log.h:106
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
void free(void *)
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition lwinline.h:75
const char * option_list_search(char **olist, const char *key)
Returns null if the key cannot be found.
Definition optionlist.c:54
void option_list_parse(char *input, char **olist)
option_list is a null-terminated list of strings, where every odd string is a key and every even stri...
Definition optionlist.c:86
#define OPTION_LIST_SIZE
Definition optionlist.h:31
uint32_t ngeoms
Definition liblwgeom.h:580
uint32_t maxgeoms
Definition liblwgeom.h:581
GBOX * bbox
Definition liblwgeom.h:574
LWGEOM ** geoms
Definition liblwgeom.h:575
uint8_t type
Definition liblwgeom.h:462
GBOX * bbox
Definition liblwgeom.h:458
int32_t srid
Definition liblwgeom.h:460
lwflags_t flags
Definition liblwgeom.h:461
POINTARRAY * points
Definition liblwgeom.h:483
int32_t srid
Definition liblwgeom.h:484
uint8_t type
Definition liblwgeom.h:474
POINTARRAY ** rings
Definition liblwgeom.h:519
uint32_t nrings
Definition liblwgeom.h:524
double m
Definition liblwgeom.h:414
double x
Definition liblwgeom.h:414
double z
Definition liblwgeom.h:414
double y
Definition liblwgeom.h:414
lwflags_t flags
Definition liblwgeom.h:431
uint32_t npoints
Definition liblwgeom.h:427