From: Sandro Santilli Date: Thu, 22 Mar 2012 15:07:03 +0000 (+0000) Subject: Add a ptarray_simplify parameter to preserve min vertices (#1698) X-Git-Tag: 2.0.0rc1~43 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5dfa7e3cc4ea9f2c099cc992e0be059f1aa9098a;p=postgis Add a ptarray_simplify parameter to preserve min vertices (#1698) A polygon ring collapsed to a segment is still better handled by mapnik than the same ring collapsed to a single point. This commit retains at least 3 vertices for polygons. git-svn-id: http://svn.osgeo.org/postgis/trunk@9528 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/liblwgeom/liblwgeom_internal.h b/liblwgeom/liblwgeom_internal.h index a7a5579f3..37c6e1e62 100644 --- a/liblwgeom/liblwgeom_internal.h +++ b/liblwgeom/liblwgeom_internal.h @@ -3,7 +3,7 @@ * PostGIS - Spatial Types for PostgreSQL * http://postgis.refractions.net * - * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2011-2012 Sandro Santilli * Copyright (C) 2011 Paul Ramsey * Copyright (C) 2007-2008 Mark Cave-Ayland * Copyright (C) 2001-2006 Refractions Research Inc. @@ -171,7 +171,11 @@ extern int32_t lw_get_int32_t(const uint8_t *loc); /* * DP simplification */ -POINTARRAY* ptarray_simplify(POINTARRAY *inpts, double epsilon); + +/** + * @param minpts minimun number of points to retain, if possible. + */ +POINTARRAY* ptarray_simplify(POINTARRAY *inpts, double epsilon, unsigned int minpts); LWLINE* lwline_simplify(const LWLINE *iline, double dist); LWPOLY* lwpoly_simplify(const LWPOLY *ipoly, double dist); LWCOLLECTION* lwcollection_simplify(const LWCOLLECTION *igeom, double dist); diff --git a/liblwgeom/lwline.c b/liblwgeom/lwline.c index a920f9eab..46061f3c7 100644 --- a/liblwgeom/lwline.c +++ b/liblwgeom/lwline.c @@ -3,6 +3,7 @@ * PostGIS - Spatial Types for PostgreSQL * http://postgis.refractions.net * + * Copyright (C) 2012 Sandro Santilli * Copyright (C) 2001-2006 Refractions Research Inc. * * This is free software; you can redistribute and/or modify it under @@ -479,7 +480,7 @@ LWLINE* lwline_simplify(const LWLINE *iline, double dist) if( lwline_is_empty(iline) ) return lwline_clone(iline); - oline = lwline_construct(iline->srid, NULL, ptarray_simplify(iline->points, dist)); + oline = lwline_construct(iline->srid, NULL, ptarray_simplify(iline->points, dist, 2)); oline->type = iline->type; return oline; } diff --git a/liblwgeom/lwpoly.c b/liblwgeom/lwpoly.c index e73b42293..aad9edb7f 100644 --- a/liblwgeom/lwpoly.c +++ b/liblwgeom/lwpoly.c @@ -3,6 +3,7 @@ * PostGIS - Spatial Types for PostgreSQL * http://postgis.refractions.net * + * Copyright (C) 2012 Sandro Santilli * Copyright (C) 2001-2006 Refractions Research Inc. * * This is free software; you can redistribute and/or modify it under @@ -353,23 +354,14 @@ LWPOLY* lwpoly_simplify(const LWPOLY *ipoly, double dist) for (i = 0; i < ipoly->nrings; i++) { - POINTARRAY *opts = ptarray_simplify(ipoly->rings[i], dist); - - /* One point implies an error in the ptarray_simplify */ - if ( i && ( opts->npoints < 2 ) ) - { - LWDEBUG(2, "ptarray_simplify returned a <2 pts array"); - ptarray_free(opts); - continue; - } + POINTARRAY *opts = ptarray_simplify(ipoly->rings[i], dist, 3); /* Less points than are needed to form a closed ring, we can't use this */ - if ( i && ( opts->npoints < 4 ) ) + if ( i && opts->npoints < 4 ) { - LWDEBUGF(3, "ring%d skipped (<4 pts)", i); + LWDEBUGF(3, "ring%d skipped (% pts)", i, opts->npoints); ptarray_free(opts); - if ( i ) continue; - else break; + continue; } LWDEBUGF(3, "ring%d simplified from %d to %d points", i, ipoly->rings[i]->npoints, opts->npoints); @@ -377,6 +369,13 @@ LWPOLY* lwpoly_simplify(const LWPOLY *ipoly, double dist) /* Add ring to simplified polygon */ if( lwpoly_add_ring(opoly, opts) == LW_FAILURE ) return NULL; + + /* Don't scan holes if shell is collapsed */ + if ( !i && opts->npoints < 4 ) + { + LWDEBUG(3, "nothing more to do for collapsed shell"); + break; + } } LWDEBUGF(3, "simplified polygon with %d rings", ipoly->nrings); diff --git a/liblwgeom/ptarray.c b/liblwgeom/ptarray.c index b623c02e2..08ebd7b0b 100644 --- a/liblwgeom/ptarray.c +++ b/liblwgeom/ptarray.c @@ -2,7 +2,9 @@ * * PostGIS - Spatial Types for PostgreSQL * http://postgis.refractions.net - * Copyright 2001-2006 Refractions Research Inc. + * + * Copyright (C) 2012 Sandro Santilli + * Copyright (C) 2001-2006 Refractions Research Inc. * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. @@ -1174,7 +1176,7 @@ ptarray_dp_findsplit(POINTARRAY *pts, int p1, int p2, int *split, double *dist) } POINTARRAY * -ptarray_simplify(POINTARRAY *inpts, double epsilon) +ptarray_simplify(POINTARRAY *inpts, double epsilon, unsigned int minpts) { int *stack; /* recursion stack */ int sp=-1; /* recursion stack pointer */ @@ -1189,7 +1191,8 @@ ptarray_simplify(POINTARRAY *inpts, double epsilon) p1 = 0; stack[++sp] = inpts->npoints-1; - LWDEBUGF(2, "Input has %d pts and %d dims", inpts->npoints, inpts->flags); + LWDEBUGF(2, "Input has %d pts and %d dims", inpts->npoints, + FLAGS_NDIMS(inpts->flags)); /* Allocate output POINTARRAY, and add first point. */ outpts = ptarray_construct_empty(FLAGS_GET_Z(inpts->flags), FLAGS_GET_M(inpts->flags), inpts->npoints); @@ -1205,8 +1208,9 @@ ptarray_simplify(POINTARRAY *inpts, double epsilon) LWDEBUGF(3, "Farthest point from P%d-P%d is P%d (dist. %g)", p1, stack[sp], split, dist); - if (dist > epsilon) + if (dist > epsilon || ( outpts->npoints+sp+1 < minpts && dist > 0 ) ) { + LWDEBUGF(4, "Added P%d to stack (outpts:%d, minpts:%d)", split, sp, minsplits); stack[++sp] = split; } else diff --git a/regress/simplify.sql b/regress/simplify.sql index 907aaec95..3c8c2476c 100644 --- a/regress/simplify.sql +++ b/regress/simplify.sql @@ -7,3 +7,5 @@ SELECT '5', ST_astext(ST_Simplify('MULTIPOINT(0 0 3 2, 0 10 6 1, 0 51 1 6, 50 20 SELECT '6', ST_astext(ST_Simplify('MULTILINESTRING((0 0 3 2, 0 10 6 1, 0 51 1 6, 50 20 6 7, 30 20 9 9, 7 32 10 5), (0 0 4 3, 1 1 2 3, 20 20 5 30))', 20)); SELECT '7', ST_astext(ST_Simplify('POLYGON((0 0 3 2, 0 10 6 1, 0 51 1 6, 50 20 6 7, 30 20 9 9, 7 32 10 5, 0 0 3 2), (1 1 4 3, 1 3 2 3, 18 18 5 30, 1 1 4 3))', 20)); SELECT '8', ST_astext(ST_Simplify('POLYGON((0 0 3 2, 0 10 6 1, 0 51 1 6, 50 20 6 7, 30 20 9 9, 7 32 10 5, 0 0 3 2), (1 1 4 3, 1 3 2 3, 18 18 5 30, 1 1 4 3))', 1)); + +SELECT '9', ST_astext(ST_Simplify('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 20));