]> granicus.if.org Git - postgis/commitdiff
Disable 'clean' test (still deciding on what's the expected output); keep only cut...
authorSandro Santilli <strk@keybit.net>
Sun, 21 Feb 2010 21:11:03 +0000 (21:11 +0000)
committerSandro Santilli <strk@keybit.net>
Sun, 21 Feb 2010 21:11:03 +0000 (21:11 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@5278 b70326c6-7e19-0410-871a-916f4a2858ee

postgis/Makefile.in
postgis/lwgeom_geos.c
postgis/lwgeom_geos.h
postgis/lwgeom_geos_clean.c [new file with mode: 0644]
regress/Makefile.in

index a0521b4c71d5164d3cf1f700b80e6c28b2d59c10..3a22bb514c06b8a9ddac8573f5a6aa9805f470b3 100644 (file)
@@ -39,6 +39,7 @@ PG_OBJS=lwgeom_pg.o \
        lwgeom_chip.o \
        lwgeom_geos.o \
        lwgeom_geos_prepared.o \
+       lwgeom_geos_clean.o \
        lwgeom_export.o \
        lwgeom_svg.o \
        lwgeom_gml.o \
index 9af8c975aef1f5331f8c270f6c883b6a096c6b66..be0eac1c540d8cc5b666caf962fb4ce0c03c9a54 100644 (file)
@@ -101,12 +101,6 @@ errorlogger(const char *fmt, ...)
        va_end(ap);
 }
 
-
-/*
- * This function would better be moved to the GEOS C-API,
- * but isn't available up to 3.2.0
- */
-GEOSGeometry* LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in);
 GEOSGeometry*
 LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in)
 {
@@ -138,6 +132,8 @@ LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in)
        ngeoms = GEOSGetNumGeometries(geos_result);
 
        POSTGIS_DEBUGF(3, "GEOSpolygonize: ngeoms in polygonize output: %d", ngeoms);
+       POSTGIS_DEBUGF(3, "GEOSpolygonize: polygonized:%s",  
+               lwgeom_to_ewkt(GEOS2LWGEOM(geos_result, 0), PARSER_CHECK_NONE));
 
        /*
         * No geometries in collection, early out
@@ -199,10 +195,17 @@ LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in)
                if ( shp == NULL )
                {
                        shp = extring;
+                       POSTGIS_DEBUGF(3, "GEOSpolygonize: shp:%s",  
+                               lwgeom_to_ewkt(GEOS2LWGEOM(shp, 0), PARSER_CHECK_NONE));
                }
                else
                {
                        tmp = GEOSSymDifference(shp, extring);
+                       POSTGIS_DEBUGF(3, "GEOSpolygonize: SymDifference(%s, %s):%s",  
+                               lwgeom_to_ewkt(GEOS2LWGEOM(shp, 0), PARSER_CHECK_NONE),
+                               lwgeom_to_ewkt(GEOS2LWGEOM(extring, 0), PARSER_CHECK_NONE),
+                               lwgeom_to_ewkt(GEOS2LWGEOM(tmp, 0), PARSER_CHECK_NONE)
+                       );
                        GEOSGeom_destroy(shp);
                        GEOSGeom_destroy(extring);
                        shp = tmp;
@@ -214,81 +217,6 @@ LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in)
        return shp;
 }
 
-/*
- * Return Nth vertex in GEOSGeometry as a POINT.
- * May return NULL if the geometry has NO vertexex.
- */
-GEOSGeometry* LWGEOM_GEOS_getPointN(const GEOSGeometry*, unsigned int);
-GEOSGeometry*
-LWGEOM_GEOS_getPointN(const GEOSGeometry* g_in, unsigned int n)
-{
-       unsigned int dims;
-       const GEOSCoordSequence* seq_in;
-       GEOSCoordSeq seq_out;
-       double val;
-       unsigned int sz;
-       int gn;
-       GEOSGeometry* ret;
-
-       switch ( GEOSGeomTypeId(g_in) )
-       {
-       case GEOS_MULTIPOINT:
-       case GEOS_MULTILINESTRING:
-       case GEOS_MULTIPOLYGON:
-       case GEOS_GEOMETRYCOLLECTION:
-       {
-               for (gn=0; gn<GEOSGetNumGeometries(g_in); ++gn)
-               {
-                       const GEOSGeometry* g = GEOSGetGeometryN(g_in, gn);
-                       ret = LWGEOM_GEOS_getPointN(g,n);
-                       if ( ret ) return ret;
-               }
-               break;
-       }
-
-       case GEOS_POLYGON:
-       {
-               ret = LWGEOM_GEOS_getPointN(GEOSGetExteriorRing(g_in), n);
-               if ( ret ) return ret;
-               for (gn=0; gn<GEOSGetNumInteriorRings(g_in); ++gn)
-               {
-                       const GEOSGeometry* g = GEOSGetInteriorRingN(g_in, gn);
-                       ret = LWGEOM_GEOS_getPointN(g, n);
-                       if ( ret ) return ret;
-               }
-               break;
-       }
-
-       case GEOS_POINT:
-       case GEOS_LINESTRING:
-       case GEOS_LINEARRING:
-               break;
-
-       }
-
-       seq_in = GEOSGeom_getCoordSeq(g_in);
-       if ( ! seq_in ) return NULL;
-       if ( ! GEOSCoordSeq_getSize(seq_in, &sz) ) return NULL;
-       if ( ! sz ) return NULL;
-
-       if ( ! GEOSCoordSeq_getDimensions(seq_in, &dims) ) return NULL;
-
-       seq_out = GEOSCoordSeq_create(1, dims);
-       if ( ! seq_out ) return NULL;
-
-       if ( ! GEOSCoordSeq_getX(seq_in, n, &val) ) return NULL;
-       if ( ! GEOSCoordSeq_setX(seq_out, n, val) ) return NULL;
-       if ( ! GEOSCoordSeq_getY(seq_in, n, &val) ) return NULL;
-       if ( ! GEOSCoordSeq_setY(seq_out, n, val) ) return NULL;
-       if ( dims > 2 )
-       {
-               if ( ! GEOSCoordSeq_getZ(seq_in, n, &val) ) return NULL;
-               if ( ! GEOSCoordSeq_setZ(seq_out, n, val) ) return NULL;
-       }
-
-       return GEOSGeom_createPoint(seq_out);
-}
-
 PG_FUNCTION_INFO_V1(postgis_geos_version);
 Datum postgis_geos_version(PG_FUNCTION_ARGS)
 {
@@ -1556,7 +1484,7 @@ Datum pointonsurface(PG_FUNCTION_ARGS)
        PROFSTOP(PROF_P2G1);
        if ( 0 == g1 )   /* exception thrown at construction */
        {
-               lwerror("First argument geometry could not be converted to GEOS: %s", loggederror);
+               elog(WARNING, "GEOSPointOnSurface(): %s", loggederror);
                PG_RETURN_NULL();
        }
 
@@ -1566,7 +1494,7 @@ Datum pointonsurface(PG_FUNCTION_ARGS)
 
        if (g3 == NULL)
        {
-               elog(ERROR,"GEOS pointonsurface() threw an error!");
+               elog(WARNING, "GEOSPointOnSurface(): %s", loggederror);
                GEOSGeom_destroy(g1);
                PG_RETURN_NULL(); /* never get here */
        }
@@ -3960,510 +3888,3 @@ Datum LWGEOM_buildarea(PG_FUNCTION_ARGS)
 
 }
 
-/*-------------------------------------------------------------
- *
- * ST_MakeValid
- *
- * Attempts to make an invalid geometries valid w/out loosing
- * point sets.
- *
- * Polygons may become collection of polygons and lines.
- * Collapsed rings (or portions of rings) may be dissolved in
- * polygon area or transformed to linestring if outside any other
- * ring.
- *
- * Author: Sandro Santilli <strk@keybit.net>
- *
- * Work done for Regione Toscana - Sistema Informativo per il Governo
- * del Territorio e dell'Ambiente (RT-SIGTA).
- *
- * Thanks to Dr. Horst Duester for previous work on a plpgsql version
- * of the cleanup logic [1]
- *
- * Thanks to Andrea Peri for recommandations on constraints.
- *
- * [1] http://www.sogis1.so.ch/sogis/dl/postgis/cleanGeometry.sql
- *
- *
- *------------------------------------------------------------*/
-
-LWGEOM * lwcollection_make_valid(LWCOLLECTION *g);
-LWGEOM * lwline_make_valid(LWLINE *line);
-LWGEOM * lwpoly_make_valid(LWPOLY *poly);
-POINTARRAY* ring_make_valid(POINTARRAY* ring);
-
-/*
- * Ensure the geometry is "valid"
- * May return the input untouched (if already valid).
- * May return geometries of lower dimension (on collapses)
- */
-static LWGEOM *
-lwgeom_make_valid(LWGEOM *geom)
-{
-       LWDEBUGF(2, "lwgeom_make_valid enter (type %d)", TYPE_GETTYPE(geom->type));
-       switch (TYPE_GETTYPE(geom->type))
-       {
-       case POINTTYPE:
-       case MULTIPOINTTYPE:
-               /* a point is always valid */
-               return geom;
-               break;
-
-       case LINETYPE:
-               /* lines need at least 2 points */
-               return lwline_make_valid((LWLINE *)geom);
-               break;
-
-       case POLYGONTYPE:
-               /* polygons need all rings closed and with npoints > 3 */
-               return lwpoly_make_valid((LWPOLY *)geom);
-               break;
-
-       case MULTILINETYPE:
-       case MULTIPOLYGONTYPE:
-       case COLLECTIONTYPE:
-               return lwcollection_make_valid((LWCOLLECTION *)geom);
-               break;
-
-       case CIRCSTRINGTYPE:
-       case COMPOUNDTYPE:
-       case CURVEPOLYTYPE:
-       case MULTISURFACETYPE:
-       case MULTICURVETYPE:
-       default:
-               lwerror("unsupported input geometry type: %d", TYPE_GETTYPE(geom->type));
-               break;
-       }
-       return 0;
-}
-
-/*
- * Close the point array, if not already closed in 2d.
- * Returns the input if already closed in 2d, or a newly
- * constructed POINTARRAY.
- */
-POINTARRAY* ptarray_close2d(POINTARRAY* ring);
-POINTARRAY*
-ptarray_close2d(POINTARRAY* ring)
-{
-       POINTARRAY* newring;
-
-       /* close the ring if not already closed (2d only) */
-       if ( ! ptarray_isclosed2d(ring) )
-       {
-               /* close it up */
-               newring = ptarray_addPoint(ring,
-                                          getPoint_internal(ring, 0),
-                                          TYPE_NDIMS(ring->dims),
-                                          ring->npoints);
-               ring = newring;
-       }
-       return ring;
-}
-
-/* May return the same input or a new one (never zero) */
-POINTARRAY*
-ring_make_valid(POINTARRAY* ring)
-{
-       POINTARRAY* closedring;
-
-       /* close the ring if not already closed (2d only) */
-       closedring = ptarray_close2d(ring);
-       if (closedring != ring )
-       {
-               ptarray_free(ring); /* should we do this ? */
-               ring = closedring;
-       }
-
-       /* return 0 for collapsed ring (after closeup) */
-
-       while ( ring->npoints < 4 )
-       {
-               LWDEBUGF(4, "ring has %d points, adding another", ring->npoints);
-               /* let's add another... */
-               ring = ptarray_addPoint(ring,
-                                       getPoint_internal(ring, 0),
-                                       TYPE_NDIMS(ring->dims),
-                                       ring->npoints);
-       }
-
-
-       return ring;
-}
-
-/* Make sure all rings are closed and have > 3 points.
- * May return the input untouched.
- */
-LWGEOM *
-lwpoly_make_valid(LWPOLY *poly)
-{
-       LWGEOM* ret;
-       POINTARRAY **new_rings;
-       int i;
-
-       /* If the polygon has no rings there's nothing to do */
-       if ( ! poly->nrings ) return (LWGEOM*)poly;
-
-       /* Allocate enough pointers for all rings */
-       new_rings = lwalloc(sizeof(POINTARRAY*)*poly->nrings);
-
-       /* All rings must be closed and have > 3 points */
-       for (i=0; i<poly->nrings; i++)
-       {
-               POINTARRAY* ring_in = poly->rings[i];
-               POINTARRAY* ring_out = ring_make_valid(ring_in);
-
-               if ( ring_in != ring_out )
-               {
-                       LWDEBUGF(3, "lwpoly_make_valid: ring %d cleaned, now has %d points", i, ring_out->npoints);
-                       /* this may come right from
-                        * the binary representation lands
-                        */
-                       /*ptarray_free(ring_in); */
-               }
-               else
-               {
-                       LWDEBUGF(3, "lwpoly_make_valid: ring %d untouched", i);
-               }
-
-               assert ( ring_out );
-               new_rings[i] = ring_out;
-       }
-
-       lwfree(poly->rings);
-       poly->rings = new_rings;
-       ret = (LWGEOM*)poly;
-
-       return ret;
-}
-
-/* Need NO or >1 points. Duplicate first if only one. */
-LWGEOM *
-lwline_make_valid(LWLINE *line)
-{
-       LWGEOM *ret;
-
-       if (line->points->npoints == 1) /* 0 is fine, 2 is fine */
-       {
-#if 0
-               /* Duplicate point */
-               line->points = ptarray_addPoint(line->points,
-                                               getPoint_internal(line->points, 0),
-                                               TYPE_NDIMS(line->points->dims),
-                                               line->points->npoints);
-               ret = (LWGEOM*)line;
-#else
-               /* Turn into a point */
-               ret = (LWGEOM*)lwpoint_construct(line->SRID, 0, line->points);
-#endif
-               return ret;
-       }
-       else
-       {
-               return (LWGEOM*)line;
-               /* return lwline_clone(line); */
-       }
-}
-
-LWGEOM *
-lwcollection_make_valid(LWCOLLECTION *g)
-{
-       LWGEOM **new_geoms;
-       uint32 i, new_ngeoms=0;
-       LWCOLLECTION *ret;
-
-       /* enough space for all components */
-       new_geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
-
-       ret = lwalloc(sizeof(LWCOLLECTION));
-       memcpy(ret, g, sizeof(LWCOLLECTION));
-
-       for (i=0; i<g->ngeoms; i++)
-       {
-               LWGEOM* newg = lwgeom_make_valid(g->geoms[i]);
-               if ( newg ) new_geoms[new_ngeoms++] = newg;
-       }
-
-       ret->bbox = 0; /* recompute later... */
-
-       ret->ngeoms = new_ngeoms;
-       if ( new_ngeoms )
-       {
-               ret->geoms = new_geoms;
-       }
-       else
-       {
-               free(new_geoms);
-               ret->geoms = 0;
-       }
-
-       return (LWGEOM*)ret;
-}
-
-/*
- * We expect initGEOS being called already.
- * Will return NULL on error (expect error handler being called by then)
- *
- */
-GEOSGeometry* LWGEOM_GEOS_makeValidPolygon(const GEOSGeometry* geom_in);
-GEOSGeometry*
-LWGEOM_GEOS_makeValidPolygon(const GEOSGeometry* gin)
-{
-       GEOSGeom gout;
-       GEOSGeom geos_bound, geos_bound_noded, geos_tmp_point;
-       GEOSGeom geos_cut_edges, geos_area;
-
-       assert (GEOSGeomTypeId(gin) == GEOS_POLYGON ||
-               GEOSGeomTypeId(gin) == GEOS_MULTIPOLYGON);
-
-       geos_bound = GEOSBoundary(gin);
-       if ( NULL == geos_bound )
-       {
-               return NULL;
-       }
-
-       POSTGIS_DEBUGF(3,
-                      "Boundaries: %s",
-                      lwgeom_to_ewkt(GEOS2LWGEOM(geos_bound, 0),
-                                     PARSER_CHECK_NONE));
-
-       /*
-        * Union with first geometry point, obtaining full noding
-        * and dissolving of duplicated repeated points
-        *
-        * TODO: substitute this with UnaryUnion?
-        *
-        */
-       geos_tmp_point = LWGEOM_GEOS_getPointN(geos_bound, 0);
-       if ( ! geos_tmp_point )
-       {
-               lwnotice("GEOSGeom_createPoint(): %s", loggederror);
-               GEOSGeom_destroy(geos_bound);
-               return NULL;
-       }
-
-       POSTGIS_DEBUGF(3,
-                      "Boundary point: %s",
-                      lwgeom_to_ewkt(GEOS2LWGEOM(geos_tmp_point, 0),
-                                     PARSER_CHECK_NONE));
-
-       geos_bound_noded = GEOSUnion(geos_bound, geos_tmp_point);
-       if ( NULL == geos_bound_noded )
-       {
-               lwnotice("GEOSUnion(): %s", loggederror);
-               GEOSGeom_destroy(geos_bound);
-               GEOSGeom_destroy(geos_tmp_point);
-               return NULL;
-       }
-
-       GEOSGeom_destroy(geos_bound);
-       GEOSGeom_destroy(geos_tmp_point);
-
-       POSTGIS_DEBUGF(3,
-                      "Noded: %s",
-                      lwgeom_to_ewkt(GEOS2LWGEOM(geos_bound_noded, 0),
-                                     PARSER_CHECK_NONE));
-
-       geos_area = LWGEOM_GEOS_buildArea(geos_bound_noded);
-       if ( ! geos_area ) /* must be an exception ... */
-       {
-               /* cleanup */
-               GEOSGeom_destroy(geos_bound_noded);
-               lwnotice("LWGEOM_GEOS_buildArea() threw an error: %s",
-                        loggederror);
-               return NULL;
-       }
-
-       /* Compute what's left out from original boundary
-        * (this would be the so called 'cut lines' */
-       geos_cut_edges = GEOSDifference(geos_bound_noded, geos_area);
-       if ( ! geos_cut_edges )   /* an exception again */
-       {
-               /* cleanup and throw */
-               GEOSGeom_destroy(geos_bound_noded);
-               GEOSGeom_destroy(geos_area);
-               lwnotice("GEOSDifference() threw an error: %s",
-                        loggederror);
-               return NULL;
-       }
-
-       GEOSGeom_destroy(geos_bound_noded);
-
-       /* Finally put togheter cut edges and area
-        * (could become a collection) */
-       gout = GEOSUnion(geos_area, geos_cut_edges);
-       GEOSGeom_destroy(geos_cut_edges);
-       GEOSGeom_destroy(geos_area);
-       if ( ! gout )   /* an exception again */
-       {
-               /* cleanup and throw */
-               lwnotice("GEOSUnion() threw an error: %s",
-                        loggederror);
-               return NULL;
-       }
-
-       return gout;
-}
-
-Datum st_makevalid(PG_FUNCTION_ARGS);
-PG_FUNCTION_INFO_V1(st_makevalid);
-Datum st_makevalid(PG_FUNCTION_ARGS)
-{
-       PG_LWGEOM *in, *out;
-       GEOSGeom geosgeom;
-       LWGEOM *lwgeom_in, *lwgeom_out;
-       /* LWGEOM *lwgeom_pointset; */
-       char ret_char;
-       int is3d;
-
-       in = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-       lwgeom_in = lwgeom_deserialize(SERIALIZED_FORM(in));
-
-       is3d = TYPE_HASZ(lwgeom_in->type);
-
-       /*
-        * Step 1 : try to convert to GEOS, if impossible, clean that up first
-        *          otherwise (adding only duplicates of existing points)
-        */
-
-       initGEOS(errorlogger, errorlogger);
-
-
-       lwgeom_out = lwgeom_in;
-       geosgeom = LWGEOM2GEOS(lwgeom_out);
-       if ( ! geosgeom )
-       {
-               POSTGIS_DEBUGF(4,
-                              "Original geom can't be converted to GEOS (%s)"
-                              " - will try cleaning that up first",
-                              loggederror);
-
-
-               lwgeom_out = lwgeom_make_valid(lwgeom_out);
-               if ( ! lwgeom_out )
-               {
-                       lwerror("Could not make a valid geometry out of input");
-               }
-
-               /* try again as we did cleanup now */
-               geosgeom = LWGEOM2GEOS(lwgeom_out);
-               if ( ! geosgeom )
-               {
-                       lwerror("Couldn't convert POSTGIS geom to GEOS: %s",
-                               loggederror);
-                       PG_RETURN_NULL();
-               }
-
-       }
-       else
-       {
-               POSTGIS_DEBUG(4, "original geom converted to GEOS");
-               lwgeom_out = lwgeom_in;
-       }
-
-
-       /*
-        * Step 2 : return the resulting geometry if it's now valid
-        */
-
-       ret_char = GEOSisValid(geosgeom);
-       if ( ret_char == 2 )
-       {
-               GEOSGeom_destroy(geosgeom);
-               geosgeom=0;
-
-               PG_FREE_IF_COPY(in, 0);
-
-               lwerror("GEOSisValid() threw an error: %s", loggederror);
-               PG_RETURN_NULL(); /* I don't think should ever happen */
-       }
-       else if ( ret_char )
-       {
-               /* It's valid at this step, return what we have */
-
-               GEOSGeom_destroy(geosgeom);
-               geosgeom=0;
-
-               out = pglwgeom_serialize(lwgeom_out);
-               lwgeom_release(lwgeom_out);
-               lwgeom_out=0;
-
-               PG_FREE_IF_COPY(in, 0);
-
-               PG_RETURN_POINTER(out);
-       }
-
-       POSTGIS_DEBUGF(3,
-                      "Geometry [%s] is still not valid:",
-                      lwgeom_to_ewkt(lwgeom_out, PARSER_CHECK_NONE));
-       POSTGIS_DEBUGF(3, " %s", loggederror);
-       POSTGIS_DEBUG(3, " will try to clean up further");
-
-       /*
-        * Step 3 : make what we got now (geosgeom) valid
-        */
-
-       switch (GEOSGeomTypeId(geosgeom))
-       {
-       case GEOS_MULTIPOINT:
-       case GEOS_POINT:
-               /* points are always valid, but we might have invalid ordinate values */
-               lwnotice("PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up");
-               PG_RETURN_NULL();
-               break;
-
-       case GEOS_LINESTRING:
-       case GEOS_MULTILINESTRING:
-               lwnotice("LINEAL geometries resulted invalid to GEOS -- dunno how to clean that up");
-               PG_RETURN_NULL();
-               break;
-
-       case GEOS_POLYGON:
-       case GEOS_MULTIPOLYGON:
-       {
-               GEOSGeom tmp = LWGEOM_GEOS_makeValidPolygon(geosgeom);
-               if ( ! tmp )  /* an exception or something */
-               {
-                       /* cleanup and throw */
-                       GEOSGeom_destroy(geosgeom);
-                       lwgeom_release(lwgeom_in);
-                       if ( lwgeom_in != lwgeom_out )
-                       {
-                               lwgeom_release(lwgeom_out);
-                       }
-                       PG_FREE_IF_COPY(in, 0);
-                       lwerror("%s", loggederror);
-                       PG_RETURN_NULL(); /* never get here */
-               }
-               GEOSGeom_destroy(geosgeom); /* input one */
-               geosgeom = tmp;
-               break; /* we've done (geosgeom is the output) */
-       }
-
-       default:
-       {
-               char* tmp = GEOSGeomType(geosgeom);
-               char* typname = pstrdup(tmp);
-               GEOSFree(tmp);
-               lwnotice("ST_MakeValid: doesn't support geometry type: %s",
-                        typname);
-               PG_RETURN_NULL();
-               break;
-       }
-       }
-
-
-       if ( ! geosgeom ) PG_RETURN_NULL();
-
-       /* Now check if every point of input is also found
-        * in output, or abort by returning NULL
-        *
-        * Input geometry was lwgeom_in
-        */
-
-       out = GEOS2POSTGIS(geosgeom, is3d);
-       GEOSGeom_destroy(geosgeom);
-       geosgeom=0;
-
-       PG_RETURN_POINTER(out);
-}
index deb559336854b4fe34d602d9caa26c39c48e9c69..659d8d1f57310899fa2b35c8775fcbb8e1f4c0e3 100644 (file)
@@ -51,3 +51,8 @@ GEOSGeometry * LWGEOM2GEOS(LWGEOM *g);
 POINTARRAY *ptarray_from_GEOSCoordSeq(const GEOSCoordSequence *cs, char want3d);
 void errorIfGeometryCollection(PG_LWGEOM *g1, PG_LWGEOM *g2);
 
+/*
+ * This function would better be moved to the GEOS C-API
+ */
+GEOSGeometry* LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in);
+
diff --git a/postgis/lwgeom_geos_clean.c b/postgis/lwgeom_geos_clean.c
new file mode 100644 (file)
index 0000000..385eb8b
--- /dev/null
@@ -0,0 +1,819 @@
+/**********************************************************************
+ * $Id: lwgeom_geos.c 5258 2010-02-17 21:02:49Z strk $
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ *
+ * Copyright 2009-2010 Sandro Santilli <strk@keybit.net>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ *
+ * ST_MakeValid
+ *
+ * Attempts to make an invalid geometries valid w/out loosing
+ * point sets.
+ *
+ * Polygons may become collection of polygons and lines.
+ * Collapsed rings (or portions of rings) may be dissolved in
+ * polygon area or transformed to linestring if outside any other
+ * ring.
+ *
+ * Author: Sandro Santilli <strk@keybit.net>
+ *
+ * Work done for Regione Toscana - Sistema Informativo per il Governo
+ * del Territorio e dell'Ambiente (RT-SIGTA).
+ *
+ * Thanks to Dr. Horst Duester for previous work on a plpgsql version
+ * of the cleanup logic [1]
+ *
+ * Thanks to Andrea Peri for recommandations on constraints.
+ *
+ * [1] http://www.sogis1.so.ch/sogis/dl/postgis/cleanGeometry.sql
+ *
+ *
+ **********************************************************************/
+
+#include "lwgeom_geos.h"
+#include "funcapi.h"
+
+#include <string.h>
+#include <assert.h>
+
+#if POSTGIS_GEOS_VERSION >= 32
+#endif
+
+/* #define POSTGIS_DEBUG_LEVEL 0 */
+
+
+#define BUFSIZE 256
+static char loggederror[BUFSIZE];
+
+static void
+errorlogger(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+
+       /* Call the supplied function */
+       if ( BUFSIZE-1 < vsnprintf(loggederror, BUFSIZE-1, fmt, ap) )
+       {
+               loggederror[BUFSIZE-1] = '\0';
+       }
+
+       va_end(ap);
+}
+
+/*
+ * Return Nth vertex in GEOSGeometry as a POINT.
+ * May return NULL if the geometry has NO vertexex.
+ */
+GEOSGeometry* LWGEOM_GEOS_getPointN(const GEOSGeometry*, unsigned int);
+GEOSGeometry*
+LWGEOM_GEOS_getPointN(const GEOSGeometry* g_in, unsigned int n)
+{
+       unsigned int dims;
+       const GEOSCoordSequence* seq_in;
+       GEOSCoordSeq seq_out;
+       double val;
+       unsigned int sz;
+       int gn;
+       GEOSGeometry* ret;
+
+       switch ( GEOSGeomTypeId(g_in) )
+       {
+       case GEOS_MULTIPOINT:
+       case GEOS_MULTILINESTRING:
+       case GEOS_MULTIPOLYGON:
+       case GEOS_GEOMETRYCOLLECTION:
+       {
+               for (gn=0; gn<GEOSGetNumGeometries(g_in); ++gn)
+               {
+                       const GEOSGeometry* g = GEOSGetGeometryN(g_in, gn);
+                       ret = LWGEOM_GEOS_getPointN(g,n);
+                       if ( ret ) return ret;
+               }
+               break;
+       }
+
+       case GEOS_POLYGON:
+       {
+               ret = LWGEOM_GEOS_getPointN(GEOSGetExteriorRing(g_in), n);
+               if ( ret ) return ret;
+               for (gn=0; gn<GEOSGetNumInteriorRings(g_in); ++gn)
+               {
+                       const GEOSGeometry* g = GEOSGetInteriorRingN(g_in, gn);
+                       ret = LWGEOM_GEOS_getPointN(g, n);
+                       if ( ret ) return ret;
+               }
+               break;
+       }
+
+       case GEOS_POINT:
+       case GEOS_LINESTRING:
+       case GEOS_LINEARRING:
+               break;
+
+       }
+
+       seq_in = GEOSGeom_getCoordSeq(g_in);
+       if ( ! seq_in ) return NULL;
+       if ( ! GEOSCoordSeq_getSize(seq_in, &sz) ) return NULL;
+       if ( ! sz ) return NULL;
+
+       if ( ! GEOSCoordSeq_getDimensions(seq_in, &dims) ) return NULL;
+
+       seq_out = GEOSCoordSeq_create(1, dims);
+       if ( ! seq_out ) return NULL;
+
+       if ( ! GEOSCoordSeq_getX(seq_in, n, &val) ) return NULL;
+       if ( ! GEOSCoordSeq_setX(seq_out, n, val) ) return NULL;
+       if ( ! GEOSCoordSeq_getY(seq_in, n, &val) ) return NULL;
+       if ( ! GEOSCoordSeq_setY(seq_out, n, val) ) return NULL;
+       if ( dims > 2 )
+       {
+               if ( ! GEOSCoordSeq_getZ(seq_in, n, &val) ) return NULL;
+               if ( ! GEOSCoordSeq_setZ(seq_out, n, val) ) return NULL;
+       }
+
+       return GEOSGeom_createPoint(seq_out);
+}
+
+
+
+LWGEOM * lwcollection_make_geos_friendly(LWCOLLECTION *g);
+LWGEOM * lwline_make_geos_friendly(LWLINE *line);
+LWGEOM * lwpoly_make_geos_friendly(LWPOLY *poly);
+POINTARRAY* ring_make_geos_friendly(POINTARRAY* ring);
+
+/*
+ * Ensure the geometry is "structurally" valid
+ * (enough for GEOS to accept it)
+ * May return the input untouched (if already valid).
+ * May return geometries of lower dimension (on collapses)
+ */
+static LWGEOM *
+lwgeom_make_geos_friendly(LWGEOM *geom)
+{
+       LWDEBUGF(2, "lwgeom_make_geos_friendly enter (type %d)", TYPE_GETTYPE(geom->type));
+       switch (TYPE_GETTYPE(geom->type))
+       {
+       case POINTTYPE:
+       case MULTIPOINTTYPE:
+               /* a point is always valid */
+               return geom;
+               break;
+
+       case LINETYPE:
+               /* lines need at least 2 points */
+               return lwline_make_geos_friendly((LWLINE *)geom);
+               break;
+
+       case POLYGONTYPE:
+               /* polygons need all rings closed and with npoints > 3 */
+               return lwpoly_make_geos_friendly((LWPOLY *)geom);
+               break;
+
+       case MULTILINETYPE:
+       case MULTIPOLYGONTYPE:
+       case COLLECTIONTYPE:
+               return lwcollection_make_geos_friendly((LWCOLLECTION *)geom);
+               break;
+
+       case CIRCSTRINGTYPE:
+       case COMPOUNDTYPE:
+       case CURVEPOLYTYPE:
+       case MULTISURFACETYPE:
+       case MULTICURVETYPE:
+       default:
+               lwerror("unsupported input geometry type: %d", TYPE_GETTYPE(geom->type));
+               break;
+       }
+       return 0;
+}
+
+/*
+ * Close the point array, if not already closed in 2d.
+ * Returns the input if already closed in 2d, or a newly
+ * constructed POINTARRAY.
+ * TODO: move in ptarray.c
+ */
+POINTARRAY* ptarray_close2d(POINTARRAY* ring);
+POINTARRAY*
+ptarray_close2d(POINTARRAY* ring)
+{
+       POINTARRAY* newring;
+
+       /* close the ring if not already closed (2d only) */
+       if ( ! ptarray_isclosed2d(ring) )
+       {
+               /* close it up */
+               newring = ptarray_addPoint(ring,
+                                          getPoint_internal(ring, 0),
+                                          TYPE_NDIMS(ring->dims),
+                                          ring->npoints);
+               ring = newring;
+       }
+       return ring;
+}
+
+/* May return the same input or a new one (never zero) */
+POINTARRAY*
+ring_make_geos_friendly(POINTARRAY* ring)
+{
+       POINTARRAY* closedring;
+
+       /* close the ring if not already closed (2d only) */
+       closedring = ptarray_close2d(ring);
+       if (closedring != ring )
+       {
+               ptarray_free(ring); /* should we do this ? */
+               ring = closedring;
+       }
+
+       /* return 0 for collapsed ring (after closeup) */
+
+       while ( ring->npoints < 4 )
+       {
+               LWDEBUGF(4, "ring has %d points, adding another", ring->npoints);
+               /* let's add another... */
+               ring = ptarray_addPoint(ring,
+                                       getPoint_internal(ring, 0),
+                                       TYPE_NDIMS(ring->dims),
+                                       ring->npoints);
+       }
+
+
+       return ring;
+}
+
+/* Make sure all rings are closed and have > 3 points.
+ * May return the input untouched.
+ */
+LWGEOM *
+lwpoly_make_geos_friendly(LWPOLY *poly)
+{
+       LWGEOM* ret;
+       POINTARRAY **new_rings;
+       int i;
+
+       /* If the polygon has no rings there's nothing to do */
+       if ( ! poly->nrings ) return (LWGEOM*)poly;
+
+       /* Allocate enough pointers for all rings */
+       new_rings = lwalloc(sizeof(POINTARRAY*)*poly->nrings);
+
+       /* All rings must be closed and have > 3 points */
+       for (i=0; i<poly->nrings; i++)
+       {
+               POINTARRAY* ring_in = poly->rings[i];
+               POINTARRAY* ring_out = ring_make_geos_friendly(ring_in);
+
+               if ( ring_in != ring_out )
+               {
+                       LWDEBUGF(3, "lwpoly_make_geos_friendly: ring %d cleaned, now has %d points", i, ring_out->npoints);
+                       /* this may come right from
+                        * the binary representation lands
+                        */
+                       /*ptarray_free(ring_in); */
+               }
+               else
+               {
+                       LWDEBUGF(3, "lwpoly_make_geos_friendly: ring %d untouched", i);
+               }
+
+               assert ( ring_out );
+               new_rings[i] = ring_out;
+       }
+
+       lwfree(poly->rings);
+       poly->rings = new_rings;
+       ret = (LWGEOM*)poly;
+
+       return ret;
+}
+
+/* Need NO or >1 points. Duplicate first if only one. */
+LWGEOM *
+lwline_make_geos_friendly(LWLINE *line)
+{
+       LWGEOM *ret;
+
+       if (line->points->npoints == 1) /* 0 is fine, 2 is fine */
+       {
+#if 1
+               /* Duplicate point */
+               line->points = ptarray_addPoint(line->points,
+                                               getPoint_internal(line->points, 0),
+                                               TYPE_NDIMS(line->points->dims),
+                                               line->points->npoints);
+               ret = (LWGEOM*)line;
+#else
+               /* Turn into a point */
+               ret = (LWGEOM*)lwpoint_construct(line->SRID, 0, line->points);
+#endif
+               return ret;
+       }
+       else
+       {
+               return (LWGEOM*)line;
+               /* return lwline_clone(line); */
+       }
+}
+
+LWGEOM *
+lwcollection_make_geos_friendly(LWCOLLECTION *g)
+{
+       LWGEOM **new_geoms;
+       uint32 i, new_ngeoms=0;
+       LWCOLLECTION *ret;
+
+       /* enough space for all components */
+       new_geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
+
+       ret = lwalloc(sizeof(LWCOLLECTION));
+       memcpy(ret, g, sizeof(LWCOLLECTION));
+
+       for (i=0; i<g->ngeoms; i++)
+       {
+               LWGEOM* newg = lwgeom_make_geos_friendly(g->geoms[i]);
+               if ( newg ) new_geoms[new_ngeoms++] = newg;
+       }
+
+       ret->bbox = 0; /* recompute later... */
+
+       ret->ngeoms = new_ngeoms;
+       if ( new_ngeoms )
+       {
+               ret->geoms = new_geoms;
+       }
+       else
+       {
+               free(new_geoms);
+               ret->geoms = 0;
+       }
+
+       return (LWGEOM*)ret;
+}
+
+/*
+ * Fully node given linework
+ * TODO: move to GEOS capi
+ */
+static GEOSGeometry*
+LWGEOM_GEOS_nodeLines(GEOSGeometry* lines)
+{
+       GEOSGeometry* noded;
+       GEOSGeometry* point;
+
+       /*
+        * Union with first geometry point, obtaining full noding
+        * and dissolving of duplicated repeated points
+        *
+        * TODO: substitute this with UnaryUnion?
+        */
+
+       point = LWGEOM_GEOS_getPointN(lines, 0);
+       if ( ! point ) return NULL;
+
+       POSTGIS_DEBUGF(3,
+                      "Boundary point: %s",
+                      lwgeom_to_ewkt(GEOS2LWGEOM(point, 0),
+                                     PARSER_CHECK_NONE));
+
+       noded = GEOSUnion(lines, point);
+       if ( NULL == noded )
+       {
+               GEOSGeom_destroy(point);
+               return NULL;
+       }
+
+       GEOSGeom_destroy(point);
+
+       POSTGIS_DEBUGF(3,
+                      "LWGEOM_GEOS_nodeLines: in[%s] out[%s]",
+                      lwgeom_to_ewkt(GEOS2LWGEOM(lines, 0),
+                                     PARSER_CHECK_NONE),
+                      lwgeom_to_ewkt(GEOS2LWGEOM(noded, 0),
+                                     PARSER_CHECK_NONE));
+
+       return noded;
+}
+
+/*
+ * We expect initGEOS being called already.
+ * Will return NULL on error (expect error handler being called by then)
+ *
+ */
+static GEOSGeometry*
+LWGEOM_GEOS_makeValidPolygon(const GEOSGeometry* gin)
+{
+       GEOSGeom gout;
+       GEOSGeom geos_bound, geos_bound_noded;
+       GEOSGeom geos_cut_edges, geos_area;
+       GEOSGeometry *vgeoms[2]; /* One for area, one for cut-edges */
+       int num_cut_edges;
+
+       assert (GEOSGeomTypeId(gin) == GEOS_POLYGON ||
+               GEOSGeomTypeId(gin) == GEOS_MULTIPOLYGON);
+
+       geos_bound = GEOSBoundary(gin);
+       if ( NULL == geos_bound )
+       {
+               return NULL;
+       }
+
+       POSTGIS_DEBUGF(3,
+                      "Boundaries: %s",
+                      lwgeom_to_ewkt(GEOS2LWGEOM(geos_bound, 0),
+                                     PARSER_CHECK_NONE));
+
+       /* Node boundaries */
+
+       geos_bound_noded = LWGEOM_GEOS_nodeLines(geos_bound);
+       if ( NULL == geos_bound_noded )
+       {
+               lwnotice("LWGEOM_GEOS_nodeLines(): %s", loggederror);
+               GEOSGeom_destroy(geos_bound);
+               return NULL;
+       }
+
+       GEOSGeom_destroy(geos_bound);
+
+       POSTGIS_DEBUGF(3,
+                      "Noded: %s",
+                      lwgeom_to_ewkt(GEOS2LWGEOM(geos_bound_noded, 0),
+                                     PARSER_CHECK_NONE));
+
+       geos_area = LWGEOM_GEOS_buildArea(geos_bound_noded);
+       if ( ! geos_area ) /* must be an exception ... */
+       {
+               /* cleanup */
+               GEOSGeom_destroy(geos_bound_noded);
+               lwnotice("LWGEOM_GEOS_buildArea() threw an error: %s",
+                        loggederror);
+               return NULL;
+       }
+
+       if ( GEOSisEmpty(geos_area) )
+       {
+               /* no area, it's all boundary */
+               POSTGIS_DEBUG(3, "No area found, returning noded bounds");
+               GEOSGeom_destroy(geos_area);
+               return geos_bound_noded;
+       }
+
+       /* Compute what's left out from original boundary
+        * (this would be the so called 'cut lines' */
+       geos_bound = GEOSBoundary(geos_area);
+       if ( ! geos_bound )
+       {
+               /* Empty area most likely,
+                * noded original bounds are the result */
+               lwnotice("GEOSBoundary('%s') threw an error: %s",
+                        lwgeom_to_ewkt(GEOS2LWGEOM(geos_area, 0),
+                                       PARSER_CHECK_NONE),
+                        loggederror);
+               return geos_bound_noded;
+       }
+
+       geos_cut_edges = GEOSDifference(geos_bound_noded, geos_bound);
+#if 0
+       {
+               GEOSGeometry const *vgeoms[1];
+               vgeoms[0] = geos_bound_noded;
+               geos_cut_edges = GEOSPolygonizer_getCutEdges(vgeoms, 1);
+       }
+#endif
+       GEOSGeom_destroy(geos_bound);
+       if ( ! geos_cut_edges )   /* an exception ? */
+       {
+               /* cleanup and throw */
+               GEOSGeom_destroy(geos_bound_noded);
+               GEOSGeom_destroy(geos_area);
+               lwnotice("GEOSDifference() threw an error: %s",
+                        loggederror);
+               return NULL;
+       }
+
+       POSTGIS_DEBUGF(3,
+                      "Cut-edges: %s",
+                      lwgeom_to_ewkt(GEOS2LWGEOM(geos_cut_edges, 0),
+                                     PARSER_CHECK_NONE));
+
+       num_cut_edges = GEOSGetNumGeometries(geos_cut_edges);
+       if ( -1 == num_cut_edges )
+       {
+               lwnotice("GEOSGetNumGeometries() threw an error: %s",
+                        loggederror);
+               GEOSGeom_destroy(geos_cut_edges);
+               GEOSGeom_destroy(geos_bound_noded);
+               return geos_area;
+       }
+
+       /* If we have no cut edges the result is the area */
+       /* NOTE: this is a short-cut we may want to drop */
+       if ( 0 == num_cut_edges )
+       {
+               GEOSGeom_destroy(geos_cut_edges);
+               GEOSGeom_destroy(geos_bound_noded);
+               return geos_area;
+       }
+
+       /*
+        * Drop cut-edge segments for which all
+        * vertices are already vertices of geos_bound_noded
+        * which fall completely in the area
+        */
+       {
+
+               GEOSGeometry** cutlines;
+               GEOSGeometry* geos_bound_noded_points;
+               int kept_edges = 0;
+               int i;
+
+               geos_bound_noded_points = GEOSGeom_extractUniquePoints(geos_area);
+               if ( ! geos_bound_noded_points )
+               {
+                       /* Exception I guess ? */
+                       lwnotice("GEOSGeom_extractUniquePoints: %s", loggederror);
+                       return 0;
+               }
+
+               /* Allocate enough space for keeping all cut edges */
+               cutlines = (GEOSGeometry**)lwalloc(sizeof(GEOSGeometry*)*num_cut_edges);
+               for (i=0; i<num_cut_edges; ++i)
+               {
+                       char ret;
+                       GEOSGeometry* xset;
+                       const GEOSGeometry* cutline = GEOSGetGeometryN(geos_cut_edges, i);
+                       GEOSGeometry* cutline_points = GEOSGeom_extractUniquePoints(cutline);
+                       if ( ! cutline_points )
+                       {
+                               /* Exception I guess ? */
+                               lwnotice("GEOSGeom_extractUniquePoints: %s", loggederror);
+                               GEOSGeom_destroy(geos_cut_edges);
+                               GEOSGeom_destroy(geos_bound_noded_points);
+                               lwfree(cutlines);
+                               return 0;
+                       }
+
+                       xset = GEOSDifference(cutline_points, geos_bound_noded_points);
+                       GEOSGeom_destroy(cutline_points);
+                       if ( ! xset )
+                       {
+                               /* exception */
+                               lwnotice("GEOSDifference: %s", loggederror);
+                               GEOSGeom_destroy(geos_cut_edges);
+                               GEOSGeom_destroy(geos_bound_noded_points);
+                               lwfree(cutlines);
+                               return 0;
+                       }
+                       ret = GEOSisEmpty(xset);
+                       GEOSGeom_destroy(xset);
+                       if ( 2 == ret )   /* exception */
+                       {
+                               lwnotice("GEOSisEmpty: %s", loggederror);
+                               GEOSGeom_destroy(geos_cut_edges);
+                               GEOSGeom_destroy(geos_bound_noded_points);
+                               lwfree(cutlines);
+                               return 0;
+                       }
+                       if ( 1 == ret )
+                       {
+                               /* all points already represented */
+                               POSTGIS_DEBUGF(3,
+                                              "Cut-edge: %s - vertices all found, drop",
+                                              lwgeom_to_ewkt(GEOS2LWGEOM(cutline, 0),
+                                                             PARSER_CHECK_NONE));
+                               continue;
+                       }
+
+                       POSTGIS_DEBUGF(3,
+                                      "Cut-edge: %s - missing vertices, keep",
+                                      lwgeom_to_ewkt(GEOS2LWGEOM(cutline, 0),
+                                                     PARSER_CHECK_NONE));
+
+                       cutlines[kept_edges++] = GEOSGeom_clone(cutline);
+               }
+
+               GEOSGeom_destroy(geos_bound_noded_points);
+               GEOSGeom_destroy(geos_cut_edges);
+
+               if ( kept_edges )
+               {
+                       geos_cut_edges = GEOSGeom_createCollection(GEOS_MULTILINESTRING, cutlines, kept_edges);
+                       if ( ! geos_cut_edges )
+                       {
+                               lwnotice("GEOSGeom_createCollection: %s", loggederror);
+                               return 0;
+                       }
+
+                       POSTGIS_DEBUGF(3,
+                                      "Trimmed cut-edges: %s",
+                                      lwgeom_to_ewkt(GEOS2LWGEOM(geos_cut_edges, 0),
+                                                     PARSER_CHECK_NONE));
+               }
+               else
+               {
+                       geos_cut_edges = 0;
+               }
+
+       }
+
+       GEOSGeom_destroy(geos_bound_noded); /* TODO: move up */
+
+       /* Collect areas and lines (if any line) */
+       if ( geos_cut_edges )
+       {
+               vgeoms[1] = geos_area;
+               vgeoms[0] = geos_cut_edges;
+               gout = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, vgeoms, 2);
+               if ( ! gout )   /* an exception again */
+               {
+                       /* cleanup and throw */
+                       lwnotice("GEOSGeom_createCollection() threw an error: %s",
+                                loggederror);
+                       return NULL;
+               }
+
+       }
+       else
+       {
+               gout = geos_area;
+       }
+
+       return gout;
+}
+
+Datum st_makevalid(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(st_makevalid);
+Datum st_makevalid(PG_FUNCTION_ARGS)
+{
+#if POSTGIS_GEOS_VERSION < 33
+       elog(ERROR, "You need GEOS-3.3.0 or up for ST_MakeValid");
+       PG_RETURN_NULL();
+#else /* POSTGIS_GEOS_VERSION >= 33 */
+
+       PG_LWGEOM *in, *out;
+       GEOSGeom geosgeom;
+       LWGEOM *lwgeom_in, *lwgeom_out;
+       /* LWGEOM *lwgeom_pointset; */
+       char ret_char;
+       int is3d;
+
+       in = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       lwgeom_in = lwgeom_deserialize(SERIALIZED_FORM(in));
+
+       is3d = TYPE_HASZ(lwgeom_in->type);
+
+       /*
+        * Step 1 : try to convert to GEOS, if impossible, clean that up first
+        *          otherwise (adding only duplicates of existing points)
+        */
+
+       initGEOS(errorlogger, errorlogger);
+
+
+       lwgeom_out = lwgeom_in;
+       geosgeom = LWGEOM2GEOS(lwgeom_out);
+       if ( ! geosgeom )
+       {
+               POSTGIS_DEBUGF(4,
+                              "Original geom can't be converted to GEOS (%s)"
+                              " - will try cleaning that up first",
+                              loggederror);
+
+
+               lwgeom_out = lwgeom_make_geos_friendly(lwgeom_out);
+               if ( ! lwgeom_out )
+               {
+                       lwerror("Could not make a valid geometry out of input");
+               }
+
+               /* try again as we did cleanup now */
+               geosgeom = LWGEOM2GEOS(lwgeom_out);
+               if ( ! geosgeom )
+               {
+                       lwerror("Couldn't convert POSTGIS geom to GEOS: %s",
+                               loggederror);
+                       PG_RETURN_NULL();
+               }
+
+       }
+       else
+       {
+               POSTGIS_DEBUG(4, "original geom converted to GEOS");
+               lwgeom_out = lwgeom_in;
+       }
+
+
+       /*
+        * Step 2 : return the resulting geometry if it's now valid
+        */
+
+       ret_char = GEOSisValid(geosgeom);
+       if ( ret_char == 2 )
+       {
+               GEOSGeom_destroy(geosgeom);
+               geosgeom=0;
+
+               PG_FREE_IF_COPY(in, 0);
+
+               lwerror("GEOSisValid() threw an error: %s", loggederror);
+               PG_RETURN_NULL(); /* I don't think should ever happen */
+       }
+       else if ( ret_char )
+       {
+               /* It's valid at this step, return what we have */
+
+               GEOSGeom_destroy(geosgeom);
+               geosgeom=0;
+
+               out = pglwgeom_serialize(lwgeom_out);
+               lwgeom_release(lwgeom_out);
+               lwgeom_out=0;
+
+               PG_FREE_IF_COPY(in, 0);
+
+               PG_RETURN_POINTER(out);
+       }
+
+       POSTGIS_DEBUGF(3,
+                      "Geometry [%s] is still not valid:",
+                      lwgeom_to_ewkt(lwgeom_out, PARSER_CHECK_NONE));
+       POSTGIS_DEBUGF(3, " %s", loggederror);
+       POSTGIS_DEBUG(3, " will try to clean up further");
+
+       /*
+        * Step 3 : make what we got now (geosgeom) valid
+        */
+
+       switch (GEOSGeomTypeId(geosgeom))
+       {
+       case GEOS_MULTIPOINT:
+       case GEOS_POINT:
+               /* points are always valid, but we might have invalid ordinate values */
+               lwnotice("PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up");
+               PG_RETURN_NULL();
+               break;
+
+       case GEOS_LINESTRING:
+       case GEOS_MULTILINESTRING:
+               lwnotice("LINEAL geometries resulted invalid to GEOS -- dunno how to clean that up");
+               PG_RETURN_NULL();
+               break;
+
+       case GEOS_POLYGON:
+       case GEOS_MULTIPOLYGON:
+       {
+               GEOSGeom tmp = LWGEOM_GEOS_makeValidPolygon(geosgeom);
+               if ( ! tmp )  /* an exception or something */
+               {
+                       /* cleanup and throw */
+                       GEOSGeom_destroy(geosgeom);
+                       lwgeom_release(lwgeom_in);
+                       if ( lwgeom_in != lwgeom_out )
+                       {
+                               lwgeom_release(lwgeom_out);
+                       }
+                       PG_FREE_IF_COPY(in, 0);
+                       lwerror("%s", loggederror);
+                       PG_RETURN_NULL(); /* never get here */
+               }
+               GEOSGeom_destroy(geosgeom); /* input one */
+               geosgeom = tmp;
+               break; /* we've done (geosgeom is the output) */
+       }
+
+       default:
+       {
+               char* tmp = GEOSGeomType(geosgeom);
+               char* typname = pstrdup(tmp);
+               GEOSFree(tmp);
+               lwnotice("ST_MakeValid: doesn't support geometry type: %s",
+                        typname);
+               PG_RETURN_NULL();
+               break;
+       }
+       }
+
+
+       if ( ! geosgeom ) PG_RETURN_NULL();
+
+       /* Now check if every point of input is also found
+        * in output, or abort by returning NULL
+        *
+        * Input geometry was lwgeom_in
+        */
+
+       out = GEOS2POSTGIS(geosgeom, is3d);
+       GEOSGeom_destroy(geosgeom);
+       geosgeom=0;
+
+       PG_RETURN_POINTER(out);
+#endif /* POSTGIS_GEOS_VERSION >= 33 */
+}
index 52ef890c85bce1a13d8159b8c44b6332e1af4a98..055c01b9a0902b4b84d17864b579ea97160f648f 100644 (file)
@@ -70,7 +70,6 @@ TESTS = \
        dumppoints \
        wmsservers \
        tickets \
-       clean \
        remove_repeated_points
 
 # Styled buffer only if GEOS >= 3.2
@@ -83,6 +82,11 @@ ifeq ($(shell expr $(POSTGIS_GEOS_VERSION) ">=" 32),1)
        TESTS += hausdorff
 endif
 
+# ST_MakeClean if GEOS > 3.2
+#ifeq ($(shell expr $(POSTGIS_GEOS_VERSION) ">" 32),1)
+#      TESTS += clean
+#endif
+
 
 all: test