37 #undef LWGEOM_PROFILE_MAKEVALID 49 const GEOSCoordSequence* seq_in;
56 switch ( GEOSGeomTypeId(g_in) )
59 case GEOS_MULTILINESTRING:
60 case GEOS_MULTIPOLYGON:
61 case GEOS_GEOMETRYCOLLECTION:
63 for (gn=0; gn<GEOSGetNumGeometries(g_in); ++gn)
65 const GEOSGeometry* g = GEOSGetGeometryN(g_in, gn);
67 if ( ret )
return ret;
75 if ( ret )
return ret;
76 for (gn=0; gn<GEOSGetNumInteriorRings(g_in); ++gn)
78 const GEOSGeometry* g = GEOSGetInteriorRingN(g_in, gn);
80 if ( ret )
return ret;
92 seq_in = GEOSGeom_getCoordSeq(g_in);
93 if ( ! seq_in )
return NULL;
94 if ( ! GEOSCoordSeq_getSize(seq_in, &sz) )
return NULL;
95 if ( ! 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);
131 LWDEBUGF(2,
"lwgeom_make_geos_friendly enter (type %d)", geom->
type);
202 if (closedring != ring )
242 for (i=0; i<poly->
nrings; i++)
247 if ( ring_in != ring_out )
249 LWDEBUGF(3,
"lwpoly_make_geos_friendly: ring %d cleaned, now has %d points", i, ring_out->
npoints);
254 LWDEBUGF(3,
"lwpoly_make_geos_friendly: ring %d untouched", i);
258 new_rings[i] = ring_out;
262 poly->
rings = new_rings;
310 for (i=0; i<g->
ngeoms; i++)
313 if ( newg ) new_geoms[new_ngeoms++] = newg;
321 ret->
geoms = new_geoms;
350 if ( ! point )
return NULL;
353 "Boundary point: %s",
356 noded = GEOSUnion(lines, point);
359 GEOSGeom_destroy(point);
363 GEOSGeom_destroy(point);
366 "LWGEOM_GEOS_nodeLines: in[%s] out[%s]",
383 GEOSGeom geos_cut_edges, geos_area, collapse_points;
384 GEOSGeometry *vgeoms[3];
385 unsigned int nvgeoms=0;
387 assert (GEOSGeomTypeId(gin) == GEOS_POLYGON ||
388 GEOSGeomTypeId(gin) == GEOS_MULTIPOLYGON);
390 geos_bound = GEOSBoundary(gin);
391 if ( NULL == geos_bound )
402 #ifdef LWGEOM_PROFILE_MAKEVALID 403 lwnotice(
"ST_MakeValid: noding lines");
408 if ( NULL == geos_cut_edges )
410 GEOSGeom_destroy(geos_bound);
421 #ifdef LWGEOM_PROFILE_MAKEVALID 422 lwnotice(
"ST_MakeValid: extracting unique points from bounds");
425 pi = GEOSGeom_extractUniquePoints(geos_bound);
428 GEOSGeom_destroy(geos_bound);
429 lwnotice(
"GEOSGeom_extractUniquePoints(): %s",
435 "Boundaries input points %s",
438 #ifdef LWGEOM_PROFILE_MAKEVALID 439 lwnotice(
"ST_MakeValid: extracting unique points from cut_edges");
442 po = GEOSGeom_extractUniquePoints(geos_cut_edges);
445 GEOSGeom_destroy(geos_bound);
446 GEOSGeom_destroy(pi);
447 lwnotice(
"GEOSGeom_extractUniquePoints(): %s",
453 "Boundaries output points %s",
456 #ifdef LWGEOM_PROFILE_MAKEVALID 457 lwnotice(
"ST_MakeValid: find collapse points");
460 collapse_points = GEOSDifference(pi, po);
461 if ( NULL == collapse_points )
463 GEOSGeom_destroy(geos_bound);
464 GEOSGeom_destroy(pi);
465 GEOSGeom_destroy(po);
471 "Collapse points: %s",
474 #ifdef LWGEOM_PROFILE_MAKEVALID 475 lwnotice(
"ST_MakeValid: cleanup(1)");
478 GEOSGeom_destroy(pi);
479 GEOSGeom_destroy(po);
481 GEOSGeom_destroy(geos_bound);
484 "Noded Boundaries: %s",
488 geos_area = GEOSGeom_createEmptyPolygon();
492 GEOSGeom_destroy(geos_cut_edges);
502 while (GEOSGetNumGeometries(geos_cut_edges))
504 GEOSGeometry* new_area=0;
505 GEOSGeometry* new_area_bound=0;
506 GEOSGeometry* symdif=0;
507 GEOSGeometry* new_cut_edges=0;
509 #ifdef LWGEOM_PROFILE_MAKEVALID 510 lwnotice(
"ST_MakeValid: building area from %d edges", GEOSGetNumGeometries(geos_cut_edges));
520 GEOSGeom_destroy(geos_cut_edges);
521 GEOSGeom_destroy(geos_area);
522 lwnotice(
"LWGEOM_GEOS_buildArea() threw an error: %s",
527 if ( GEOSisEmpty(new_area) )
530 GEOSGeom_destroy(new_area);
538 #ifdef LWGEOM_PROFILE_MAKEVALID 539 lwnotice(
"ST_MakeValid: ring built with %d cut edges, saving boundaries", GEOSGetNumGeometries(geos_cut_edges));
546 new_area_bound = GEOSBoundary(new_area);
547 if ( ! new_area_bound )
551 lwnotice(
"GEOSBoundary('%s') threw an error: %s",
554 GEOSGeom_destroy(new_area);
555 GEOSGeom_destroy(geos_area);
559 #ifdef LWGEOM_PROFILE_MAKEVALID 560 lwnotice(
"ST_MakeValid: running SymDifference with new area");
566 symdif = GEOSSymDifference(geos_area, new_area);
569 GEOSGeom_destroy(geos_cut_edges);
570 GEOSGeom_destroy(new_area);
571 GEOSGeom_destroy(new_area_bound);
572 GEOSGeom_destroy(geos_area);
573 lwnotice(
"GEOSSymDifference() threw an error: %s",
578 GEOSGeom_destroy(geos_area);
579 GEOSGeom_destroy(new_area);
594 #ifdef LWGEOM_PROFILE_MAKEVALID 595 lwnotice(
"ST_MakeValid: computing new cut_edges (GEOSDifference)");
598 new_cut_edges = GEOSDifference(geos_cut_edges, new_area_bound);
599 GEOSGeom_destroy(new_area_bound);
600 if ( ! new_cut_edges )
603 GEOSGeom_destroy(geos_cut_edges);
604 GEOSGeom_destroy(geos_area);
606 lwnotice(
"GEOSDifference() threw an error: %s",
610 GEOSGeom_destroy(geos_cut_edges);
611 geos_cut_edges = new_cut_edges;
614 #ifdef LWGEOM_PROFILE_MAKEVALID 615 lwnotice(
"ST_MakeValid: final checks");
618 if ( ! GEOSisEmpty(geos_area) )
620 vgeoms[nvgeoms++] = geos_area;
624 GEOSGeom_destroy(geos_area);
627 if ( ! GEOSisEmpty(geos_cut_edges) )
629 vgeoms[nvgeoms++] = geos_cut_edges;
633 GEOSGeom_destroy(geos_cut_edges);
636 if ( ! GEOSisEmpty(collapse_points) )
638 vgeoms[nvgeoms++] = collapse_points;
642 GEOSGeom_destroy(collapse_points);
653 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms);
658 lwnotice(
"GEOSGeom_createCollection() threw an error: %s",
680 GEOSGeometry** lines;
681 GEOSGeometry** points;
682 GEOSGeometry* mline_out=0;
683 GEOSGeometry* mpoint_out=0;
684 GEOSGeometry* gout=0;
690 ngeoms = GEOSGetNumGeometries(gin);
692 nlines_alloc = ngeoms;
693 lines =
lwalloc(
sizeof(GEOSGeometry*)*nlines_alloc);
694 points =
lwalloc(
sizeof(GEOSGeometry*)*ngeoms);
696 for (i=0; i<ngeoms; ++i)
698 const GEOSGeometry* g = GEOSGetGeometryN(gin, i);
706 GEOSGeom_destroy(vg);
709 if ( GEOSGeomTypeId(vg) == GEOS_POINT )
711 points[npoints++] = vg;
713 else if ( GEOSGeomTypeId(vg) == GEOS_LINESTRING )
715 lines[nlines++] = vg;
717 else if ( GEOSGeomTypeId(vg) == GEOS_MULTILINESTRING )
719 nsubgeoms=GEOSGetNumGeometries(vg);
720 nlines_alloc += nsubgeoms;
721 lines =
lwrealloc(lines,
sizeof(GEOSGeometry*)*nlines_alloc);
722 for (j=0; j<nsubgeoms; ++j)
724 const GEOSGeometry* gc = GEOSGetGeometryN(vg, j);
727 lines[nlines++] = GEOSGeom_clone(gc);
734 lwerror(
"unexpected geom type returned " 735 "by LWGEOM_GEOS_makeValid: %s",
744 mpoint_out = GEOSGeom_createCollection(GEOS_MULTIPOINT,
749 mpoint_out = points[0];
757 mline_out = GEOSGeom_createCollection(
758 GEOS_MULTILINESTRING, lines, nlines);
762 mline_out = lines[0];
768 if ( mline_out && mpoint_out )
770 points[0] = mline_out;
771 points[1] = mpoint_out;
772 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION,
775 else if ( mline_out )
779 else if ( mpoint_out )
799 GEOSGeometry **vgeoms;
803 nvgeoms = GEOSGetNumGeometries(gin);
804 if ( nvgeoms == -1 ) {
809 vgeoms =
lwalloc(
sizeof(GEOSGeometry*) * nvgeoms );
811 lwerror(
"LWGEOM_GEOS_makeValidCollection: out of memory");
815 for ( i=0; i<nvgeoms; ++i ) {
818 while (i--) GEOSGeom_destroy(vgeoms[i]);
826 gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms);
830 for ( i=0; i<nvgeoms; ++i ) GEOSGeom_destroy(vgeoms[i]);
832 lwerror(
"GEOSGeom_createCollection() threw an error: %s",
853 ret_char = GEOSisValid(gin);
863 "Geometry [%s] is valid. ",
867 return GEOSGeom_clone(gin);
871 "Geometry [%s] is still not valid: %s. " 872 "Will try to clean up further.",
881 switch (GEOSGeomTypeId(gin))
883 case GEOS_MULTIPOINT:
886 lwnotice(
"PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up");
890 case GEOS_LINESTRING:
900 case GEOS_MULTILINESTRING:
911 case GEOS_MULTIPOLYGON:
923 case GEOS_GEOMETRYCOLLECTION:
937 char* typname = GEOSGeomType(gin);
938 lwnotice(
"ST_MakeValid: doesn't support geometry type: %s",
946 #if PARANOIA_LEVEL > 1 955 GEOSGeometry *pi, *po, *pd;
961 pi = GEOSGeom_extractUniquePoints(gin);
962 po = GEOSGeom_extractUniquePoints(gout);
963 pd = GEOSDifference(pi, po);
964 GEOSGeom_destroy(pi);
965 GEOSGeom_destroy(po);
966 loss = pd && !GEOSisEmpty(pd);
967 GEOSGeom_destroy(pd);
970 lwnotice(
"%s [%d] Vertices lost in LWGEOM_GEOS_makeValid", __FILE__, __LINE__);
986 GEOSGeometry* geosout;
998 lwgeom_out = lwgeom_in;
1003 "Original geom can't be converted to GEOS (%s)" 1004 " - will try cleaning that up first",
1011 lwerror(
"Could not make a valid geometry out of input");
1019 lwerror(
"Couldn't convert POSTGIS geom to GEOS: %s",
1027 LWDEBUG(4,
"original geom converted to GEOS");
1028 lwgeom_out = lwgeom_in;
1032 GEOSGeom_destroy(geosgeom);
1039 GEOSGeom_destroy(geosout);
1045 LWDEBUG(3,
"lwgeom_make_valid: forcing multi");
1048 assert(lwgeom_in != lwgeom_out);
1049 ogeoms[0] = lwgeom_out;
1051 lwgeom_out->
srid, lwgeom_out->
bbox, 1, ogeoms);
1052 lwgeom_out->
bbox = NULL;
1056 lwgeom_out->
srid = lwgeom_in->
srid;
POINTARRAY * ptarray_close2d(POINTARRAY *ring)
LWGEOM * lwgeom_make_valid(LWGEOM *lwgeom_in)
Attempts to make an invalid geometries valid w/out losing points.
static GEOSGeometry * LWGEOM_GEOS_makeValidPolygon(const GEOSGeometry *gin)
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
static LWGEOM * lwgeom_make_geos_friendly(LWGEOM *geom)
LWCOLLECTION * lwcollection_construct(uint8_t type, int srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
char * lwgeom_to_ewkt(const LWGEOM *lwgeom)
Return an alloced string.
void ptarray_free(POINTARRAY *pa)
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
#define LWDEBUG(level, msg)
static GEOSGeometry * LWGEOM_GEOS_makeValid(const GEOSGeometry *)
LWGEOM * lwcollection_make_geos_friendly(LWCOLLECTION *g)
int ptarray_is_closed_2d(const POINTARRAY *pa)
POINTARRAY * ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where)
Add a point in a pointarray.
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
void lwgeom_geos_error(const char *fmt,...)
const GEOSGeometry * geom
uint8_t * getPoint_internal(const POINTARRAY *pa, int n)
static GEOSGeometry * LWGEOM_GEOS_makeValidLine(const GEOSGeometry *gin)
#define FLAGS_GET_Z(flags)
Macros for manipulating the 'flags' byte.
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, int autofix)
uint8_t MULTITYPE[NUMTYPES]
Look-up for the correct MULTI* type promotion for singleton types.
POINTARRAY * ring_make_geos_friendly(POINTARRAY *ring)
GEOSGeometry * LWGEOM_GEOS_getPointN(const GEOSGeometry *, uint32_t)
void * lwrealloc(void *mem, size_t size)
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
LWGEOM * GEOS2LWGEOM(const GEOSGeometry *geom, char want3d)
LWGEOM * lwpoly_make_geos_friendly(LWPOLY *poly)
LWPOINT * lwpoint_construct(int srid, GBOX *bbox, POINTARRAY *point)
void * lwalloc(size_t size)
LWGEOM * lwline_make_geos_friendly(LWLINE *line)
#define LWDEBUGF(level, msg,...)
#define FLAGS_NDIMS(flags)
static GEOSGeometry * LWGEOM_GEOS_makeValidMultiLine(const GEOSGeometry *gin)
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
This library is the generic geometry handling section of PostGIS.
static GEOSGeometry * LWGEOM_GEOS_nodeLines(const GEOSGeometry *lines)
GEOSGeometry * LWGEOM_GEOS_buildArea(const GEOSGeometry *geom_in)
static GEOSGeometry * LWGEOM_GEOS_makeValidCollection(const GEOSGeometry *gin)