25 #include "../postgis_config.h"
39 #undef LWGEOM_PROFILE_MAKEVALID
41 #if POSTGIS_GEOS_VERSION < 30800
46 GEOSGeometry* LWGEOM_GEOS_getPointN(
const GEOSGeometry*, uint32_t);
48 LWGEOM_GEOS_getPointN(
const GEOSGeometry* g_in, uint32_t n)
51 const GEOSCoordSequence* seq_in;
58 switch (GEOSGeomTypeId(g_in))
61 case GEOS_MULTILINESTRING:
62 case GEOS_MULTIPOLYGON:
63 case GEOS_GEOMETRYCOLLECTION:
65 for (gn = 0; gn < GEOSGetNumGeometries(g_in); ++gn)
67 const GEOSGeometry* g = GEOSGetGeometryN(g_in, gn);
68 ret = LWGEOM_GEOS_getPointN(g, n);
76 ret = LWGEOM_GEOS_getPointN(GEOSGetExteriorRing(g_in), n);
78 for (gn = 0; gn < GEOSGetNumInteriorRings(g_in); ++gn)
80 const GEOSGeometry* g = GEOSGetInteriorRingN(g_in, gn);
81 ret = LWGEOM_GEOS_getPointN(g, n);
93 seq_in = GEOSGeom_getCoordSeq(g_in);
94 if (!seq_in)
return NULL;
95 if (!GEOSCoordSeq_getSize(seq_in, &sz))
return NULL;
98 if (!GEOSCoordSeq_getDimensions(seq_in, &dims))
return NULL;
100 seq_out = GEOSCoordSeq_create(1, dims);
101 if (!seq_out)
return NULL;
103 if (!GEOSCoordSeq_getX(seq_in, n, &val))
return NULL;
104 if (!GEOSCoordSeq_setX(seq_out, n, val))
return NULL;
105 if (!GEOSCoordSeq_getY(seq_in, n, &val))
return NULL;
106 if (!GEOSCoordSeq_setY(seq_out, n, val))
return NULL;
109 if (!GEOSCoordSeq_getZ(seq_in, n, &val))
return NULL;
110 if (!GEOSCoordSeq_setZ(seq_out, n, val))
return NULL;
113 return GEOSGeom_createPoint(seq_out);
128 for ( i = 0; i < pa->
npoints; i++ )
132 if ( isnan(p->
x) || isnan(p->
y) ) isnan = 1;
133 else if (ndims > 2 && isnan(p->
z) ) isnan = 1;
134 else if (ndims > 3 && isnan(p->
m) ) isnan = 1;
135 if ( isnan )
continue;
161 LWDEBUGF(2,
"lwgeom_make_geos_friendly enter (type %d)", geom->
type);
191 lwerror(
"lwgeom_make_geos_friendly: unsupported input geometry type: %s (%d)",
231 if (closedring != ring) ring = closedring;
264 for (i = 0; i < poly->
nrings; i++)
269 if (ring_in != ring_out)
272 3,
"lwpoly_make_geos_friendly: ring %d cleaned, now has %d points", i, ring_out->
npoints);
276 LWDEBUGF(3,
"lwpoly_make_geos_friendly: ring %d untouched", i);
279 new_rings[i] = ring_out;
283 poly->
rings = new_rings;
323 uint32_t i, new_ngeoms = 0;
327 LWDEBUG(3,
"lwcollection_make_geos_friendly: returning input untouched");
338 for (i = 0; i < g->
ngeoms; i++)
342 if ( newg != g->
geoms[i] ) {
343 new_geoms[new_ngeoms++] = newg;
353 ret->
geoms = new_geoms;
364 #if POSTGIS_GEOS_VERSION < 30800
370 LWGEOM_GEOS_nodeLines(
const GEOSGeometry* lines)
376 GEOSGeometry *unioned, *point;
377 point = LWGEOM_GEOS_getPointN(lines, 0);
378 if (!point)
return NULL;
379 unioned = GEOSUnion(lines, point);
380 GEOSGeom_destroy(point);
389 LWGEOM_GEOS_makeValidPolygon(
const GEOSGeometry* gin)
393 GEOSGeom geos_cut_edges, geos_area, collapse_points;
394 GEOSGeometry* vgeoms[3];
395 unsigned int nvgeoms = 0;
397 assert(GEOSGeomTypeId(gin) == GEOS_POLYGON || GEOSGeomTypeId(gin) == GEOS_MULTIPOLYGON);
399 geos_bound = GEOSBoundary(gin);
400 if (!geos_bound)
return NULL;
404 geos_cut_edges = LWGEOM_GEOS_nodeLines(geos_bound);
407 GEOSGeom_destroy(geos_bound);
418 pi = GEOSGeom_extractUniquePoints(geos_bound);
421 GEOSGeom_destroy(geos_bound);
426 po = GEOSGeom_extractUniquePoints(geos_cut_edges);
429 GEOSGeom_destroy(geos_bound);
430 GEOSGeom_destroy(pi);
435 collapse_points = GEOSDifference(pi, po);
436 if (!collapse_points)
438 GEOSGeom_destroy(geos_bound);
439 GEOSGeom_destroy(pi);
440 GEOSGeom_destroy(po);
445 GEOSGeom_destroy(pi);
446 GEOSGeom_destroy(po);
448 GEOSGeom_destroy(geos_bound);
451 geos_area = GEOSGeom_createEmptyPolygon();
455 GEOSGeom_destroy(geos_cut_edges);
465 while (GEOSGetNumGeometries(geos_cut_edges))
467 GEOSGeometry* new_area = 0;
468 GEOSGeometry* new_area_bound = 0;
469 GEOSGeometry* symdif = 0;
470 GEOSGeometry* new_cut_edges = 0;
472 #ifdef LWGEOM_PROFILE_MAKEVALID
473 lwnotice(
"ST_MakeValid: building area from %d edges", GEOSGetNumGeometries(geos_cut_edges));
480 new_area = LWGEOM_GEOS_buildArea(geos_cut_edges);
483 GEOSGeom_destroy(geos_cut_edges);
484 GEOSGeom_destroy(geos_area);
489 if (GEOSisEmpty(new_area))
492 GEOSGeom_destroy(new_area);
501 new_area_bound = GEOSBoundary(new_area);
505 lwnotice(
"GEOSBoundary('%s') threw an error: %s",
508 GEOSGeom_destroy(new_area);
509 GEOSGeom_destroy(geos_area);
516 symdif = GEOSSymDifference(geos_area, new_area);
519 GEOSGeom_destroy(geos_cut_edges);
520 GEOSGeom_destroy(new_area);
521 GEOSGeom_destroy(new_area_bound);
522 GEOSGeom_destroy(geos_area);
527 GEOSGeom_destroy(geos_area);
528 GEOSGeom_destroy(new_area);
543 new_cut_edges = GEOSDifference(geos_cut_edges, new_area_bound);
544 GEOSGeom_destroy(new_area_bound);
548 GEOSGeom_destroy(geos_cut_edges);
549 GEOSGeom_destroy(geos_area);
553 GEOSGeom_destroy(geos_cut_edges);
554 geos_cut_edges = new_cut_edges;
557 if (!GEOSisEmpty(geos_area))
558 vgeoms[nvgeoms++] = geos_area;
560 GEOSGeom_destroy(geos_area);
562 if (!GEOSisEmpty(geos_cut_edges))
563 vgeoms[nvgeoms++] = geos_cut_edges;
565 GEOSGeom_destroy(geos_cut_edges);
567 if (!GEOSisEmpty(collapse_points))
568 vgeoms[nvgeoms++] = collapse_points;
570 GEOSGeom_destroy(collapse_points);
580 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms);
594 LWGEOM_GEOS_makeValidLine(
const GEOSGeometry* gin)
597 noded = LWGEOM_GEOS_nodeLines(gin);
602 LWGEOM_GEOS_makeValidMultiLine(
const GEOSGeometry* gin)
604 GEOSGeometry** lines;
605 GEOSGeometry** points;
606 GEOSGeometry* mline_out = 0;
607 GEOSGeometry* mpoint_out = 0;
608 GEOSGeometry* gout = 0;
609 uint32_t nlines = 0, nlines_alloc;
610 uint32_t npoints = 0;
611 uint32_t ngeoms = 0, nsubgeoms;
614 ngeoms = GEOSGetNumGeometries(gin);
616 nlines_alloc = ngeoms;
617 lines =
lwalloc(
sizeof(GEOSGeometry*) * nlines_alloc);
618 points =
lwalloc(
sizeof(GEOSGeometry*) * ngeoms);
620 for (i = 0; i < ngeoms; ++i)
622 const GEOSGeometry* g = GEOSGetGeometryN(gin, i);
624 vg = LWGEOM_GEOS_makeValidLine(g);
630 GEOSGeom_destroy(vg);
634 if (GEOSGeomTypeId(vg) == GEOS_POINT)
635 points[npoints++] = vg;
636 else if (GEOSGeomTypeId(vg) == GEOS_LINESTRING)
637 lines[nlines++] = vg;
638 else if (GEOSGeomTypeId(vg) == GEOS_MULTILINESTRING)
640 nsubgeoms = GEOSGetNumGeometries(vg);
641 nlines_alloc += nsubgeoms;
642 lines =
lwrealloc(lines,
sizeof(GEOSGeometry*) * nlines_alloc);
643 for (j = 0; j < nsubgeoms; ++j)
645 const GEOSGeometry* gc = GEOSGetGeometryN(vg, j);
648 lines[nlines++] = GEOSGeom_clone(gc);
655 lwerror(
"unexpected geom type returned by LWGEOM_GEOS_makeValid: %s", GEOSGeomType(vg));
662 mpoint_out = GEOSGeom_createCollection(GEOS_MULTIPOINT, points, npoints);
664 mpoint_out = points[0];
670 mline_out = GEOSGeom_createCollection(GEOS_MULTILINESTRING, lines, nlines);
672 mline_out = lines[0];
677 if (mline_out && mpoint_out)
679 points[0] = mline_out;
680 points[1] = mpoint_out;
681 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, points, 2);
699 LWGEOM_GEOS_makeValidCollection(
const GEOSGeometry* gin)
702 GEOSGeometry** vgeoms;
706 nvgeoms = GEOSGetNumGeometries(gin);
713 vgeoms =
lwalloc(
sizeof(GEOSGeometry*) * nvgeoms);
716 lwerror(
"LWGEOM_GEOS_makeValidCollection: out of memory");
720 for (i = 0; i < nvgeoms; ++i)
722 vgeoms[i] = LWGEOM_GEOS_makeValid(GEOSGetGeometryN(gin, i));
726 for (j = 0; j < i - 1; j++)
727 GEOSGeom_destroy(vgeoms[j]);
735 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms);
739 for (i = 0; i < nvgeoms; ++i)
740 GEOSGeom_destroy(vgeoms[i]);
751 LWGEOM_GEOS_makeValid(
const GEOSGeometry* gin)
755 #if POSTGIS_DEBUG_LEVEL >= 3
764 ret_char = GEOSisValid(gin);
773 #if POSTGIS_DEBUG_LEVEL >= 3
776 LWDEBUGF(3,
"Geometry [%s] is valid. ", geom_ewkt);
782 return GEOSGeom_clone(gin);
785 #if POSTGIS_DEBUG_LEVEL >= 3
789 "Geometry [%s] is still not valid: %s. Will try to clean up further.",
800 switch (GEOSGeomTypeId(gin))
802 case GEOS_MULTIPOINT:
805 lwnotice(
"PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up");
809 case GEOS_LINESTRING:
810 gout = LWGEOM_GEOS_makeValidLine(gin);
819 case GEOS_MULTILINESTRING:
820 gout = LWGEOM_GEOS_makeValidMultiLine(gin);
830 case GEOS_MULTIPOLYGON:
832 gout = LWGEOM_GEOS_makeValidPolygon(gin);
842 case GEOS_GEOMETRYCOLLECTION:
844 gout = LWGEOM_GEOS_makeValidCollection(gin);
856 char* typname = GEOSGeomType(gin);
857 lwnotice(
"ST_MakeValid: doesn't support geometry type: %s", typname);
864 #if PARANOIA_LEVEL > 1
872 GEOSGeometry *pi, *po, *pd;
878 pi = GEOSGeom_extractUniquePoints(gin);
879 po = GEOSGeom_extractUniquePoints(gout);
880 pd = GEOSDifference(pi, po);
881 GEOSGeom_destroy(pi);
882 GEOSGeom_destroy(po);
883 loss = pd && !GEOSisEmpty(pd);
884 GEOSGeom_destroy(pd);
887 lwnotice(
"%s [%d] Vertices lost in LWGEOM_GEOS_makeValid", __FILE__, __LINE__);
910 GEOSGeometry* geosout;
913 LWDEBUG(1,
"lwgeom_make_valid enter");
925 if (!lwgeom_out)
lwerror(
"Could not make a geos friendly geometry out of input");
927 LWDEBUGF(4,
"Input geom %p made GEOS-valid as %p", lwgeom_in, lwgeom_out);
930 if ( lwgeom_in != lwgeom_out ) {
940 LWDEBUG(4,
"geom converted to GEOS");
943 #if POSTGIS_GEOS_VERSION < 30800
944 geosout = LWGEOM_GEOS_makeValid(geosgeom);
945 #elif POSTGIS_GEOS_VERSION < 31000
946 geosout = GEOSMakeValid(geosgeom);
948 if (!make_valid_params) {
949 geosout = GEOSMakeValid(geosgeom);
962 memset(param_list, 0,
sizeof(param_list));
964 GEOSMakeValidParams *params = GEOSMakeValidParams_create();
967 if (strcasecmp(
value,
"linework") == 0) {
968 GEOSMakeValidParams_setMethod(params, GEOS_MAKE_VALID_LINEWORK);
970 else if (strcasecmp(
value,
"structure") == 0) {
971 GEOSMakeValidParams_setMethod(params, GEOS_MAKE_VALID_STRUCTURE);
975 GEOSMakeValidParams_destroy(params);
976 lwerror(
"Unsupported value for 'method', '%s'. Use 'linework' or 'structure'.",
value);
981 if (strcasecmp(
value,
"true") == 0) {
982 GEOSMakeValidParams_setKeepCollapsed(params, 1);
984 else if (strcasecmp(
value,
"false") == 0) {
985 GEOSMakeValidParams_setKeepCollapsed(params, 0);
989 GEOSMakeValidParams_destroy(params);
990 lwerror(
"Unsupported value for 'keepcollapsed', '%s'. Use 'true' or 'false'",
value);
993 geosout = GEOSMakeValidWithParams(geosgeom, params);
994 GEOSMakeValidParams_destroy(params);
997 GEOSGeom_destroy(geosgeom);
998 if (!geosout)
return NULL;
1001 GEOSGeom_destroy(geosout);
1007 LWDEBUG(3,
"lwgeom_make_valid: forcing multi");
1010 assert(lwgeom_in != lwgeom_out);
1011 ogeoms[0] = lwgeom_out;
1014 lwgeom_out->
bbox = NULL;
1018 lwgeom_out->
srid = lwgeom_in->
srid;
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
LWGEOM * GEOS2LWGEOM(const GEOSGeometry *geom, uint8_t want3d)
void lwgeom_geos_error(const char *fmt,...)
static POINTARRAY * ptarray_close2d(POINTARRAY *ring)
LWGEOM * lwline_make_geos_friendly(LWLINE *line)
LWGEOM * lwpoly_make_geos_friendly(LWPOLY *poly)
LWGEOM * lwgeom_make_valid(LWGEOM *lwgeom_in)
Attempts to make an invalid geometries valid w/out losing points.
LWGEOM * lwcollection_make_geos_friendly(LWCOLLECTION *g)
static LWGEOM * lwgeom_make_geos_friendly(LWGEOM *geom)
POINTARRAY * ring_make_geos_friendly(POINTARRAY *ring)
LWGEOM * lwgeom_make_valid_params(LWGEOM *lwgeom_in, char *make_valid_params)
static void ptarray_strip_nan_coords_in_place(POINTARRAY *pa)
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
void lwgeom_free(LWGEOM *geom)
POINTARRAY * ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where)
Add a point in a pointarray.
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
char * lwgeom_to_ewkt(const LWGEOM *lwgeom)
Return an alloced string.
#define FLAGS_GET_Z(flags)
void * lwrealloc(void *mem, size_t size)
#define FLAGS_NDIMS(flags)
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
void ptarray_free(POINTARRAY *pa)
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
void * lwalloc(size_t size)
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
int ptarray_is_closed_2d(const POINTARRAY *pa)
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.
#define LWDEBUG(level, msg)
#define LWDEBUGF(level, msg,...)
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
const char * option_list_search(char **olist, const char *key)
Returns null if the key cannot be found.
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...