25 #include "../postgis_config.h"
38 #undef LWGEOM_PROFILE_MAKEVALID
40 #if POSTGIS_GEOS_VERSION < 38
45 GEOSGeometry* LWGEOM_GEOS_getPointN(
const GEOSGeometry*, uint32_t);
47 LWGEOM_GEOS_getPointN(
const GEOSGeometry* g_in, uint32_t n)
50 const GEOSCoordSequence* seq_in;
57 switch (GEOSGeomTypeId(g_in))
60 case GEOS_MULTILINESTRING:
61 case GEOS_MULTIPOLYGON:
62 case GEOS_GEOMETRYCOLLECTION:
64 for (gn = 0; gn < GEOSGetNumGeometries(g_in); ++gn)
66 const GEOSGeometry* g = GEOSGetGeometryN(g_in, gn);
67 ret = LWGEOM_GEOS_getPointN(g, n);
75 ret = LWGEOM_GEOS_getPointN(GEOSGetExteriorRing(g_in), n);
77 for (gn = 0; gn < GEOSGetNumInteriorRings(g_in); ++gn)
79 const GEOSGeometry* g = GEOSGetInteriorRingN(g_in, gn);
80 ret = LWGEOM_GEOS_getPointN(g, n);
92 seq_in = GEOSGeom_getCoordSeq(g_in);
93 if (!seq_in)
return NULL;
94 if (!GEOSCoordSeq_getSize(seq_in, &sz))
return NULL;
97 if (!GEOSCoordSeq_getDimensions(seq_in, &dims))
return NULL;
99 seq_out = GEOSCoordSeq_create(1, dims);
100 if (!seq_out)
return NULL;
102 if (!GEOSCoordSeq_getX(seq_in, n, &val))
return NULL;
103 if (!GEOSCoordSeq_setX(seq_out, n, val))
return NULL;
104 if (!GEOSCoordSeq_getY(seq_in, n, &val))
return NULL;
105 if (!GEOSCoordSeq_setY(seq_out, n, val))
return NULL;
108 if (!GEOSCoordSeq_getZ(seq_in, n, &val))
return NULL;
109 if (!GEOSCoordSeq_setZ(seq_out, n, val))
return NULL;
112 return GEOSGeom_createPoint(seq_out);
127 for ( i = 0; i < pa->
npoints; i++ )
131 if ( isnan(p->
x) || isnan(p->
y) ) isnan = 1;
132 else if (ndims > 2 && isnan(p->
z) ) isnan = 1;
133 else if (ndims > 3 && isnan(p->
m) ) isnan = 1;
134 if ( isnan )
continue;
160 LWDEBUGF(2,
"lwgeom_make_geos_friendly enter (type %d)", geom->
type);
190 lwerror(
"lwgeom_make_geos_friendly: unsupported input geometry type: %s (%d)",
230 if (closedring != ring) ring = closedring;
263 for (i = 0; i < poly->
nrings; i++)
268 if (ring_in != ring_out)
271 3,
"lwpoly_make_geos_friendly: ring %d cleaned, now has %d points", i, ring_out->
npoints);
275 LWDEBUGF(3,
"lwpoly_make_geos_friendly: ring %d untouched", i);
278 new_rings[i] = ring_out;
282 poly->
rings = new_rings;
322 uint32_t i, new_ngeoms = 0;
326 LWDEBUG(3,
"lwcollection_make_geos_friendly: returning input untouched");
337 for (i = 0; i < g->
ngeoms; i++)
341 if ( newg != g->
geoms[i] ) {
342 new_geoms[new_ngeoms++] = newg;
352 ret->
geoms = new_geoms;
363 #if POSTGIS_GEOS_VERSION < 38
369 LWGEOM_GEOS_nodeLines(
const GEOSGeometry* lines)
375 GEOSGeometry *unioned, *point;
376 point = LWGEOM_GEOS_getPointN(lines, 0);
377 if (!point)
return NULL;
378 unioned = GEOSUnion(lines, point);
379 GEOSGeom_destroy(point);
388 LWGEOM_GEOS_makeValidPolygon(
const GEOSGeometry* gin)
392 GEOSGeom geos_cut_edges, geos_area, collapse_points;
393 GEOSGeometry* vgeoms[3];
394 unsigned int nvgeoms = 0;
396 assert(GEOSGeomTypeId(gin) == GEOS_POLYGON || GEOSGeomTypeId(gin) == GEOS_MULTIPOLYGON);
398 geos_bound = GEOSBoundary(gin);
399 if (!geos_bound)
return NULL;
403 geos_cut_edges = LWGEOM_GEOS_nodeLines(geos_bound);
406 GEOSGeom_destroy(geos_bound);
417 pi = GEOSGeom_extractUniquePoints(geos_bound);
420 GEOSGeom_destroy(geos_bound);
425 po = GEOSGeom_extractUniquePoints(geos_cut_edges);
428 GEOSGeom_destroy(geos_bound);
429 GEOSGeom_destroy(pi);
434 collapse_points = GEOSDifference(pi, po);
435 if (!collapse_points)
437 GEOSGeom_destroy(geos_bound);
438 GEOSGeom_destroy(pi);
439 GEOSGeom_destroy(po);
444 GEOSGeom_destroy(pi);
445 GEOSGeom_destroy(po);
447 GEOSGeom_destroy(geos_bound);
450 geos_area = GEOSGeom_createEmptyPolygon();
454 GEOSGeom_destroy(geos_cut_edges);
464 while (GEOSGetNumGeometries(geos_cut_edges))
466 GEOSGeometry* new_area = 0;
467 GEOSGeometry* new_area_bound = 0;
468 GEOSGeometry* symdif = 0;
469 GEOSGeometry* new_cut_edges = 0;
471 #ifdef LWGEOM_PROFILE_MAKEVALID
472 lwnotice(
"ST_MakeValid: building area from %d edges", GEOSGetNumGeometries(geos_cut_edges));
479 new_area = LWGEOM_GEOS_buildArea(geos_cut_edges);
482 GEOSGeom_destroy(geos_cut_edges);
483 GEOSGeom_destroy(geos_area);
488 if (GEOSisEmpty(new_area))
491 GEOSGeom_destroy(new_area);
500 new_area_bound = GEOSBoundary(new_area);
504 lwnotice(
"GEOSBoundary('%s') threw an error: %s",
507 GEOSGeom_destroy(new_area);
508 GEOSGeom_destroy(geos_area);
515 symdif = GEOSSymDifference(geos_area, new_area);
518 GEOSGeom_destroy(geos_cut_edges);
519 GEOSGeom_destroy(new_area);
520 GEOSGeom_destroy(new_area_bound);
521 GEOSGeom_destroy(geos_area);
526 GEOSGeom_destroy(geos_area);
527 GEOSGeom_destroy(new_area);
542 new_cut_edges = GEOSDifference(geos_cut_edges, new_area_bound);
543 GEOSGeom_destroy(new_area_bound);
547 GEOSGeom_destroy(geos_cut_edges);
548 GEOSGeom_destroy(geos_area);
552 GEOSGeom_destroy(geos_cut_edges);
553 geos_cut_edges = new_cut_edges;
556 if (!GEOSisEmpty(geos_area))
557 vgeoms[nvgeoms++] = geos_area;
559 GEOSGeom_destroy(geos_area);
561 if (!GEOSisEmpty(geos_cut_edges))
562 vgeoms[nvgeoms++] = geos_cut_edges;
564 GEOSGeom_destroy(geos_cut_edges);
566 if (!GEOSisEmpty(collapse_points))
567 vgeoms[nvgeoms++] = collapse_points;
569 GEOSGeom_destroy(collapse_points);
579 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms);
593 LWGEOM_GEOS_makeValidLine(
const GEOSGeometry* gin)
596 noded = LWGEOM_GEOS_nodeLines(gin);
601 LWGEOM_GEOS_makeValidMultiLine(
const GEOSGeometry* gin)
603 GEOSGeometry** lines;
604 GEOSGeometry** points;
605 GEOSGeometry* mline_out = 0;
606 GEOSGeometry* mpoint_out = 0;
607 GEOSGeometry* gout = 0;
608 uint32_t nlines = 0, nlines_alloc;
609 uint32_t npoints = 0;
610 uint32_t ngeoms = 0, nsubgeoms;
613 ngeoms = GEOSGetNumGeometries(gin);
615 nlines_alloc = ngeoms;
616 lines =
lwalloc(
sizeof(GEOSGeometry*) * nlines_alloc);
617 points =
lwalloc(
sizeof(GEOSGeometry*) * ngeoms);
619 for (i = 0; i < ngeoms; ++i)
621 const GEOSGeometry* g = GEOSGetGeometryN(gin, i);
623 vg = LWGEOM_GEOS_makeValidLine(g);
629 GEOSGeom_destroy(vg);
633 if (GEOSGeomTypeId(vg) == GEOS_POINT)
634 points[npoints++] = vg;
635 else if (GEOSGeomTypeId(vg) == GEOS_LINESTRING)
636 lines[nlines++] = vg;
637 else if (GEOSGeomTypeId(vg) == GEOS_MULTILINESTRING)
639 nsubgeoms = GEOSGetNumGeometries(vg);
640 nlines_alloc += nsubgeoms;
641 lines =
lwrealloc(lines,
sizeof(GEOSGeometry*) * nlines_alloc);
642 for (j = 0; j < nsubgeoms; ++j)
644 const GEOSGeometry* gc = GEOSGetGeometryN(vg, j);
647 lines[nlines++] = GEOSGeom_clone(gc);
654 lwerror(
"unexpected geom type returned by LWGEOM_GEOS_makeValid: %s", GEOSGeomType(vg));
661 mpoint_out = GEOSGeom_createCollection(GEOS_MULTIPOINT, points, npoints);
663 mpoint_out = points[0];
669 mline_out = GEOSGeom_createCollection(GEOS_MULTILINESTRING, lines, nlines);
671 mline_out = lines[0];
676 if (mline_out && mpoint_out)
678 points[0] = mline_out;
679 points[1] = mpoint_out;
680 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, points, 2);
698 LWGEOM_GEOS_makeValidCollection(
const GEOSGeometry* gin)
701 GEOSGeometry** vgeoms;
705 nvgeoms = GEOSGetNumGeometries(gin);
712 vgeoms =
lwalloc(
sizeof(GEOSGeometry*) * nvgeoms);
715 lwerror(
"LWGEOM_GEOS_makeValidCollection: out of memory");
719 for (i = 0; i < nvgeoms; ++i)
721 vgeoms[i] = LWGEOM_GEOS_makeValid(GEOSGetGeometryN(gin, i));
725 for (j = 0; j < i - 1; j++)
726 GEOSGeom_destroy(vgeoms[j]);
734 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms);
738 for (i = 0; i < nvgeoms; ++i)
739 GEOSGeom_destroy(vgeoms[i]);
750 LWGEOM_GEOS_makeValid(
const GEOSGeometry* gin)
754 #if POSTGIS_DEBUG_LEVEL >= 3
763 ret_char = GEOSisValid(gin);
772 #if POSTGIS_DEBUG_LEVEL >= 3
775 LWDEBUGF(3,
"Geometry [%s] is valid. ", geom_ewkt);
781 return GEOSGeom_clone(gin);
784 #if POSTGIS_DEBUG_LEVEL >= 3
788 "Geometry [%s] is still not valid: %s. Will try to clean up further.",
799 switch (GEOSGeomTypeId(gin))
801 case GEOS_MULTIPOINT:
804 lwnotice(
"PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up");
808 case GEOS_LINESTRING:
809 gout = LWGEOM_GEOS_makeValidLine(gin);
818 case GEOS_MULTILINESTRING:
819 gout = LWGEOM_GEOS_makeValidMultiLine(gin);
829 case GEOS_MULTIPOLYGON:
831 gout = LWGEOM_GEOS_makeValidPolygon(gin);
841 case GEOS_GEOMETRYCOLLECTION:
843 gout = LWGEOM_GEOS_makeValidCollection(gin);
855 char* typname = GEOSGeomType(gin);
856 lwnotice(
"ST_MakeValid: doesn't support geometry type: %s", typname);
863 #if PARANOIA_LEVEL > 1
871 GEOSGeometry *pi, *po, *pd;
877 pi = GEOSGeom_extractUniquePoints(gin);
878 po = GEOSGeom_extractUniquePoints(gout);
879 pd = GEOSDifference(pi, po);
880 GEOSGeom_destroy(pi);
881 GEOSGeom_destroy(po);
882 loss = pd && !GEOSisEmpty(pd);
883 GEOSGeom_destroy(pd);
886 lwnotice(
"%s [%d] Vertices lost in LWGEOM_GEOS_makeValid", __FILE__, __LINE__);
902 GEOSGeometry* geosout;
905 LWDEBUG(1,
"lwgeom_make_valid enter");
917 if (!lwgeom_out)
lwerror(
"Could not make a geos friendly geometry out of input");
919 LWDEBUGF(4,
"Input geom %p made GEOS-valid as %p", lwgeom_in, lwgeom_out);
922 if ( lwgeom_in != lwgeom_out ) {
932 LWDEBUG(4,
"geom converted to GEOS");
935 #if POSTGIS_GEOS_VERSION < 38
936 geosout = LWGEOM_GEOS_makeValid(geosgeom);
938 geosout = GEOSMakeValid(geosgeom);
940 GEOSGeom_destroy(geosgeom);
941 if (!geosout)
return NULL;
944 GEOSGeom_destroy(geosout);
950 LWDEBUG(3,
"lwgeom_make_valid: forcing multi");
953 assert(lwgeom_in != lwgeom_out);
954 ogeoms[0] = lwgeom_out;
957 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,...)
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)
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)