[postgis-tickets] r16479 - Add ChaikinSmoothing #4050

nicklas.aven at jordogskog.no nicklas.aven at jordogskog.no
Tue Mar 20 06:11:17 PDT 2018


Author: nicklas
Date: 2018-03-20 06:11:17 -0700 (Tue, 20 Mar 2018)
New Revision: 16479

Added:
   trunk/liblwgeom/cunit/cu_chaikin.c
   trunk/liblwgeom/lwchaikins.c
   trunk/regress/chaikin.sql
   trunk/regress/chaikin_expected
Modified:
   trunk/NEWS
   trunk/doc/reference_processing.xml
   trunk/liblwgeom/Makefile.in
   trunk/liblwgeom/cunit/Makefile.in
   trunk/liblwgeom/cunit/cu_tester.c
   trunk/liblwgeom/liblwgeom.h.in
   trunk/postgis/lwgeom_functions_analytic.c
   trunk/postgis/postgis.sql.in
   trunk/regress/Makefile.in
Log:
Add ChaikinSmoothing #4050

Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/NEWS	2018-03-20 13:11:17 UTC (rev 16479)
@@ -2,6 +2,7 @@
 2018/xx/xx
 
 * New Features *
+  - #4050, ST_ChaikinSmoothing (Nicklas Avén)
   - #3989, ST_Buffer single sided option (Stephen Knox)
   - #3876, ST_Angle function (Rémi Cura)
   - #3564, ST_LineInterpolatePoints (Dan Baston)

Modified: trunk/doc/reference_processing.xml
===================================================================
--- trunk/doc/reference_processing.xml	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/doc/reference_processing.xml	2018-03-20 13:11:17 UTC (rev 16479)
@@ -3081,7 +3081,7 @@
 		  </refsection>
 	</refentry>
 
-<refentry id="ST_SimplifyVW">
+    <refentry id="ST_SimplifyVW">
 	  <refnamediv>
 		<refname>ST_SimplifyVW</refname>
 		<refpurpose>Returns a "simplified" version of the given geometry using the Visvalingam-Whyatt algorithm</refpurpose>
@@ -3129,8 +3129,64 @@
 			<para><xref linkend="ST_SetEffectiveArea" />, <xref linkend="ST_Simplify" />, <xref linkend="ST_SimplifyPreserveTopology" />, Topology <xref linkend="TP_ST_Simplify"/></para>
 		  </refsection>
 	</refentry>
-		<refentry id="ST_SetEffectiveArea">
+
+    <refentry id="ST_ChaikinSmoothing">
 	  <refnamediv>
+		<refname>ST_ChaikinSmoothing</refname>
+		<refpurpose>Returns a "smoothed" version of the given geometry using the Chaikin algorithm</refpurpose>
+	  </refnamediv>
+
+	  <refsynopsisdiv>
+		<funcsynopsis>
+		  <funcprototype>
+			<funcdef>geometry <function>ST_ChaikinSmoothing</function></funcdef>
+			<paramdef><type>geometry</type> <parameter>geomA</parameter></paramdef>
+			<paramdef><type>integer</type> <parameter>nIterations</parameter></paramdef>
+			<paramdef><type>boolean</type> <parameter>preserveEndPoints</parameter></paramdef>
+		  </funcprototype>
+		</funcsynopsis>
+	  </refsynopsisdiv>
+
+	  <refsection>
+		<title>Description</title>
+		<para> Returns a "smoothed" version of the given geometry using the Chaikin algorithm.
+        See <ulink url="http://www.idav.ucdavis.edu/education/CAGDNotes/Chaikins-Algorithm/Chaikins-Algorithm.html">Chaikins-Algorithm</ulink> for an explanation of the process.
+		For each iteration the number of vertex points will double. 
+        The function puts new vertex points at 1/4 of the line before and after each point and removes the original point.
+        To reduce the number of points use one of the simplification functions on the result. 
+        The new points gets interpolated values for all included dimensions, also z and m.</para>
+
+		<note><para>Note that returned geometry will get more points than the original.
+                To reduce the number of points again use one of the simplification functions on the result.
+                (see <xref linkend="ST_Simplify" /> and <xref linkend="ST_SimplifyVW" />)</para></note>
+		<note><para>Second argument, number of iterations is limited to max 5 iterations</para></note>
+		<note><para>Note third argument is only valid for polygons, and will be ignored for linestrings</para></note>
+		<note><para>This function handles 3D and the third dimension will affect the result.</para></note>
+		<para>Availability: 2.5.0</para>
+	  </refsection>
+
+		  <refsection>
+			<title>Examples</title>
+			<para>A triangle is smoothed</para>
+				<programlisting>
+
+select ST_AsText(ST_ChaikinSmoothing(geom)) smoothed
+FROM (SELECT  'LINESTRING(0 0, 8 8, 0 16)'::geometry geom) As foo;
+-result
+ smoothed
+-----------+-------------------+
+LINESTRING(0 0,6 6,6 10,0 16)
+
+				</programlisting>
+		  </refsection>
+		  <refsection>
+			<title>See Also</title>
+			<para><xref linkend="ST_Simplify" />, <xref linkend="ST_SimplifyVW" /></para>
+		  </refsection>
+	</refentry>
+    
+    <refentry id="ST_SetEffectiveArea">
+	  <refnamediv>
 		<refname>ST_SetEffectiveArea</refname>
 		<refpurpose>
 			Sets the effective area for each vertex, storing the value in the M ordinate.  A simplified geometry can then be generated by filtering on the M ordinate.

Modified: trunk/liblwgeom/Makefile.in
===================================================================
--- trunk/liblwgeom/Makefile.in	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/liblwgeom/Makefile.in	2018-03-20 13:11:17 UTC (rev 16479)
@@ -114,6 +114,7 @@
 	lwgeom_wrapx.o \
 	lwunionfind.o \
 	effectivearea.o \
+	lwchaikins.o \
 	lwkmeans.o \
 	varint.o
 

Modified: trunk/liblwgeom/cunit/Makefile.in
===================================================================
--- trunk/liblwgeom/cunit/Makefile.in	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/liblwgeom/cunit/Makefile.in	2018-03-20 13:11:17 UTC (rev 16479)
@@ -36,6 +36,7 @@
 	cu_tree.o \
 	cu_measures.o \
 	cu_effectivearea.o \
+	cu_chaikin.o \
 	cu_node.o \
 	cu_clip_by_rect.o \
 	cu_libgeom.o \

Added: trunk/liblwgeom/cunit/cu_chaikin.c
===================================================================
--- trunk/liblwgeom/cunit/cu_chaikin.c	                        (rev 0)
+++ trunk/liblwgeom/cunit/cu_chaikin.c	2018-03-20 13:11:17 UTC (rev 16479)
@@ -0,0 +1,66 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * Copyright 2012 Sandro Santilli <strk at kbt.io>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "CUnit/Basic.h"
+
+#include "liblwgeom_internal.h"
+#include "cu_tester.h"
+
+static void do_test_chaikin(char *geom_txt,char *expected, int n_iterations, int preserve_end_points)
+{
+	LWGEOM *geom_in, *geom_out;
+	char *out_txt;
+	geom_in = lwgeom_from_wkt(geom_txt, LW_PARSER_CHECK_NONE);
+	geom_out = lwgeom_chaikin(geom_in, n_iterations, preserve_end_points);
+	out_txt = lwgeom_to_wkt(geom_out, WKT_EXTENDED, 3, NULL);
+	if(strcmp(expected, out_txt))
+		printf("%s is not equal to %s\n", expected, out_txt);
+	CU_ASSERT_STRING_EQUAL(expected, out_txt)
+	lwfree(out_txt);
+	lwgeom_free(geom_in);
+	lwgeom_free(geom_out);
+	return;
+}
+
+static void do_test_chaikin_lines(void)
+{
+	/*Simpliest test*/
+	do_test_chaikin("LINESTRING(0 0,8 8,16 0)","LINESTRING(0 0,6 6,10 6,16 0)", 1, 1);
+	/*2 iterations*/
+	do_test_chaikin("LINESTRING(0 0,8 8,16 0)","LINESTRING(0 0,4.5 4.5,7 6,9 6,11.5 4.5,16 0)", 2, 1);
+	/*check so it really ignores preserve_end_points set to off for linestrings*/
+	do_test_chaikin("LINESTRING(0 0,8 8,16 0)","LINESTRING(0 0,4.5 4.5,7 6,9 6,11.5 4.5,16 0)", 2, 0);
+	return;
+}
+
+static void do_test_chaikin_polygons(void)
+{
+	/*Simpliest test*/
+	do_test_chaikin("POLYGON((0 0,8 8,16 0,0 0))","POLYGON((0 0,6 6,10 6,14 2,12 0,0 0))", 1, 1);
+	/*2 iterations*/
+	do_test_chaikin("POLYGON((0 0,8 8,16 0,0 0))","POLYGON((0 0,4.5 4.5,7 6,9 6,11 5,13 3,13.5 1.5,12.5 0.5,9 0,0 0))", 2, 1);
+	/*2 iterations without preserving end points*/
+	do_test_chaikin("POLYGON((0 0,8 8,16 0,0 0))","POLYGON((3 3,5 5,7 6,9 6,11 5,13 3,13.5 1.5,12.5 0.5,10 0,6 0,3.5 0.5,2.5 1.5,3 3))", 2, 0);
+	return;
+}
+
+
+void chaikin_suite_setup(void);
+void chaikin_suite_setup(void)
+{
+	CU_pSuite suite = CU_add_suite("chaikin",NULL,NULL);
+	PG_ADD_TEST(suite, do_test_chaikin_lines);
+	PG_ADD_TEST(suite, do_test_chaikin_polygons);
+}

Modified: trunk/liblwgeom/cunit/cu_tester.c
===================================================================
--- trunk/liblwgeom/cunit/cu_tester.c	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/liblwgeom/cunit/cu_tester.c	2018-03-20 13:11:17 UTC (rev 16479)
@@ -47,6 +47,7 @@
 extern void lwstroke_suite_setup(void);
 extern void measures_suite_setup(void);
 extern void effectivearea_suite_setup(void);
+extern void chaikin_suite_setup(void);
 extern void minimum_bounding_circle_suite_setup(void);
 extern void misc_suite_setup(void);
 extern void node_suite_setup(void);
@@ -97,6 +98,7 @@
 	lwstroke_suite_setup,
 	measures_suite_setup,
 	effectivearea_suite_setup,
+	chaikin_suite_setup,
 	minimum_bounding_circle_suite_setup,
 	misc_suite_setup,
 	node_suite_setup,

Modified: trunk/liblwgeom/liblwgeom.h.in
===================================================================
--- trunk/liblwgeom/liblwgeom.h.in	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/liblwgeom/liblwgeom.h.in	2018-03-20 13:11:17 UTC (rev 16479)
@@ -990,6 +990,7 @@
 extern LWGEOM* lwgeom_force_4d(const LWGEOM *geom);
 
 extern LWGEOM* lwgeom_set_effective_area(const LWGEOM *igeom, int set_area, double area);
+extern LWGEOM* lwgeom_chaikin(const LWGEOM *igeom, int n_iterations, int preserve_endpoint);
 
 /*
  * Force to use SFS 1.1 geometry type

Added: trunk/liblwgeom/lwchaikins.c
===================================================================
--- trunk/liblwgeom/lwchaikins.c	                        (rev 0)
+++ trunk/liblwgeom/lwchaikins.c	2018-03-20 13:11:17 UTC (rev 16479)
@@ -0,0 +1,202 @@
+
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * PostGIS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * PostGIS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ **********************************************************************
+ *
+ * Copyright 2018 Nicklas Avén
+ *
+ **********************************************************************/
+
+
+ #include "liblwgeom_internal.h"
+
+
+
+static POINTARRAY * ptarray_chaikin(POINTARRAY *inpts, int preserve_endpoint, int isclosed)
+{
+	uint32_t p, i, n_out_points=0, p1_set=0, p2_set=0;
+	POINT4D p1, p2;
+	POINTARRAY *opts;
+	double *dlist;
+	double deltaval, quarter_delta, val1, val2;
+	int ndims = 2 + ptarray_has_z(inpts) + ptarray_has_m(inpts);
+	int new_npoints = inpts->npoints * 2;
+	opts = ptarray_construct_empty(FLAGS_GET_Z(inpts->flags), FLAGS_GET_M(inpts->flags), new_npoints);
+
+	dlist = (double*)(opts->serialized_pointlist);
+
+	p1 = getPoint4d(inpts, 0);
+	/*if first point*/
+	if(preserve_endpoint)
+	{
+		ptarray_append_point(opts, &p1, LW_TRUE);
+		n_out_points++;
+	}
+
+	for (p=1;p<inpts->npoints;p++)
+	{
+		memcpy(&p2, &p1, sizeof(POINT4D));
+		p1 = getPoint4d(inpts, p);
+		if(p>0)
+		{
+			p1_set = p2_set = 0;
+			for (i=0;i<ndims;i++)
+			{
+				val1 = ((double*) &(p1))[i];
+				val2 = ((double*) &(p2))[i];
+				deltaval =  val1 - val2;
+				quarter_delta = deltaval * 0.25;
+				if(!preserve_endpoint || p > 1)
+				{
+					dlist[n_out_points * ndims + i] = val2 + quarter_delta;
+					p1_set = 1;
+				}
+				if(!preserve_endpoint || p < inpts->npoints - 1)
+				{
+					dlist[(n_out_points + p1_set) * ndims + i] = val1 - quarter_delta;
+					p2_set = 1;
+				}
+			}
+			n_out_points+=(p1_set + p2_set);
+		}
+	}
+
+	/*if last point*/
+	if(preserve_endpoint)
+	{
+		opts->npoints = n_out_points;
+		ptarray_append_point(opts, &p1, LW_TRUE);
+		n_out_points++;
+	}
+
+	if(isclosed && !preserve_endpoint)
+	{
+		opts->npoints = n_out_points;
+		POINT4D first_point = getPoint4d(opts, 0);
+		ptarray_append_point(opts, &first_point, LW_TRUE);
+		n_out_points++;
+	}
+	opts->npoints = n_out_points;
+
+	return opts;
+
+}
+
+static LWLINE* lwline_chaikin(const LWLINE *iline, int n_iterations)
+{
+	POINTARRAY *pa, *pa_new;
+	int j;
+
+	if( lwline_is_empty(iline))
+		return lwline_clone(iline);
+
+
+	LWLINE *oline = lwline_construct_empty(iline->srid, FLAGS_GET_Z(iline->flags), FLAGS_GET_M(iline->flags));
+	pa = iline->points;
+	for (j=0;j<n_iterations;j++)
+	{
+		pa_new = ptarray_chaikin(pa,1,LW_FALSE);
+		if(j>0)
+			ptarray_free(pa);
+		pa=pa_new;
+	}
+	oline = lwline_construct(iline->srid, NULL, pa);
+
+	oline->type = iline->type;
+	return oline;
+
+}
+
+
+static LWPOLY* lwpoly_chaikin(const LWPOLY *ipoly, int n_iterations, int preserve_endpoint)
+{
+	uint32_t i, j;
+	POINTARRAY *pa, *pa_new;
+	LWPOLY *opoly = lwpoly_construct_empty(ipoly->srid, FLAGS_GET_Z(ipoly->flags), FLAGS_GET_M(ipoly->flags));
+
+	if( lwpoly_is_empty(ipoly) )
+		return opoly;
+	for (i = 0; i < ipoly->nrings; i++)
+	{
+		pa = ipoly->rings[i];
+		for(j=0;j<n_iterations;j++)
+		{
+			pa_new = ptarray_chaikin(pa,preserve_endpoint,LW_TRUE);
+			if(j>0)
+				ptarray_free(pa);
+			pa=pa_new;
+		}
+		if(pa->npoints>=4)
+		{
+			if( lwpoly_add_ring(opoly,pa ) == LW_FAILURE )
+				return NULL;
+		}
+	}
+
+	opoly->type = ipoly->type;
+
+	if( lwpoly_is_empty(opoly) )
+		return NULL;
+
+	return opoly;
+
+}
+
+
+static LWCOLLECTION* lwcollection_chaikin(const LWCOLLECTION *igeom, int n_iterations, int preserve_endpoint)
+{
+	LWDEBUG(2, "Entered  lwcollection_set_effective_area");
+	uint32_t i;
+
+	LWCOLLECTION *out = lwcollection_construct_empty(igeom->type, igeom->srid, FLAGS_GET_Z(igeom->flags), FLAGS_GET_M(igeom->flags));
+
+	if( lwcollection_is_empty(igeom) )
+		return out; /* should we return NULL instead ? */
+
+	for( i = 0; i < igeom->ngeoms; i++ )
+	{
+		LWGEOM *ngeom = lwgeom_chaikin(igeom->geoms[i],n_iterations,preserve_endpoint);
+		if ( ngeom ) out = lwcollection_add_lwgeom(out, ngeom);
+	}
+
+	return out;
+}
+
+
+LWGEOM* lwgeom_chaikin(const LWGEOM *igeom, int n_iterations, int preserve_endpoint)
+{
+	LWDEBUG(2, "Entered  lwgeom_set_effective_area");
+	switch (igeom->type)
+	{
+	case POINTTYPE:
+	case MULTIPOINTTYPE:
+		return lwgeom_clone(igeom);
+	case LINETYPE:
+		return (LWGEOM*)lwline_chaikin((LWLINE*)igeom, n_iterations);
+	case POLYGONTYPE:
+		return (LWGEOM*)lwpoly_chaikin((LWPOLY*)igeom,n_iterations,preserve_endpoint);
+	case MULTILINETYPE:
+	case MULTIPOLYGONTYPE:
+	case COLLECTIONTYPE:
+		return (LWGEOM*)lwcollection_chaikin((LWCOLLECTION *)igeom,n_iterations,preserve_endpoint);
+	default:
+		lwerror("lwgeom_chaikin: unsupported geometry type: %s",lwtype_name(igeom->type));
+	}
+	return NULL;
+}

Modified: trunk/postgis/lwgeom_functions_analytic.c
===================================================================
--- trunk/postgis/lwgeom_functions_analytic.c	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/postgis/lwgeom_functions_analytic.c	2018-03-20 13:11:17 UTC (rev 16479)
@@ -127,7 +127,51 @@
 	PG_RETURN_POINTER(result);
 }
 
+PG_FUNCTION_INFO_V1(LWGEOM_ChaikinSmoothing);
+Datum LWGEOM_ChaikinSmoothing(PG_FUNCTION_ARGS)
+{
+	GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
+	GSERIALIZED *result;
+	int type = gserialized_get_type(geom);
+	LWGEOM *in;
+	LWGEOM *out;
+	int preserve_endpoints=1;
+	int n_iterations=1;
 
+	if ( type == POINTTYPE || type == MULTIPOINTTYPE )
+		PG_RETURN_POINTER(geom);
+
+	if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
+		n_iterations = PG_GETARG_INT32(1);
+
+	if (n_iterations>5)
+		elog(ERROR,"Not more than 5 iterations please");
+	if (n_iterations< 1)
+		elog(ERROR,"Number of iterations must be between 1 and 5");
+
+	if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) )
+	{
+		if(PG_GETARG_BOOL(2))
+			preserve_endpoints = 1;
+		else
+			preserve_endpoints = 0;
+	}
+
+	in = lwgeom_from_gserialized(geom);
+
+	out = lwgeom_chaikin(in, n_iterations, preserve_endpoints);
+	if ( ! out ) PG_RETURN_NULL();
+
+	/* COMPUTE_BBOX TAINTING */
+	if ( in->bbox ) lwgeom_add_bbox(out);
+
+	result = geometry_serialize(out);
+	lwgeom_free(out);
+	PG_FREE_IF_COPY(geom, 0);
+	PG_RETURN_POINTER(result);
+}
+
+
 /***********************************************************************
  * --strk at kbt.io;
  ***********************************************************************/

Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/postgis/postgis.sql.in	2018-03-20 13:11:17 UTC (rev 16479)
@@ -3144,7 +3144,14 @@
 	AS 'MODULE_PATHNAME', 'LWGEOM_SetEffectiveArea'
 	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
 	COST 1; -- reset cost, see #3675
-
+	
+-- Availability: 2.5.0
+CREATE OR REPLACE FUNCTION ST_ChaikinSmoothing(geometry, integer default 1, boolean default true)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME', 'LWGEOM_ChaikinSmoothing'
+	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
+	COST 1; -- reset cost, see #3675
+	
 -- ST_SnapToGrid(input, xoff, yoff, xsize, ysize)
 -- Availability: 1.2.2
 CREATE OR REPLACE FUNCTION ST_SnapToGrid(geometry, float8, float8, float8, float8)

Modified: trunk/regress/Makefile.in
===================================================================
--- trunk/regress/Makefile.in	2018-03-20 09:37:03 UTC (rev 16478)
+++ trunk/regress/Makefile.in	2018-03-20 13:11:17 UTC (rev 16479)
@@ -80,6 +80,7 @@
 	bestsrid \
 	binary \
 	boundary \
+	chaikin \
 	cluster \
 	concave_hull\
 	ctors \

Added: trunk/regress/chaikin.sql
===================================================================
--- trunk/regress/chaikin.sql	                        (rev 0)
+++ trunk/regress/chaikin.sql	2018-03-20 13:11:17 UTC (rev 16479)
@@ -0,0 +1,6 @@
+SELECT '1', ST_astext(ST_ChaikinSmoothing('LINESTRING(0 0, 8 8, 0 16)'));
+SELECT '2', ST_astext(ST_ChaikinSmoothing('LINESTRING(0 0, 8 8, 0 16)',10));
+SELECT '3', ST_astext(ST_ChaikinSmoothing('LINESTRING(0 0, 8 8, 0 16)',0));
+SELECT '4', ST_astext(ST_ChaikinSmoothing('LINESTRING(0 0, 8 8, 0 16)',2));
+SELECT '5', ST_astext(ST_ChaikinSmoothing('POINT(0 0)'));
+SELECT '6', ST_astext(ST_ChaikinSmoothing('GEOMETRYCOLLECTION(POINT(1 1), LINESTRING(1 1, 1 3, 1 5), POLYGON((5 5, 5 10, 10 10, 10 5, 5 5), (6 6, 6 7, 7 7, 7 6, 6 6 )))', 2, 't'));

Added: trunk/regress/chaikin_expected
===================================================================
--- trunk/regress/chaikin_expected	                        (rev 0)
+++ trunk/regress/chaikin_expected	2018-03-20 13:11:17 UTC (rev 16479)
@@ -0,0 +1,6 @@
+1|LINESTRING(0 0,6 6,6 10,0 16)
+ERROR:  Not more than 5 iterations please
+ERROR:  Number of iterations must be between 1 and 5
+4|LINESTRING(0 0,4.5 4.5,6 7,6 9,4.5 11.5,0 16)
+5|POINT(0 0)
+6|GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(1 1,1 2.125,1 2.75,1 3.25,1 3.875,1 5),POLYGON((5 5,5 7.8125,5.3125 9.0625,5.9375 9.6875,6.875 10,8.125 10,9.0625 9.6875,9.6875 9.0625,10 8.125,10 6.875,9.6875 5.9375,9.0625 5.3125,7.8125 5,5 5),(6 6,6 6.5625,6.0625 6.8125,6.1875 6.9375,6.375 7,6.625 7,6.8125 6.9375,6.9375 6.8125,7 6.625,7 6.375,6.9375 6.1875,6.8125 6.0625,6.5625 6,6 6)))



More information about the postgis-tickets mailing list