36 #undef LWGEOM_PROFILE_MAKEVALID
38 #if POSTGIS_GEOS_VERSION < 38
43 GEOSGeometry* LWGEOM_GEOS_getPointN(
const GEOSGeometry*,
uint32_t);
45 LWGEOM_GEOS_getPointN(
const GEOSGeometry* g_in,
uint32_t n)
48 const GEOSCoordSequence* seq_in;
55 switch (GEOSGeomTypeId(g_in))
58 case GEOS_MULTILINESTRING:
59 case GEOS_MULTIPOLYGON:
60 case GEOS_GEOMETRYCOLLECTION:
62 for (gn = 0; gn < GEOSGetNumGeometries(g_in); ++gn)
64 const GEOSGeometry* g = GEOSGetGeometryN(g_in, gn);
65 ret = LWGEOM_GEOS_getPointN(g, n);
73 ret = LWGEOM_GEOS_getPointN(GEOSGetExteriorRing(g_in), n);
75 for (gn = 0; gn < GEOSGetNumInteriorRings(g_in); ++gn)
77 const GEOSGeometry* g = GEOSGetInteriorRingN(g_in, gn);
78 ret = LWGEOM_GEOS_getPointN(g, n);
90 seq_in = GEOSGeom_getCoordSeq(g_in);
91 if (!seq_in)
return NULL;
92 if (!GEOSCoordSeq_getSize(seq_in, &sz))
return NULL;
95 if (!GEOSCoordSeq_getDimensions(seq_in, &dims))
return NULL;
97 seq_out = GEOSCoordSeq_create(1, dims);
98 if (!seq_out)
return NULL;
100 if (!GEOSCoordSeq_getX(seq_in, n, &val))
return NULL;
101 if (!GEOSCoordSeq_setX(seq_out, n, val))
return NULL;
102 if (!GEOSCoordSeq_getY(seq_in, n, &val))
return NULL;
103 if (!GEOSCoordSeq_setY(seq_out, n, val))
return NULL;
106 if (!GEOSCoordSeq_getZ(seq_in, n, &val))
return NULL;
107 if (!GEOSCoordSeq_setZ(seq_out, n, val))
return NULL;
110 return GEOSGeom_createPoint(seq_out);
128 LWDEBUGF(2,
"lwgeom_make_geos_friendly enter (type %d)", geom->
type);
159 lwerror(
"lwgeom_make_geos_friendly: unsupported input geometry type: %s (%d)",
198 if (closedring != ring) ring = closedring;
231 for (i = 0; i < poly->
nrings; i++)
236 if (ring_in != ring_out)
239 3,
"lwpoly_make_geos_friendly: ring %d cleaned, now has %d points", i, ring_out->
npoints);
243 LWDEBUGF(3,
"lwpoly_make_geos_friendly: ring %d untouched", i);
246 new_rings[i] = ring_out;
250 poly->
rings = new_rings;
298 for (i = 0; i < g->
ngeoms; i++)
301 if (newg) new_geoms[new_ngeoms++] = newg;
308 ret->
geoms = new_geoms;
319 #if POSTGIS_GEOS_VERSION < 38
325 LWGEOM_GEOS_nodeLines(
const GEOSGeometry* lines)
331 GEOSGeometry *unioned, *point;
332 point = LWGEOM_GEOS_getPointN(lines, 0);
333 if (!point)
return NULL;
334 unioned = GEOSUnion(lines, point);
335 GEOSGeom_destroy(point);
344 LWGEOM_GEOS_makeValidPolygon(
const GEOSGeometry* gin)
348 GEOSGeom geos_cut_edges, geos_area, collapse_points;
349 GEOSGeometry* vgeoms[3];
350 unsigned int nvgeoms = 0;
352 assert(GEOSGeomTypeId(gin) == GEOS_POLYGON || GEOSGeomTypeId(gin) == GEOS_MULTIPOLYGON);
354 geos_bound = GEOSBoundary(gin);
355 if (!geos_bound)
return NULL;
359 geos_cut_edges = LWGEOM_GEOS_nodeLines(geos_bound);
362 GEOSGeom_destroy(geos_bound);
373 pi = GEOSGeom_extractUniquePoints(geos_bound);
376 GEOSGeom_destroy(geos_bound);
381 po = GEOSGeom_extractUniquePoints(geos_cut_edges);
384 GEOSGeom_destroy(geos_bound);
385 GEOSGeom_destroy(pi);
390 collapse_points = GEOSDifference(pi, po);
391 if (!collapse_points)
393 GEOSGeom_destroy(geos_bound);
394 GEOSGeom_destroy(pi);
395 GEOSGeom_destroy(po);
400 GEOSGeom_destroy(pi);
401 GEOSGeom_destroy(po);
403 GEOSGeom_destroy(geos_bound);
406 geos_area = GEOSGeom_createEmptyPolygon();
410 GEOSGeom_destroy(geos_cut_edges);
420 while (GEOSGetNumGeometries(geos_cut_edges))
422 GEOSGeometry* new_area = 0;
423 GEOSGeometry* new_area_bound = 0;
424 GEOSGeometry* symdif = 0;
425 GEOSGeometry* new_cut_edges = 0;
427 #ifdef LWGEOM_PROFILE_MAKEVALID
428 lwnotice(
"ST_MakeValid: building area from %d edges", GEOSGetNumGeometries(geos_cut_edges));
438 GEOSGeom_destroy(geos_cut_edges);
439 GEOSGeom_destroy(geos_area);
444 if (GEOSisEmpty(new_area))
447 GEOSGeom_destroy(new_area);
456 new_area_bound = GEOSBoundary(new_area);
460 lwnotice(
"GEOSBoundary('%s') threw an error: %s",
463 GEOSGeom_destroy(new_area);
464 GEOSGeom_destroy(geos_area);
471 symdif = GEOSSymDifference(geos_area, new_area);
474 GEOSGeom_destroy(geos_cut_edges);
475 GEOSGeom_destroy(new_area);
476 GEOSGeom_destroy(new_area_bound);
477 GEOSGeom_destroy(geos_area);
482 GEOSGeom_destroy(geos_area);
483 GEOSGeom_destroy(new_area);
498 new_cut_edges = GEOSDifference(geos_cut_edges, new_area_bound);
499 GEOSGeom_destroy(new_area_bound);
503 GEOSGeom_destroy(geos_cut_edges);
504 GEOSGeom_destroy(geos_area);
508 GEOSGeom_destroy(geos_cut_edges);
509 geos_cut_edges = new_cut_edges;
512 if (!GEOSisEmpty(geos_area))
513 vgeoms[nvgeoms++] = geos_area;
515 GEOSGeom_destroy(geos_area);
517 if (!GEOSisEmpty(geos_cut_edges))
518 vgeoms[nvgeoms++] = geos_cut_edges;
520 GEOSGeom_destroy(geos_cut_edges);
522 if (!GEOSisEmpty(collapse_points))
523 vgeoms[nvgeoms++] = collapse_points;
525 GEOSGeom_destroy(collapse_points);
535 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms);
549 LWGEOM_GEOS_makeValidLine(
const GEOSGeometry* gin)
552 noded = LWGEOM_GEOS_nodeLines(gin);
557 LWGEOM_GEOS_makeValidMultiLine(
const GEOSGeometry* gin)
559 GEOSGeometry** lines;
560 GEOSGeometry** points;
561 GEOSGeometry* mline_out = 0;
562 GEOSGeometry* mpoint_out = 0;
563 GEOSGeometry* gout = 0;
569 ngeoms = GEOSGetNumGeometries(gin);
571 nlines_alloc = ngeoms;
572 lines =
lwalloc(
sizeof(GEOSGeometry*) * nlines_alloc);
573 points =
lwalloc(
sizeof(GEOSGeometry*) * ngeoms);
575 for (i = 0; i < ngeoms; ++i)
577 const GEOSGeometry* g = GEOSGetGeometryN(gin, i);
579 vg = LWGEOM_GEOS_makeValidLine(g);
585 GEOSGeom_destroy(vg);
589 if (GEOSGeomTypeId(vg) == GEOS_POINT)
590 points[npoints++] = vg;
591 else if (GEOSGeomTypeId(vg) == GEOS_LINESTRING)
592 lines[nlines++] = vg;
593 else if (GEOSGeomTypeId(vg) == GEOS_MULTILINESTRING)
595 nsubgeoms = GEOSGetNumGeometries(vg);
596 nlines_alloc += nsubgeoms;
597 lines =
lwrealloc(lines,
sizeof(GEOSGeometry*) * nlines_alloc);
598 for (j = 0; j < nsubgeoms; ++j)
600 const GEOSGeometry* gc = GEOSGetGeometryN(vg, j);
603 lines[nlines++] = GEOSGeom_clone(gc);
610 lwerror(
"unexpected geom type returned by LWGEOM_GEOS_makeValid: %s", GEOSGeomType(vg));
617 mpoint_out = GEOSGeom_createCollection(GEOS_MULTIPOINT, points, npoints);
619 mpoint_out = points[0];
625 mline_out = GEOSGeom_createCollection(GEOS_MULTILINESTRING, lines, nlines);
627 mline_out = lines[0];
632 if (mline_out && mpoint_out)
634 points[0] = mline_out;
635 points[1] = mpoint_out;
636 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, points, 2);
654 LWGEOM_GEOS_makeValidCollection(
const GEOSGeometry* gin)
657 GEOSGeometry** vgeoms;
661 nvgeoms = GEOSGetNumGeometries(gin);
668 vgeoms =
lwalloc(
sizeof(GEOSGeometry*) * nvgeoms);
671 lwerror(
"LWGEOM_GEOS_makeValidCollection: out of memory");
675 for (i = 0; i < nvgeoms; ++i)
681 for (j = 0; j < i - 1; j++)
682 GEOSGeom_destroy(vgeoms[j]);
690 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms);
694 for (i = 0; i < nvgeoms; ++i)
695 GEOSGeom_destroy(vgeoms[i]);
710 #if POSTGIS_DEBUG_LEVEL >= 3
719 ret_char = GEOSisValid(gin);
728 #if POSTGIS_DEBUG_LEVEL >= 3
731 LWDEBUGF(3,
"Geometry [%s] is valid. ", geom_ewkt);
737 return GEOSGeom_clone(gin);
740 #if POSTGIS_DEBUG_LEVEL >= 3
744 "Geometry [%s] is still not valid: %s. Will try to clean up further.",
755 switch (GEOSGeomTypeId(gin))
757 case GEOS_MULTIPOINT:
760 lwnotice(
"PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up");
764 case GEOS_LINESTRING:
765 gout = LWGEOM_GEOS_makeValidLine(gin);
774 case GEOS_MULTILINESTRING:
775 gout = LWGEOM_GEOS_makeValidMultiLine(gin);
785 case GEOS_MULTIPOLYGON:
787 gout = LWGEOM_GEOS_makeValidPolygon(gin);
797 case GEOS_GEOMETRYCOLLECTION:
799 gout = LWGEOM_GEOS_makeValidCollection(gin);
811 char* typname = GEOSGeomType(gin);
812 lwnotice(
"ST_MakeValid: doesn't support geometry type: %s", typname);
819 #if PARANOIA_LEVEL > 1
827 GEOSGeometry *pi, *po, *pd;
833 pi = GEOSGeom_extractUniquePoints(gin);
834 po = GEOSGeom_extractUniquePoints(gout);
835 pd = GEOSDifference(pi, po);
836 GEOSGeom_destroy(pi);
837 GEOSGeom_destroy(po);
838 loss = pd && !GEOSisEmpty(pd);
839 GEOSGeom_destroy(pd);
842 lwnotice(
"%s [%d] Vertices lost in LWGEOM_GEOS_makeValid", __FILE__, __LINE__);
858 GEOSGeometry* geosout;
870 lwgeom_out = lwgeom_in;
875 "Original geom can't be converted to GEOS (%s)"
876 " - will try cleaning that up first",
880 if (!lwgeom_out)
lwerror(
"Could not make a valid geometry out of input");
893 LWDEBUG(4,
"original geom converted to GEOS");
894 lwgeom_out = lwgeom_in;
897 #if POSTGIS_GEOS_VERSION < 38
900 geosout = GEOSMakeValid(geosgeom);
902 GEOSGeom_destroy(geosgeom);
903 if (!geosout)
return NULL;
906 GEOSGeom_destroy(geosout);
912 LWDEBUG(3,
"lwgeom_make_valid: forcing multi");
915 assert(lwgeom_in != lwgeom_out);
916 ogeoms[0] = lwgeom_out;
919 lwgeom_out->
bbox = NULL;
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,...)
GEOSGeometry * LWGEOM_GEOS_makeValid(const GEOSGeometry *)
GEOSGeometry * LWGEOM_GEOS_buildArea(const GEOSGeometry *geom_in)
LWGEOM * lwline_make_geos_friendly(LWLINE *line)
POINTARRAY * ptarray_close2d(POINTARRAY *ring)
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)
void lwgeom_free(LWGEOM *geom)
uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
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)
Macros for manipulating the 'flags' byte.
void * lwrealloc(void *mem, size_t size)
#define FLAGS_NDIMS(flags)
LWPOINT * lwpoint_construct(int srid, GBOX *bbox, POINTARRAY *point)
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
void ptarray_free(POINTARRAY *pa)
LWCOLLECTION * lwcollection_construct(uint8_t type, int srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
void * lwalloc(size_t size)
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.