]> granicus.if.org Git - postgis/commitdiff
Allow retaining badly collapsed rings (single-point) as points in ST_MakeValid. Add...
authorSandro Santilli <strk@keybit.net>
Wed, 17 Feb 2010 21:02:49 +0000 (21:02 +0000)
committerSandro Santilli <strk@keybit.net>
Wed, 17 Feb 2010 21:02:49 +0000 (21:02 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@5258 b70326c6-7e19-0410-871a-916f4a2858ee

postgis/lwgeom_geos.c
regress/clean.sql
regress/clean_expected

index e56f403a266fb557f1ce301c292715201fd2db2b..9af8c975aef1f5331f8c270f6c883b6a096c6b66 100644 (file)
@@ -214,6 +214,81 @@ 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)
 {
@@ -4002,15 +4077,14 @@ ring_make_valid(POINTARRAY* ring)
 
        /* return 0 for collapsed ring (after closeup) */
 
-       if ( ring->npoints < 4 )
+       while ( ring->npoints < 4 )
        {
                LWDEBUGF(4, "ring has %d points, adding another", ring->npoints);
                /* let's add another... */
-               closedring = ptarray_addPoint(closedring,
-                                             getPoint_internal(closedring, 0),
-                                             TYPE_NDIMS(closedring->dims),
-                                             closedring->npoints);
-               return closedring;
+               ring = ptarray_addPoint(ring,
+                                       getPoint_internal(ring, 0),
+                                       TYPE_NDIMS(ring->dims),
+                                       ring->npoints);
        }
 
 
@@ -4041,7 +4115,7 @@ lwpoly_make_valid(LWPOLY *poly)
 
                if ( ring_in != ring_out )
                {
-                       LWDEBUGF(3, "lwpoly_make_valid: ring %d cleaned", i);
+                       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
                         */
@@ -4126,7 +4200,11 @@ lwcollection_make_valid(LWCOLLECTION *g)
        return (LWGEOM*)ret;
 }
 
-/* We expect initGEOS being called already */
+/*
+ * 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)
@@ -4150,23 +4228,25 @@ LWGEOM_GEOS_makeValidPolygon(const GEOSGeometry* gin)
                                      PARSER_CHECK_NONE));
 
        /*
-        * Union with an empty point, obtaining full noding
+        * Union with first geometry point, obtaining full noding
         * and dissolving of duplicated repeated points
         *
         * TODO: substitute this with UnaryUnion?
         *
-        * need a point on the line here rather
-        * than an arbitrary one ?
         */
-       geos_tmp_point = GEOSGeom_createPoint(0);
-       // GEOSPointOnSurface(geos_bound);
+       geos_tmp_point = LWGEOM_GEOS_getPointN(geos_bound, 0);
        if ( ! geos_tmp_point )
        {
-               lwnotice("GEOSGeom_createPoint(0): %s", loggederror);
+               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 )
        {
@@ -4347,7 +4427,10 @@ Datum st_makevalid(PG_FUNCTION_ARGS)
                        /* cleanup and throw */
                        GEOSGeom_destroy(geosgeom);
                        lwgeom_release(lwgeom_in);
-                       lwgeom_release(lwgeom_out);
+                       if ( lwgeom_in != lwgeom_out )
+                       {
+                               lwgeom_release(lwgeom_out);
+                       }
                        PG_FREE_IF_COPY(in, 0);
                        lwerror("%s", loggederror);
                        PG_RETURN_NULL(); /* never get here */
index dea5105325ae05daa34cdd544d3e0e27550011b7..c141472c64c84fca81cb03a0daeb65c06a19350b 100644 (file)
@@ -18,8 +18,13 @@ RT   12      0103000000020000000500000000000000008040C00000000000002C4000000000008040C0
 RT     13.2    0103000000010000000700000000000000000044C000000000000039400000000000003EC000000000000039400000000000003EC000000000008041400000000000003EC000000000008046400000000000003EC0000000000080414000000000000044C0000000000080414000000000000044C00000000000003940      0107000000020000000102000000020000000000000000003EC000000000008041400000000000003EC00000000000804640010300000001000000050000000000000000003EC000000000008041400000000000003EC0000000000000394000000000000044C0000000000000394000000000000044C000000000008041400000000000003EC00000000000804140
 RT     14      01030000000100000009000000000000000000004000000000000018C0000000000000004000000000000010C0000000000000104000000000000010C0000000000000204000000000000018C0000000000000244000000000000018C0000000000000244000000000000010C0000000000000204000000000000010C0000000000000104000000000000018C0000000000000004000000000000018C0      01060000000200000001030000000100000006000000000000000000004000000000000018C0000000000000004000000000000010C0000000000000104000000000000010C0000000000000184000000000000014C0000000000000104000000000000018C0000000000000004000000000000018C001030000000100000006000000000000000000184000000000000014C0000000000000204000000000000010C0000000000000244000000000000010C0000000000000244000000000000018C0000000000000204000000000000018C0000000000000184000000000000014C0
 RT     15      0103000000010000000B00000000000000000040400000000000002440000000000000404000000000000034400000000000004540000000000000344000000000000045400000000000002E40000000000000454000000000000024400000000000004A4000000000000024400000000000004A4000000000000034400000000000004540000000000000344000000000000045400000000000002E400000000000004540000000000000244000000000000040400000000000002440      010300000001000000070000000000000000004040000000000000244000000000000040400000000000003440000000000000454000000000000034400000000000004A4000000000000034400000000000004A4000000000000024400000000000004540000000000000244000000000000040400000000000002440
+PG     1       0103000000010000000100000000000000000000000000000000000000      010100000000000000000000000000000000000000
 \.
 
+-- PG.1 : polygon with single ring with single point in it
+--        to be converted to a POINT
+--
+
 SELECT caseno,
        st_astext(st_makevalid(orig)) = st_astext(valid),
        st_isvalid(st_makevalid(orig)), -- paranoia
index e09a04de2c98436687654a324fc9b06e20dc592e..182db614ccc62983a62453b885939e02ef65bf70 100644 (file)
@@ -14,3 +14,4 @@
 13.2|t|t|f
 14|t|t|f
 15|t|t|f
+1|t|t|f