[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