]> granicus.if.org Git - postgis/commitdiff
Ptarray in place repeated point removal returns, passing all tests, even in topology...
authorPaul Ramsey <pramsey@cleverelephant.ca>
Tue, 10 Oct 2017 16:27:08 +0000 (16:27 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Tue, 10 Oct 2017 16:27:08 +0000 (16:27 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@15943 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/cunit/cu_algorithm.c
liblwgeom/cunit/cu_geos.c
liblwgeom/liblwgeom_internal.h
liblwgeom/lwcollection.c
liblwgeom/lwgeom.c
liblwgeom/lwline.c
liblwgeom/lwmpoint.c
liblwgeom/lwpoly.c
liblwgeom/ptarray.c

index ae85074377467f5b881afa2ae35c3cea646707f5..eca7399313a9c991134b0e80c304f5b9b6c8fca4 100644 (file)
@@ -1003,6 +1003,39 @@ static void test_geohash_point_as_int(void)
        CU_ASSERT_EQUAL(gh, rs);
 }
 
+static void test_lwgeom_remove_repeated_points(void)
+{
+       LWGEOM *g;
+       char *ewkt;
+
+       g = lwgeom_from_wkt("MULTIPOINT(0 0, 10 0, 10 10, 10 10, 0 10, 0 10, 0 10, 0 0, 0 0, 0 0, 5 5, 0 0, 5 5)", LW_PARSER_CHECK_NONE);
+       lwgeom_remove_repeated_points_in_place(g, 1);
+       ewkt = lwgeom_to_ewkt(g);
+       CU_ASSERT_STRING_EQUAL(ewkt, "MULTIPOINT(0 0,10 0,10 10,0 10,5 5)");
+       // printf("%s\n", ewkt);
+       lwgeom_free(g);
+       lwfree(ewkt);
+
+       g = lwgeom_from_wkt("LINESTRING(1612830.15445 4841287.12672,1612830.15824 4841287.12674,1612829.98813 4841274.56198)", LW_PARSER_CHECK_NONE);
+       lwgeom_remove_repeated_points_in_place(g, 0.01);
+       ewkt = lwgeom_to_ewkt(g);
+       CU_ASSERT_STRING_EQUAL(ewkt, "LINESTRING(1612830.15445 4841287.12672,1612829.98813 4841274.56198)");
+       // printf("%s\n", ewkt);
+       lwgeom_free(g);
+       lwfree(ewkt);
+
+
+       g = lwgeom_from_wkt("MULTIPOINT(0 0,10 0,10 10,10 10,0 10,0 10,0 10,0 0,0 0,0 0,5 5,5 5,5 8,8 8,8 8,8 8,8 5,8 5,5 5,5 5,5 5,5 5,5 5,50 50,50 50,50 50,50 60,50 60,50 60,60 60,60 50,60 50,50 50,55 55,55 58,58 58,58 55,58 55,55 55)", LW_PARSER_CHECK_NONE);
+       lwgeom_remove_repeated_points_in_place(g, 1);
+       ewkt = lwgeom_to_ewkt(g);
+       CU_ASSERT_STRING_EQUAL(ewkt, "MULTIPOINT(0 0,10 0,10 10,0 10,5 5,5 8,8 8,8 5,50 50,50 60,60 60,60 50,55 55,55 58,58 58,58 55)");
+       // printf("%s\n", ewkt);
+       lwgeom_free(g);
+       lwfree(ewkt);
+
+
+}
+
 static void test_lwgeom_simplify(void)
 {
        LWGEOM *l;
@@ -1292,4 +1325,5 @@ void algorithms_suite_setup(void)
        PG_ADD_TEST(suite,test_median_handles_3d_correctly);
        PG_ADD_TEST(suite,test_median_robustness);
        PG_ADD_TEST(suite,test_lwpoly_construct_circle);
+       PG_ADD_TEST(suite,test_lwgeom_remove_repeated_points);
 }
index ec45d66dab018cea7b7beb5c4831c7c72123a0b9..e39163b5ee26c0c86d110f06149360e4c93b4494 100644 (file)
@@ -51,7 +51,7 @@ static void test_geos_noop(void)
                geom_in = lwgeom_from_wkt(in_ewkt, LW_PARSER_CHECK_NONE);
                geom_out = lwgeom_geos_noop(geom_in);
                if ( ! geom_out ) {
-                       fprintf(stderr, "\nNull return from lwgeom_geos_noop with wkt:   %s\n", in_ewkt);
+                       // fprintf(stderr, "\nNull return from lwgeom_geos_noop with wkt:   %s\n", in_ewkt);
                        lwgeom_free(geom_in);
                        continue;
                }
index 871224a26b2a73988cbeb8c937d30e5a0a1ef8b5..7bcbabd67a013a0d5d4260185272baaac9372589 100644 (file)
@@ -398,12 +398,10 @@ void closest_point_on_segment(const POINT4D *R, const POINT4D *A, const POINT4D
 /*
 * Repeated points
 */
-POINTARRAY *ptarray_remove_repeated_points_minpoints(const POINTARRAY *in, double tolerance, int minpoints);
 POINTARRAY *ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance);
-LWGEOM* lwmpoint_remove_repeated_points(const LWMPOINT *in, double tolerance);
 LWGEOM* lwline_remove_repeated_points(const LWLINE *in, double tolerance);
-LWGEOM* lwcollection_remove_repeated_points(const LWCOLLECTION *in, double tolerance);
-LWGEOM* lwpoly_remove_repeated_points(const LWPOLY *in, double tolerance);
+void ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, int min_points);
+void lwgeom_remove_repeated_points_in_place(LWGEOM *geom, double tolerance);
 
 /*
 * Closure test
index 79788c9071fbbe78b8ca74db83a559c4998ac8ec..9b233c2ca867b3f1b5cbb9825d3f80ba60af7976 100644 (file)
@@ -456,23 +456,6 @@ LWCOLLECTION* lwcollection_extract(LWCOLLECTION *col, int type)
        return outcol;
 }
 
-LWGEOM*
-lwcollection_remove_repeated_points(const LWCOLLECTION *coll, double tolerance)
-{
-       uint32_t i;
-       LWGEOM **newgeoms;
-
-       newgeoms = lwalloc(sizeof(LWGEOM *)*coll->ngeoms);
-       for (i=0; i<coll->ngeoms; i++)
-       {
-               newgeoms[i] = lwgeom_remove_repeated_points(coll->geoms[i], tolerance);
-       }
-
-       return (LWGEOM*)lwcollection_construct(coll->type,
-                                              coll->srid, coll->bbox ? gbox_copy(coll->bbox) : NULL,
-                                              coll->ngeoms, newgeoms);
-}
-
 
 LWCOLLECTION*
 lwcollection_force_dims(const LWCOLLECTION *col, int hasz, int hasm)
index 85a8654f82af3a923f3aa33f418311674112ce5f..28ff179a87de68138e42fd827f508cd03c0bf47c 100644 (file)
@@ -1486,53 +1486,9 @@ extern int lwgeom_dimensionality(const LWGEOM *geom)
 
 extern LWGEOM* lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance)
 {
-       LWDEBUGF(4, "lwgeom_remove_repeated_points got type %s",
-                lwtype_name(in->type));
-
-       if(lwgeom_is_empty(in))
-       {
-               return lwgeom_clone_deep(in);
-       }
-
-       switch (in->type)
-       {
-       case MULTIPOINTTYPE:
-               return lwmpoint_remove_repeated_points((LWMPOINT*)in, tolerance);
-               break;
-       case LINETYPE:
-               return lwline_remove_repeated_points((LWLINE*)in, tolerance);
-
-       case MULTILINETYPE:
-       case COLLECTIONTYPE:
-       case MULTIPOLYGONTYPE:
-       case POLYHEDRALSURFACETYPE:
-               return lwcollection_remove_repeated_points((LWCOLLECTION *)in, tolerance);
-
-       case POLYGONTYPE:
-               return lwpoly_remove_repeated_points((LWPOLY *)in, tolerance);
-               break;
-
-       case POINTTYPE:
-       case TRIANGLETYPE:
-       case TINTYPE:
-               /* No point is repeated for a single point, or for Triangle or TIN */
-               return lwgeom_clone_deep(in);
-
-       case CIRCSTRINGTYPE:
-       case COMPOUNDTYPE:
-       case MULTICURVETYPE:
-       case CURVEPOLYTYPE:
-       case MULTISURFACETYPE:
-               /* Dunno how to handle these, will return untouched */
-               return lwgeom_clone_deep(in);
-
-       default:
-               lwnotice("%s: unsupported geometry type: %s",
-                        __func__, lwtype_name(in->type));
-               return lwgeom_clone_deep(in);
-               break;
-       }
-       return 0;
+       LWGEOM *out = lwgeom_clone_deep(in);
+       lwgeom_remove_repeated_points_in_place(out, tolerance);
+       return out;
 }
 
 void lwgeom_swap_ordinates(LWGEOM *in, LWORD o1, LWORD o2)
@@ -1624,9 +1580,151 @@ void lwgeom_set_srid(LWGEOM *geom, int32_t srid)
        }
 }
 
+
 /**************************************************************/
 
 
+void
+lwgeom_remove_repeated_points_in_place(LWGEOM *geom, double tolerance)
+{
+       switch (geom->type)
+       {
+               /* No-op! Cannot remote points */
+               case POINTTYPE:
+                       return;
+               case LINETYPE:
+               {
+                       LWLINE *g = (LWLINE*)(geom);
+                       POINTARRAY *pa = g->points;
+                       ptarray_remove_repeated_points_in_place(pa, tolerance, 2);
+                       /* Invalid output */
+                       if (pa->npoints == 1 && pa->maxpoints > 1)
+                       {
+                               /* Use first point as last point */
+                               pa->npoints = 2;
+                               ptarray_copy_point(pa, 0, 1);
+                       }
+                       break;
+               }
+               case POLYGONTYPE:
+               {
+                       int i, j = 0;
+                       LWPOLY *g = (LWPOLY*)(geom);
+                       for (i = 0; i < g->nrings; i++)
+                       {
+                               POINTARRAY *pa = g->rings[i];
+                               int minpoints = 4;
+                               /* Skip zero'ed out rings */
+                               if(!pa)
+                                       continue;
+                               ptarray_remove_repeated_points_in_place(pa, tolerance, minpoints);
+                               /* Drop collapsed rings */
+                               if(pa->npoints < 4)
+                               {
+                                       ptarray_free(pa);
+                                       continue;
+                               }
+                               g->rings[j++] = pa;
+                       }
+                       /* Update ring count */
+                       g->nrings = j;
+                       break;
+               }
+               case MULTIPOINTTYPE:
+               {
+                       static int out_stack_size = 32;
+                       double tolsq = tolerance*tolerance;
+                       int i, j, n = 0;
+                       LWMPOINT *mpt = (LWMPOINT *)(geom);
+                       LWPOINT **out;
+                       LWPOINT *out_stack[out_stack_size];
+                       int use_heap = (mpt->ngeoms > out_stack_size);
+
+                       /* No-op on empty */
+                       if (mpt->ngeoms == 0) return;
+
+                       /* We cannot write directly back to the multipoint */
+                       /* geoms array because we're reading out of it still */
+                       /* so we use a side array */
+                       if (use_heap)
+                               out = lwalloc(sizeof(LWMPOINT *) * mpt->ngeoms);
+                       else
+                               out = out_stack;
+
+                       /* Inefficient O(n^2) implementation */
+                       for (i = 0; i < mpt->ngeoms; i++)
+                       {
+                               int seen = 0;
+                               LWPOINT *p1 = mpt->geoms[i];
+                               const POINT2D *pt1 = getPoint2d_cp(p1->point, 0);
+                               for (j = 0; j < n; j++)
+                               {
+                                       LWPOINT *p2 = out[j];
+                                       const POINT2D *pt2 = getPoint2d_cp(p2->point, 0);
+                                       if (distance2d_sqr_pt_pt(pt1, pt2) <= tolsq)
+                                       {
+                                               seen = 1;
+                                               break;
+                                       }
+                               }
+                               if (seen) continue;
+                               out[n++] = p1;
+                       }
+
+                       /* Copy remaining points back into the input */
+                       /* array */
+                       memcpy(mpt->geoms, out, sizeof(LWPOINT *) * n);
+                       mpt->ngeoms = n;
+                       if (use_heap) lwfree(out);
+                       return;
+               }
+
+               case CIRCSTRINGTYPE:
+                       /* Dunno how to handle these, will return untouched */
+                       return;
+
+               /* Can process most multi* types as generic collection */
+               case MULTILINETYPE:
+               case MULTIPOLYGONTYPE:
+               case COLLECTIONTYPE:
+               /* Curve types we mostly ignore, but allow the linear */
+               /* portions to be processed by recursing into them */
+               case MULTICURVETYPE:
+               case CURVEPOLYTYPE:
+               case MULTISURFACETYPE:
+               case COMPOUNDTYPE:
+               {
+                       int i, j = 0;
+                       LWCOLLECTION *col = (LWCOLLECTION*)(geom);
+                       for (i = 0; i < col->ngeoms; i++)
+                       {
+                               LWGEOM *g = col->geoms[i];
+                               if (!g) continue;
+                               lwgeom_remove_repeated_points_in_place(g, tolerance);
+                               /* Drop zero'ed out geometries */
+                               if(lwgeom_is_empty(g))
+                               {
+                                       lwgeom_free(g);
+                                       continue;
+                               }
+                               col->geoms[j++] = g;
+                       }
+                       /* Update geometry count */
+                       col->ngeoms = j;
+                       break;
+               }
+               default:
+               {
+                       lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type));
+                       break;
+               }
+       }
+       return;
+}
+
+
+/**************************************************************/
+
 void
 lwgeom_simplify_in_place(LWGEOM *geom, double epsilon, int preserve_collapsed)
 {
@@ -1722,9 +1820,6 @@ lwgeom_simplify_in_place(LWGEOM *geom, double epsilon, int preserve_collapsed)
        return;
 }
 
-
-/**************************************************************/
-
 LWGEOM* lwgeom_simplify(const LWGEOM *igeom, double dist, int preserve_collapsed)
 {
        LWGEOM *lwgeom_out = lwgeom_clone_deep(igeom);
@@ -1733,6 +1828,8 @@ LWGEOM* lwgeom_simplify(const LWGEOM *igeom, double dist, int preserve_collapsed
        return lwgeom_out;
 }
 
+/**************************************************************/
+
 
 double lwgeom_area(const LWGEOM *geom)
 {
index 33e0a8b27e4241432a2ba95adfde5d59cd1b1fd8..73f06e62f1f73c3a8c0ab3fe049b5872d65181d2 100644 (file)
@@ -449,13 +449,7 @@ lwline_measured_from_lwline(const LWLINE *lwline, double m_start, double m_end)
 LWGEOM*
 lwline_remove_repeated_points(const LWLINE *lwline, double tolerance)
 {
-       POINTARRAY* npts = ptarray_remove_repeated_points_minpoints(lwline->points, tolerance, 2);
-
-       LWDEBUGF(3, "%s: npts %p", __func__, npts);
-
-       return (LWGEOM*)lwline_construct(lwline->srid,
-                                        lwline->bbox ? gbox_copy(lwline->bbox) : 0,
-                                        npts);
+       return lwgeom_remove_repeated_points((LWGEOM*)lwline, tolerance);
 }
 
 int
index dd86f504f381a8135827eb7f5386bfc2367714d4..b711f9d95cf57d9de4d2e1c00df568a812e9b9fc 100644 (file)
@@ -88,40 +88,6 @@ void lwmpoint_free(LWMPOINT *mpt)
        lwfree(mpt);
 }
 
-LWGEOM*
-lwmpoint_remove_repeated_points(const LWMPOINT *mpoint, double tolerance)
-{
-       uint32_t nnewgeoms;
-       uint32_t i, j;
-       LWGEOM **newgeoms;
-       LWGEOM *lwpt1, *lwpt2;
-
-       newgeoms = lwalloc(sizeof(LWGEOM *)*mpoint->ngeoms);
-       nnewgeoms = 0;
-       for (i=0; i<mpoint->ngeoms; ++i)
-       {
-               lwpt1 = (LWGEOM*)mpoint->geoms[i];
-               /* Brute force, may be optimized by building an index */
-               int seen=0;
-               for (j=0; j<nnewgeoms; ++j)
-               {
-                       lwpt2 = (LWGEOM*)newgeoms[j];
-                       if ( lwgeom_mindistance2d(lwpt1, lwpt2) <= tolerance )
-                       {
-                               seen=1;
-                               break;
-                       }
-               }
-               if ( seen ) continue;
-               newgeoms[nnewgeoms++] = lwgeom_clone_deep(lwpt1);
-       }
-
-       return (LWGEOM*)lwcollection_construct(mpoint->type,
-                                              mpoint->srid,
-                                                                                  mpoint->bbox ? gbox_copy(mpoint->bbox) : NULL,
-                                              nnewgeoms, newgeoms);
-
-}
 
 LWMPOINT*
 lwmpoint_from_lwgeom(const LWGEOM *g)
index 96c64010d6a5b66b4802ca66ff3fea2d5b135a5b..f6593976d5a4f258be54971fa891d508e0833985 100644 (file)
@@ -387,25 +387,6 @@ lwpoly_from_lwlines(const LWLINE *shell,
        return ret;
 }
 
-LWGEOM*
-lwpoly_remove_repeated_points(const LWPOLY *poly, double tolerance)
-{
-       uint32_t i;
-       POINTARRAY **newrings;
-
-       newrings = lwalloc(sizeof(POINTARRAY *)*poly->nrings);
-       for (i=0; i<poly->nrings; i++)
-       {
-               newrings[i] = ptarray_remove_repeated_points_minpoints(poly->rings[i], tolerance, 4);
-       }
-
-       return (LWGEOM*)lwpoly_construct(poly->srid,
-                                        poly->bbox ? gbox_copy(poly->bbox) : NULL,
-                                        poly->nrings, newrings);
-
-}
-
-
 LWPOLY*
 lwpoly_force_dims(const LWPOLY *poly, int hasz, int hasm)
 {
index 1e8977194d0a9631103c6a347b6df652320a1726..c7ffa8ae9069ed4da6da7f6583f5d8e1eed4fab1 100644 (file)
@@ -1429,93 +1429,86 @@ ptarray_longitude_shift(POINTARRAY *pa)
  *
  * Always returns a newly allocated object.
  */
-POINTARRAY *
+static POINTARRAY *
 ptarray_remove_repeated_points_minpoints(const POINTARRAY *in, double tolerance, int minpoints)
 {
-       POINTARRAY *out;
-       size_t ptsize = ptarray_point_size(in);
-       int has_z = FLAGS_GET_Z(in->flags);
-       int has_m = FLAGS_GET_M(in->flags);
-       int ipn = 1, opn = 1;
-       const POINT2D *prev_point, *this_point;
-       uint8_t *p1, *p2;
-       double tolsq = tolerance * tolerance;
-
-       LWDEBUGF(3, "%s called", __func__);
+       POINTARRAY *out = ptarray_clone_deep(in);
+       ptarray_remove_repeated_points_in_place(out, tolerance, minpoints);
+       return out;
+}
 
-       /* Single or zero point arrays can't have duplicates */
-       if ( in->npoints < 3 ) return ptarray_clone_deep(in);
+POINTARRAY *
+ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance)
+{
+       return ptarray_remove_repeated_points_minpoints(in, tolerance, 2);
+}
 
-       /* Condition minpoints */
-       if ( minpoints < 1 ) minpoints = 1;
 
-       /* Allocate enough output space for all points */
-       out = ptarray_construct(has_z, has_m, in->npoints);
+void
+ptarray_remove_repeated_points_in_place(POINTARRAY *pa, double tolerance, int min_points)
+{
+       int i;
+       double tolsq = tolerance * tolerance;
+       const POINT2D *last = NULL;
+       const POINT2D *pt;
+       int n_points = pa->npoints;
+       int n_points_out = 0;
+       int pt_size = ptarray_point_size(pa);
+       double dsq;
 
-       /* Keep the first point */
-       p1 = getPoint_internal(out, 0);
-       p2 = getPoint_internal(in, 0);
-       memcpy(p1, p2, ptsize);
+       /* No-op on short inputs */
+       if ( n_points <= 2 ) return;
 
-       /* Now fill up the actual points */
-       prev_point = getPoint2d_cp(in, 0);
-       LWDEBUGF(3, " first point copied, out points: %d", opn);
-       for ( ipn = 1; ipn < in->npoints; ipn++ )
+       for (i = 0; i < n_points; i++)
        {
-               this_point = getPoint2d_cp(in, ipn);
+               int last_point = (i == n_points-1);
 
-               /*
-               * If number of points left > number of points we need
-               * then it's still OK to drop dupes
-               */
-               if ( in->npoints - ipn > minpoints - opn )
+               /* Look straight into the abyss */
+               pt = getPoint2d_cp(pa, i);
+
+               /* Preserve first point always */
+               if (last)
                {
-                       if (tolerance > 0.0)
+                       /* Don't drop points if we are running short of points */
+               if (n_points - i > min_points - n_points_out)
                        {
-                               /* within the removal tolerance? */
-                               double dsq = distance2d_sqr_pt_pt(prev_point, this_point);
-                               if (dsq <= tolsq)
-                                       /* then skip it */
-                                       continue;
-                       }
-                       else
-                       {
-                               /* exact duplicate? */
-                               p1 = getPoint_internal(in, ipn-1);
-                               p2 = getPoint_internal(in, ipn);
-                               if (memcmp(p1, p2, ptsize) == 0)
-                                       /* then skip it */
-                                       continue;
+                               if (tolerance > 0.0)
+                               {
+                                       /* Only drop points that are within our tolerance */
+                                       dsq = distance2d_sqr_pt_pt(last, pt);
+                                       /* Allow any point but the last one to be dropped */
+                                       if (!last_point && dsq <= tolsq)
+                                       {
+                                               continue;
+                                       }
+                               }
+                               else
+                               {
+                                       /* At tolerance zero, only skip exact dupes */
+                                       if (memcmp((char*)pt, (char*)last, pt_size) == 0)
+                                               continue;
+                               }
                        }
                }
-               /*
-               * The point is different (see above) from
-               * the previous point, so add it to output
-               */
-               p1 = getPoint_internal(out, opn++);
-               p2 = getPoint_internal(in, ipn);
-               memcpy(p1, p2, ptsize);
-
-               prev_point = this_point;
-       }
 
-       /* Keep the last point */
-       p1 = getPoint_internal(out, opn-1); /* Last output point */
-       p2 = getPoint_internal(in, ipn-1); /* Last input point */
-       if ( memcmp(p1, p2, ptsize) != 0 )
-               memcpy(p1, p2, ptsize);
-
-       LWDEBUGF(3, " in:%d out:%d", out->npoints, opn);
-       out->npoints = opn;
+               /* Got to last point, and it's not very different from */
+               /* the point that preceded it. We want to keep the last */
+               /* point, not the second-to-last one, so we pull our write */
+               /* index back one value */
+               if (last_point && n_points_out > 1 && tolerance > 0.0 && dsq <= tolsq)
+               {
+                       n_points_out--;
+               }
 
-       return out;
+               /* Compact all remaining values to front of array */
+               ptarray_copy_point(pa, i, n_points_out++);
+               last = pt;
+       }
+       /* Adjust array length */
+       pa->npoints = n_points_out;
+       return;
 }
 
-POINTARRAY *
-ptarray_remove_repeated_points(const POINTARRAY *in, double tolerance)
-{
-       return ptarray_remove_repeated_points_minpoints(in, tolerance, 2);
-}
 
 /************************************************************************/