[postgis-tickets] r14961 - Implement lwgeom_wrapx and ST_WrapX

Sandro Santilli strk at kbt.io
Thu Jun 16 02:09:26 PDT 2016


Author: strk
Date: 2016-06-16 02:09:26 -0700 (Thu, 16 Jun 2016)
New Revision: 14961

Added:
   trunk/liblwgeom/cunit/cu_wrapx.c
   trunk/liblwgeom/lwgeom_wrapx.c
   trunk/regress/wrapx.sql
   trunk/regress/wrapx_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_basic.c
   trunk/postgis/postgis.sql.in
Log:
Implement lwgeom_wrapx and ST_WrapX

Includes tests (both cunit and regress) and documentation.

Closes #454

Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2016-06-16 08:46:07 UTC (rev 14960)
+++ trunk/NEWS	2016-06-16 09:09:26 UTC (rev 14961)
@@ -17,6 +17,7 @@
   - Add parameters for geography ST_Buffer (Thomas Bonfort)
   - TopoGeom_addElement, TopoGeom_remElement (Sandro Santilli)
   - populate_topology_layer (Sandro Santilli)
+  - #454,  ST_WrapX and lwgeom_wrapx (Sandro Santilli)
   - #1758, ST_Normalize (Sandro Santilli)
   - #2259, ST_Voronoi (Dan Baston)
   - #2991, Enable ST_Transform to use PROJ.4 text (Mike Toews)

Modified: trunk/doc/reference_processing.xml
===================================================================
--- trunk/doc/reference_processing.xml	2016-06-16 08:46:07 UTC (rev 14960)
+++ trunk/doc/reference_processing.xml	2016-06-16 09:09:26 UTC (rev 14961)
@@ -2703,10 +2703,76 @@
 	  <!-- Optionally add a "See Also" section -->
 	  <refsection>
 		<title>See Also</title>
-		<para><xref linkend="ST_GeomFromEWKT" />, <xref linkend="ST_GeomFromText" />, <xref linkend="ST_AsEWKT" /></para>
+		<para>
+      <xref linkend="ST_WrapX" />
+    </para>
 	  </refsection>
 	</refentry>
 
+	<refentry id="ST_WrapX">
+	  <refnamediv>
+		<refname>ST_WrapX</refname>
+
+		<refpurpose>Wrap a geometry around an X value.</refpurpose>
+	  </refnamediv>
+
+	  <refsynopsisdiv>
+		<funcsynopsis>
+		  <funcprototype>
+			<funcdef>geometry <function>ST_WrapX</function></funcdef>
+			<paramdef><type>geometry </type> <parameter>geom</parameter></paramdef>
+			<paramdef><type>float8 </type> <parameter>wrap</parameter></paramdef>
+			<paramdef><type>float8 </type> <parameter>move</parameter></paramdef>
+		  </funcprototype>
+		</funcsynopsis>
+	  </refsynopsisdiv>
+
+	  <refsection>
+		<title>Description</title>
+
+    <para>
+This function splits the input geometries and then moves every resulting
+component falling on the right (for negative 'move') or on the left (for
+positive 'move') of given 'wrap' line in the direction specified by the
+'move' parameter, finally re-unioning the pieces togheter.
+    </para>
+
+		<note><para>
+This is useful to "recenter" long-lat input to have features
+of interest not spawned from one side to the other.
+    </para></note>
+
+		<para>Availability: 2.3.0</para>
+
+		<para>&Z_support;</para>
+<!-- TODO: check these
+		<para>&P_support;</para>
+		<para>&T_support;</para>
+-->
+	  </refsection>
+
+
+	  <refsection>
+		<title>Examples</title>
+
+		<programlisting>
+-- Move all components of the given geometries whose bounding box
+-- falls completely on the left of x=0 to +360
+select ST_WrapX(the_geom, 0, 360);
+
+-- Move all components of the given geometries whose bounding box
+-- falls completely on the left of x=-30 to +360
+select ST_WrapX(the_geom, -30, 360);
+		</programlisting>
+	  </refsection>
+
+	  <!-- Optionally add a "See Also" section -->
+	  <refsection>
+		<title>See Also</title>
+		<para><xref linkend="ST_Shift_Longitude" /></para>
+	  </refsection>
+	</refentry>
+
 	<refentry id="ST_Simplify">
 	  <refnamediv>
 		<refname>ST_Simplify</refname>

Modified: trunk/liblwgeom/Makefile.in
===================================================================
--- trunk/liblwgeom/Makefile.in	2016-06-16 08:46:07 UTC (rev 14960)
+++ trunk/liblwgeom/Makefile.in	2016-06-16 09:09:26 UTC (rev 14961)
@@ -112,6 +112,7 @@
 	lwgeom_geos_split.o \
 	lwgeom_topo.o \
 	lwgeom_transform.o \
+	lwgeom_wrapx.o \
 	lwunionfind.o \
 	effectivearea.o \
 	lwkmeans.o \

Modified: trunk/liblwgeom/cunit/Makefile.in
===================================================================
--- trunk/liblwgeom/cunit/Makefile.in	2016-06-16 08:46:07 UTC (rev 14960)
+++ trunk/liblwgeom/cunit/Makefile.in	2016-06-16 09:09:26 UTC (rev 14961)
@@ -62,6 +62,7 @@
 	cu_iterator.o \
 	cu_varint.o \
 	cu_unionfind.o \
+	cu_wrapx.o \
 	cu_tester.o
 
 ifeq (@SFCGAL@,sfcgal)

Modified: trunk/liblwgeom/cunit/cu_tester.c
===================================================================
--- trunk/liblwgeom/cunit/cu_tester.c	2016-06-16 08:46:07 UTC (rev 14960)
+++ trunk/liblwgeom/cunit/cu_tester.c	2016-06-16 09:09:26 UTC (rev 14961)
@@ -68,6 +68,7 @@
 extern void surface_suite_setup(void);
 extern void wkb_in_suite_setup(void);
 extern void wkt_in_suite_setup(void);
+extern void wrapx_suite_setup(void);
 
 
 /* AND ADD YOUR SUITE SETUP FUNCTION HERE (2 of 2) */
@@ -117,6 +118,7 @@
 	wkb_out_suite_setup,
 	wkt_in_suite_setup,
 	wkt_out_suite_setup,
+	wrapx_suite_setup,
 	NULL
 };
 
@@ -274,7 +276,7 @@
   char buf[MAX_CUNIT_MSG_LENGTH+1];
   vsnprintf (buf, MAX_CUNIT_MSG_LENGTH, fmt, ap);
   buf[MAX_CUNIT_MSG_LENGTH]='\0';
-  /*fprintf(stderr, "NOTICE: %s\n", buf);*/
+  fprintf(stderr, "NOTICE: %s\n", buf);
 }
 
 static void

Added: trunk/liblwgeom/cunit/cu_wrapx.c
===================================================================
--- trunk/liblwgeom/cunit/cu_wrapx.c	                        (rev 0)
+++ trunk/liblwgeom/cunit/cu_wrapx.c	2016-06-16 09:09:26 UTC (rev 14961)
@@ -0,0 +1,134 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * Copyright (C) 2016 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 "CUnit/Basic.h"
+#include "cu_tester.h"
+
+#include "liblwgeom.h"
+#include "liblwgeom_internal.h"
+
+static void test_lwgeom_wrapx(void)
+{
+	LWGEOM *geom, *ret;
+	char *exp_wkt, *obt_wkt;
+
+	geom = lwgeom_from_wkt(
+		"POLYGON EMPTY",
+		LW_PARSER_CHECK_NONE);
+	CU_ASSERT(geom != NULL);
+	ret = lwgeom_wrapx(geom, 0, 20);
+	CU_ASSERT(ret != NULL);
+	obt_wkt = lwgeom_to_ewkt(ret);
+	exp_wkt = "POLYGON EMPTY";
+	ASSERT_STRING_EQUAL(obt_wkt, exp_wkt);
+	lwfree(obt_wkt);
+	lwgeom_free(ret);
+	lwgeom_free(geom);
+
+	geom = lwgeom_from_wkt(
+		"POINT(0 0)",
+		LW_PARSER_CHECK_NONE);
+	CU_ASSERT(geom != NULL);
+	ret = lwgeom_wrapx(geom, 2, 10);
+	CU_ASSERT(ret != NULL);
+	obt_wkt = lwgeom_to_ewkt(ret);
+	exp_wkt = "POINT(10 0)";
+	ASSERT_STRING_EQUAL(obt_wkt, exp_wkt);
+	lwfree(obt_wkt);
+	lwgeom_free(ret);
+	lwgeom_free(geom);
+
+	geom = lwgeom_from_wkt(
+		"POINT(0 0)",
+		LW_PARSER_CHECK_NONE);
+	CU_ASSERT(geom != NULL);
+	ret = lwgeom_wrapx(geom, 0, 20);
+	CU_ASSERT(ret != NULL);
+	obt_wkt = lwgeom_to_ewkt(ret);
+	exp_wkt = "POINT(0 0)";
+	ASSERT_STRING_EQUAL(obt_wkt, exp_wkt);
+	lwfree(obt_wkt);
+	lwgeom_free(ret);
+	lwgeom_free(geom);
+
+	geom = lwgeom_from_wkt(
+		"POINT(0 0)",
+		LW_PARSER_CHECK_NONE);
+	CU_ASSERT(geom != NULL);
+	ret = lwgeom_wrapx(geom, 0, -20);
+	CU_ASSERT(ret != NULL);
+	obt_wkt = lwgeom_to_ewkt(ret);
+	exp_wkt = "POINT(0 0)";
+	ASSERT_STRING_EQUAL(obt_wkt, exp_wkt);
+	lwfree(obt_wkt);
+	lwgeom_free(ret);
+	lwgeom_free(geom);
+
+	geom = lwgeom_from_wkt(
+		"LINESTRING(0 0,10 0)",
+		LW_PARSER_CHECK_NONE);
+	CU_ASSERT(geom != NULL);
+	ret = lwgeom_wrapx(geom, 8, -10);
+	CU_ASSERT(ret != NULL);
+	obt_wkt = lwgeom_to_ewkt(ret);
+	exp_wkt = "MULTILINESTRING((0 0,8 0),(-2 0,0 0))";
+	ASSERT_STRING_EQUAL(obt_wkt, exp_wkt);
+	lwfree(obt_wkt);
+	lwgeom_free(ret);
+	lwgeom_free(geom);
+
+	geom = lwgeom_from_wkt(
+		"MULTILINESTRING((-5 -2,0 0),(0 0,10 10))",
+		LW_PARSER_CHECK_NONE);
+	CU_ASSERT(geom != NULL);
+	ret = lwgeom_wrapx(geom, 0, 20);
+	CU_ASSERT(ret != NULL);
+	obt_wkt = lwgeom_to_ewkt(ret);
+	exp_wkt = "MULTILINESTRING((15 -2,20 0),(0 0,10 10))";
+	ASSERT_STRING_EQUAL(obt_wkt, exp_wkt);
+	lwfree(obt_wkt);
+	lwgeom_free(ret);
+	lwgeom_free(geom);
+
+	geom = lwgeom_from_wkt(
+		"GEOMETRYCOLLECTION("
+		" MULTILINESTRING((-5 -2,0 0),(0 0,10 10)),"
+		" POINT(-5 0),"
+		" POLYGON EMPTY"
+		")",
+		LW_PARSER_CHECK_NONE);
+	CU_ASSERT(geom != NULL);
+	ret = lwgeom_wrapx(geom, 0, 20);
+	CU_ASSERT(ret != NULL);
+	obt_wkt = lwgeom_to_ewkt(ret);
+	exp_wkt = "GEOMETRYCOLLECTION("
+						"MULTILINESTRING((15 -2,20 0),(0 0,10 10)),"
+						"POINT(15 0),"
+						"POLYGON EMPTY"
+						")";
+	ASSERT_STRING_EQUAL(obt_wkt, exp_wkt);
+	lwfree(obt_wkt);
+	lwgeom_free(ret);
+	lwgeom_free(geom);
+
+}
+
+
+/*
+** Used by test harness to register the tests in this file.
+*/
+void wrapx_suite_setup(void);
+void wrapx_suite_setup(void)
+{
+	CU_pSuite suite = CU_add_suite("wrapx", NULL, NULL);
+	PG_ADD_TEST(suite, test_lwgeom_wrapx);
+}

Modified: trunk/liblwgeom/liblwgeom.h.in
===================================================================
--- trunk/liblwgeom/liblwgeom.h.in	2016-06-16 08:46:07 UTC (rev 14960)
+++ trunk/liblwgeom/liblwgeom.h.in	2016-06-16 09:09:26 UTC (rev 14961)
@@ -1218,7 +1218,21 @@
 
 void lwgeom_longitude_shift(LWGEOM *lwgeom);
 
+/**
+ * @brief wrap geometry on given cut x value
+ *
+ * For a positive amount, shifts anything that is on the left
+ * of "cutx" to the right by the given amount.
+ *
+ * For a negative amount, shifts anything that is on the right
+ * of "cutx" to the left by the given absolute amount.
+ *
+ * @param cutx the X value to perform wrapping on
+ * @param amount shift amount and wrapping direction
+ */
+LWGEOM *lwgeom_wrapx(const LWGEOM *lwgeom, double cutx, double amount);
 
+
 /**
 * @brief Check whether or not a lwgeom is big enough to warrant a bounding box.
 *

Added: trunk/liblwgeom/lwgeom_wrapx.c
===================================================================
--- trunk/liblwgeom/lwgeom_wrapx.c	                        (rev 0)
+++ trunk/liblwgeom/lwgeom_wrapx.c	2016-06-16 09:09:26 UTC (rev 14961)
@@ -0,0 +1,190 @@
+/**********************************************************************
+ *
+ * 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 2016 Sandro Santilli <strk at kbt.net>
+ *
+ **********************************************************************/
+
+#include "../postgis_config.h"
+#define POSTGIS_DEBUG_LEVEL 4
+#include "lwgeom_geos.h"
+#include "liblwgeom_internal.h"
+
+#include <string.h>
+#include <assert.h>
+
+LWGEOM* lwgeom_wrapx(const LWGEOM* lwgeom_in, double cutx, double amount);
+static LWCOLLECTION* lwcollection_wrapx(const LWCOLLECTION* lwcoll_in, double cutx, double amount);
+
+static LWGEOM*
+lwgeom_split_wrapx(const LWGEOM* geom_in, double cutx, double amount)
+{
+	LWGEOM *blade, *split;
+	POINTARRAY *bladepa;
+	POINT4D pt;
+	const GBOX *box_in;
+	AFFINE affine = {
+		1, 0, 0,
+		0, 1, 0,
+		0, 0, 1,
+		amount, 0, 0,
+	};
+
+	/* Extract box */
+	/* TODO: check if the bbox should be force-recomputed */
+	box_in = lwgeom_get_bbox(geom_in);
+	if ( ! box_in ) {
+		/* must be empty */
+		return lwgeom_clone(geom_in);
+	}
+
+	LWDEBUGF(2, "BOX X range is %g..%g, cutx:%g, amount:%g", box_in->xmin, box_in->xmax, cutx, amount);
+
+	/* Check if geometry is fully on the side needing shift */
+	if ( ( amount < 0 && box_in->xmin >= cutx ) || ( amount > 0 && box_in->xmax <= cutx ) )
+	{
+		split = lwgeom_clone_deep(geom_in);
+		LWDEBUG(2, "returning the translated geometry");
+		lwgeom_affine(split, &affine);
+		return split;
+	}
+
+//DEBUG2: [lwgeom_wrapx.c:lwgeom_split_wrapx:58] BOX X range is 8..10, cutx:8, amount:-10
+
+	/* Check if geometry is fully on the side needing no shift */
+	if ( ( amount < 0 && box_in->xmax <= cutx ) || ( amount > 0 && box_in->xmin >= cutx ) )
+	{
+		LWDEBUG(2, "returning the cloned geometry");
+		return lwgeom_clone_deep(geom_in);
+	}
+
+	/* We need splitting here */
+
+	/* construct blade */
+	bladepa = ptarray_construct(0, 0, 2);
+	pt.x = cutx;
+	pt.y = box_in->ymin - 1;
+	ptarray_set_point4d(bladepa, 0, &pt);
+	pt.y = box_in->ymax + 1;
+	ptarray_set_point4d(bladepa, 1, &pt);
+	blade = lwline_as_lwgeom(lwline_construct(geom_in->srid, NULL, bladepa));
+
+	LWDEBUG(2, "splitting the geometry");
+
+	/* split by blade */
+	split = lwgeom_split(geom_in, blade);
+	lwgeom_free(blade);
+
+	/* iterate over components, translate if needed */
+	const LWCOLLECTION *col = lwgeom_as_lwcollection(split);
+	if ( ! col ) {
+		/* not split, this is unexpected */
+		lwnotice("WARNING: unexpected lack of split in lwgeom_split_wrapx");
+		return lwgeom_clone(geom_in);
+	}
+	LWCOLLECTION *col_out = lwcollection_wrapx(col, cutx, amount);
+	lwgeom_free(split);
+
+	/* unary-union the result (homogenize too ?) */
+	LWGEOM* out = lwgeom_unaryunion(lwcollection_as_lwgeom(col_out));
+	lwcollection_free(col_out);
+
+	return out;
+}
+
+static LWCOLLECTION*
+lwcollection_wrapx(const LWCOLLECTION* lwcoll_in, double cutx, double amount)
+{
+	LWGEOM** wrap_geoms=NULL;
+	LWCOLLECTION* out;
+	size_t i;
+
+	wrap_geoms = lwalloc(lwcoll_in->ngeoms * sizeof(LWGEOM*));
+	if ( ! wrap_geoms )
+	{
+		lwerror("Out of virtual memory");
+		return NULL;
+	}
+
+	for (i=0; i<lwcoll_in->ngeoms; ++i)
+	{
+		wrap_geoms[i] = lwgeom_wrapx(lwcoll_in->geoms[i], cutx, amount);
+		/* an exception should prevent this from ever returning NULL */
+		if ( ! wrap_geoms[i] ) {
+			while (--i>=0) lwgeom_free(wrap_geoms[i]);
+			lwfree(wrap_geoms);
+			return NULL;
+		}
+	}
+
+	/* Now wrap_geoms has wrap_geoms_size geometries */
+	out = lwcollection_construct(lwcoll_in->type, lwcoll_in->srid,
+	                             NULL, lwcoll_in->ngeoms, wrap_geoms);
+
+	return out;
+}
+
+/* exported */
+LWGEOM*
+lwgeom_wrapx(const LWGEOM* lwgeom_in, double cutx, double amount)
+{
+	/* Nothing to wrap in an empty geom */
+	if ( lwgeom_is_empty(lwgeom_in) ) return lwgeom_clone(lwgeom_in);
+
+	/* Nothing to wrap if shift amount is zero */
+	if ( amount == 0 ) return lwgeom_clone(lwgeom_in);
+
+	switch (lwgeom_in->type)
+	{
+	case LINETYPE:
+	case POLYGONTYPE:
+		return lwgeom_split_wrapx(lwgeom_in, cutx, amount);
+
+	case POINTTYPE:
+	{
+		const LWPOINT *pt = lwgeom_as_lwpoint(lwgeom_clone_deep(lwgeom_in));
+		POINT4D pt4d;
+		getPoint4d_p(pt->point, 0, &pt4d);
+
+		LWDEBUGF(2, "POINT X is %g, cutx:%g, amount:%g", pt4d.x, cutx, amount);
+
+		if ( ( amount < 0 && pt4d.x > cutx ) || ( amount > 0 && pt4d.x < cutx ) )
+		{
+			pt4d.x += amount;
+			ptarray_set_point4d(pt->point, 0, &pt4d);
+		}
+		return lwpoint_as_lwgeom(pt);
+	}
+
+	case MULTIPOINTTYPE:
+	case MULTIPOLYGONTYPE:
+	case MULTILINETYPE:
+	case COLLECTIONTYPE:
+		return lwcollection_as_lwgeom(
+						lwcollection_wrapx((const LWCOLLECTION*)lwgeom_in, cutx, amount)
+					 );
+
+	default:
+		lwerror("Wrapping of %s geometries is unsupported",
+		        lwtype_name(lwgeom_in->type));
+		return NULL;
+	}
+
+}

Modified: trunk/postgis/lwgeom_functions_basic.c
===================================================================
--- trunk/postgis/lwgeom_functions_basic.c	2016-06-16 08:46:07 UTC (rev 14960)
+++ trunk/postgis/lwgeom_functions_basic.c	2016-06-16 09:09:26 UTC (rev 14961)
@@ -110,6 +110,7 @@
 Datum ST_CollectionExtract(PG_FUNCTION_ARGS);
 Datum ST_CollectionHomogenize(PG_FUNCTION_ARGS);
 Datum ST_IsCollection(PG_FUNCTION_ARGS);
+Datum ST_WrapX(PG_FUNCTION_ARGS);
 
 
 /*------------------------------------------------------------------*/
@@ -1057,6 +1058,37 @@
 	PG_RETURN_POINTER(ret);
 }
 
+PG_FUNCTION_INFO_V1(ST_WrapX);
+Datum ST_WrapX(PG_FUNCTION_ARGS)
+{
+	Datum gdatum;
+	GSERIALIZED *geom_in;
+	LWGEOM *lwgeom_in, *lwgeom_out;
+	GSERIALIZED *geom_out;
+	double cutx;
+	double amount;
+
+	POSTGIS_DEBUG(2, "ST_WrapX called.");
+
+	gdatum = PG_GETARG_DATUM(0);
+	cutx = PG_GETARG_FLOAT8(1);
+	amount = PG_GETARG_FLOAT8(2);
+
+	//if ( ! amount ) PG_RETURN_DATUM(gdatum);
+
+	geom_in = ((GSERIALIZED *)PG_DETOAST_DATUM(gdatum));
+	lwgeom_in = lwgeom_from_gserialized(geom_in);
+
+	lwgeom_out = lwgeom_wrapx(lwgeom_in, cutx, amount);
+	geom_out = geometry_serialize(lwgeom_out);
+
+	lwgeom_free(lwgeom_in);
+	lwgeom_free(lwgeom_out);
+	PG_FREE_IF_COPY(geom_in, 0);
+
+	PG_RETURN_POINTER(geom_out);
+}
+
 PG_FUNCTION_INFO_V1(LWGEOM_inside_circle_point);
 Datum LWGEOM_inside_circle_point(PG_FUNCTION_ARGS)
 {

Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in	2016-06-16 08:46:07 UTC (rev 14960)
+++ trunk/postgis/postgis.sql.in	2016-06-16 09:09:26 UTC (rev 14961)
@@ -887,6 +887,12 @@
 	AS 'MODULE_PATHNAME', 'LWGEOM_longitude_shift'
 	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
 
+-- Availability: 2.3.0
+CREATE OR REPLACE FUNCTION ST_WrapX(geom geometry, wrap float8, move float8)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME', 'ST_WrapX'
+	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
+
 -- Availability: 1.2.2
 -- Deprecation in 2.2.0
 CREATE OR REPLACE FUNCTION ST_Shift_Longitude(geometry)

Added: trunk/regress/wrapx.sql
===================================================================
--- trunk/regress/wrapx.sql	                        (rev 0)
+++ trunk/regress/wrapx.sql	2016-06-16 09:09:26 UTC (rev 14961)
@@ -0,0 +1,94 @@
+CREATE FUNCTION test(geom geometry, wrap float8, amount float8, exp geometry)
+RETURNS text AS $$
+DECLARE
+  obt geometry;
+BEGIN
+  obt = ST_Normalize(ST_WrapX(geom, wrap, amount));
+  IF ST_OrderingEquals(obt, exp) THEN
+    RETURN 'OK';
+  ELSE
+    RETURN 'KO:' || ST_AsEWKT(obt) || ' != ' || ST_AsEWKT(exp);
+  END IF;
+END
+$$ LANGUAGE plpgsql;
+
+SELECT 'P1', test(
+  'POINT(0 0)', 2, 10,
+  'POINT(10 0)');
+
+SELECT 'P2', test(
+  'POINT(0 0)', 2, -10,
+  'POINT(0 0)');
+
+SELECT 'P3', test(
+  'POINT(0 0)', -2, -10,
+  'POINT(-10 0)');
+
+SELECT 'L1', test(
+  'LINESTRING(0 0,10 0)', 2, 10,
+  --'LINESTRING(2 0,12 0)');
+  'MULTILINESTRING((10 0,12 0),(2 0,10 0))');
+
+SELECT 'L2', test(
+  'LINESTRING(0 0,10 0)', 8, -10,
+  'MULTILINESTRING((0 0,8 0),(-2 0,0 0))');
+
+SELECT 'L3', test(
+  'LINESTRING(0 0,10 0)', 0, 10,
+  'LINESTRING(0 0,10 0)');
+
+SELECT 'L4', test(
+  'LINESTRING(0 0,10 0)', 10, -10,
+  'LINESTRING(0 0,10 0)');
+
+SELECT 'ML1', test(
+  'MULTILINESTRING((-10 0,0 0),(0 0,10 0))', 0, 20,
+  'MULTILINESTRING((10 0,20 0),(0 0,10 0))');
+
+SELECT 'ML2', test(
+  'MULTILINESTRING((-10 0,0 0),(0 0,10 0))', 0, -20,
+  'MULTILINESTRING((-10 0,0 0),(-20 0,-10 0))');
+
+SELECT 'ML3', test(
+  'MULTILINESTRING((10 0,5 0),(-10 0,0 0),(0 0,5 0))', 0, -20,
+  'MULTILINESTRING((-10 0,0 0),(-15 0,-10 0),(-20 0,-15 0))');
+
+SELECT 'A1', test(
+  'POLYGON((0 0,10 0,10 10,0 10,0 0),
+           (1 2,3 2,3 4,1 4,1 2),
+           (4 2,6 2,6 4,4 4,4 2),
+           (7 2,9 2,9 4,7 4,7 2))', 5, 10,
+  'POLYGON((5 0,5 2,6 2,6 4,5 4,5 10,10 10,15 10,15 4,14 4,14 2,15 2,15 0,10 0,5 0),
+           (11 2,13 2,13 4,11 4,11 2),
+           (7 2,9 2,9 4,7 4,7 2))');
+
+SELECT 'A2', test(
+  'POLYGON((0 0,10 0,10 10,0 10,0 0),
+           (1 2,3 2,3 4,1 4,1 2),
+           (4 2,6 2,6 4,4 4,4 2),
+           (7 2,9 2,9 4,7 4,7 2))', 5, -10,
+  'POLYGON((-5 0,-5 2,-4 2,-4 4,-5 4,-5 10,0 10,5 10,5 4,4 4,4 2,5 2,5 0,0 0,-5 0),
+           (1 2,3 2,3 4,1 4,1 2),
+           (-3 2,-1 2,-1 4,-3 4,-3 2))');
+
+SELECT 'C1', test(
+  'GEOMETRYCOLLECTION(
+    POLYGON((0 0,10 0,10 10,0 10,0 0),
+            (1 2,3 2,3 4,1 4,1 2),
+            (4 2,6 2,6 4,4 4,4 2),
+            (7 2,9 2,9 4,7 4,7 2)),
+    POINT(2 20),
+    POINT(7 -20),
+    LINESTRING(0 40,10 40)
+  )',
+  5, -10,
+  'GEOMETRYCOLLECTION(
+    POLYGON((-5 0,-5 2,-4 2,-4 4,-5 4,-5 10,0 10,5 10,5 4,4 4,4 2,5 2,5 0,0 0,-5 0),
+            (1 2,3 2,3 4,1 4,1 2),
+            (-3 2,-1 2,-1 4,-3 4,-3 2)),
+    MULTILINESTRING((0 40,5 40),(-5 40,0 40)),
+    POINT(2 20),
+    POINT(-3 -20)
+  )');
+
+DROP FUNCTION test(geometry, float8, float8, geometry);

Added: trunk/regress/wrapx_expected
===================================================================
--- trunk/regress/wrapx_expected	                        (rev 0)
+++ trunk/regress/wrapx_expected	2016-06-16 09:09:26 UTC (rev 14961)
@@ -0,0 +1,13 @@
+P1|OK
+P2|OK
+P3|OK
+L1|OK
+L2|OK
+L3|OK
+L4|OK
+ML1|OK
+ML2|OK
+ML3|OK
+A1|OK
+A2|OK
+C1|OK



More information about the postgis-tickets mailing list