From: Sandro Santilli Date: Thu, 16 Jun 2016 09:09:26 +0000 (+0000) Subject: Implement lwgeom_wrapx and ST_WrapX X-Git-Tag: 2.3.0beta1~72 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=46a110e7a1224e8a8ef322c0e85f1ba68cccd99d;p=postgis Implement lwgeom_wrapx and ST_WrapX Includes tests (both cunit and regress) and documentation. Closes #454 git-svn-id: http://svn.osgeo.org/postgis/trunk@14961 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/NEWS b/NEWS index 6dd21f428..4d9868407 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ PostGIS 2.3.0 - 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) diff --git a/doc/reference_processing.xml b/doc/reference_processing.xml index bdce63eb8..5b6da95e0 100644 --- a/doc/reference_processing.xml +++ b/doc/reference_processing.xml @@ -2703,7 +2703,73 @@ LINESTRING(241.42 38.38,241.8 38.45) See Also - , , + + + + + + + + + ST_WrapX + + Wrap a geometry around an X value. + + + + + + geometry ST_WrapX + geometry geom + float8 wrap + float8 move + + + + + + Description + + +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. + + + +This is useful to "recenter" long-lat input to have features +of interest not spawned from one side to the other. + + + Availability: 2.3.0 + + &Z_support; + + + + + + Examples + + +-- 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); + + + + + + See Also + diff --git a/liblwgeom/Makefile.in b/liblwgeom/Makefile.in index 1c76b53dc..f2652278f 100644 --- a/liblwgeom/Makefile.in +++ b/liblwgeom/Makefile.in @@ -112,6 +112,7 @@ SA_OBJS = \ lwgeom_geos_split.o \ lwgeom_topo.o \ lwgeom_transform.o \ + lwgeom_wrapx.o \ lwunionfind.o \ effectivearea.o \ lwkmeans.o \ diff --git a/liblwgeom/cunit/Makefile.in b/liblwgeom/cunit/Makefile.in index f0e899d82..e6cb4abe3 100644 --- a/liblwgeom/cunit/Makefile.in +++ b/liblwgeom/cunit/Makefile.in @@ -62,6 +62,7 @@ OBJS= \ cu_iterator.o \ cu_varint.o \ cu_unionfind.o \ + cu_wrapx.o \ cu_tester.o ifeq (@SFCGAL@,sfcgal) diff --git a/liblwgeom/cunit/cu_tester.c b/liblwgeom/cunit/cu_tester.c index 7f266d5a7..306159885 100644 --- a/liblwgeom/cunit/cu_tester.c +++ b/liblwgeom/cunit/cu_tester.c @@ -68,6 +68,7 @@ extern void wkb_out_suite_setup(void); 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 @@ PG_SuiteSetup setupfuncs[] = wkb_out_suite_setup, wkt_in_suite_setup, wkt_out_suite_setup, + wrapx_suite_setup, NULL }; @@ -274,7 +276,7 @@ cu_noticereporter(const char *fmt, va_list ap) 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 diff --git a/liblwgeom/cunit/cu_wrapx.c b/liblwgeom/cunit/cu_wrapx.c new file mode 100644 index 000000000..bc5044b84 --- /dev/null +++ b/liblwgeom/cunit/cu_wrapx.c @@ -0,0 +1,134 @@ +/********************************************************************** + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.net + * + * Copyright (C) 2016 Sandro Santilli + * + * 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); +} diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in index d36fa6081..c028dc221 100644 --- a/liblwgeom/liblwgeom.h.in +++ b/liblwgeom/liblwgeom.h.in @@ -1218,6 +1218,20 @@ extern void interpolate_point4d(POINT4D *A, POINT4D *B, POINT4D *I, double F); 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. diff --git a/liblwgeom/lwgeom_wrapx.c b/liblwgeom/lwgeom_wrapx.c new file mode 100644 index 000000000..1f35d98fd --- /dev/null +++ b/liblwgeom/lwgeom_wrapx.c @@ -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 . + * + ********************************************************************** + * + * Copyright 2016 Sandro Santilli + * + **********************************************************************/ + +#include "../postgis_config.h" +#define POSTGIS_DEBUG_LEVEL 4 +#include "lwgeom_geos.h" +#include "liblwgeom_internal.h" + +#include +#include + +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; ingeoms; ++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; + } + +} diff --git a/postgis/lwgeom_functions_basic.c b/postgis/lwgeom_functions_basic.c index f023f5aa8..34e85f3db 100644 --- a/postgis/lwgeom_functions_basic.c +++ b/postgis/lwgeom_functions_basic.c @@ -110,6 +110,7 @@ Datum ST_MakeEnvelope(PG_FUNCTION_ARGS); 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 @@ Datum LWGEOM_longitude_shift(PG_FUNCTION_ARGS) 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) { diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in index d53be8df3..59b3d177c 100644 --- a/postgis/postgis.sql.in +++ b/postgis/postgis.sql.in @@ -887,6 +887,12 @@ CREATE OR REPLACE FUNCTION ST_ShiftLongitude(geometry) 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) diff --git a/regress/wrapx.sql b/regress/wrapx.sql new file mode 100644 index 000000000..d08dc9c9c --- /dev/null +++ b/regress/wrapx.sql @@ -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); diff --git a/regress/wrapx_expected b/regress/wrapx_expected new file mode 100644 index 000000000..79be8f3f9 --- /dev/null +++ b/regress/wrapx_expected @@ -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