]> granicus.if.org Git - postgis/commitdiff
ST_OffsetCurve: support for MULTILINESTRING, GEOMETRYCOLLECTION and non-simple inputs
authorDarafei Praliaskouski <me@komzpa.net>
Mon, 5 Mar 2018 16:54:13 +0000 (16:54 +0000)
committerDarafei Praliaskouski <me@komzpa.net>
Mon, 5 Mar 2018 16:54:13 +0000 (16:54 +0000)
Closes #2508
Closes https://github.com/postgis/postgis/pull/224

git-svn-id: http://svn.osgeo.org/postgis/trunk@16444 b70326c6-7e19-0410-871a-916f4a2858ee

13 files changed:
NEWS
liblwgeom/cunit/cu_clean.c
liblwgeom/cunit/cu_geos.c
liblwgeom/liblwgeom.h.in
liblwgeom/lwcollection.c
liblwgeom/lwgeom_geos.c
liblwgeom/lwgeom_geos.h
liblwgeom/lwgeom_geos_clean.c
liblwgeom/lwgeom_geos_node.c
liblwgeom/lwlinearreferencing.c
postgis/lwgeom_geos.c
regress/offsetcurve.sql
regress/offsetcurve_expected

diff --git a/NEWS b/NEWS
index 5e54a46c9f02558a13e847900bea4d8b87d0dfde..9d3683e80c158f3c9a6020d58c03736c4de05eea 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -37,10 +37,11 @@ PostGIS 2.5.0
   - #3977, ST_ClusterKMeans is now faster and simpler (Darafei Praliaskouski)
   - #3982, ST_AsEncodedPolyline supports LINESTRING EMPTY and MULTIPOINT EMPTY
            (Darafei Praliaskouski)
-  - #3986, ST_AsText now has second argument to limit decimal digits 
+  - #3986, ST_AsText now has second argument to limit decimal digits
            (Marc Ducobu, Darafei Praliaskouski)
   - #4020, Casting from box3d to geometry now returns correctly connected
            PolyhedralSurface (Matthias Bay)
+  - #2508, ST_OffsetCurve now works with collections (Darafei Praliaskouski)
 
 PostGIS 2.4.0
 2017/09/30
index 5fb32c27798fe48928fb3f8aa742b55797b36470..e3652c519a1c858d776324d4789e961fe5f95a90 100644 (file)
@@ -101,7 +101,7 @@ static void test_lwgeom_make_valid(void)
 /*     CU_ASSERT_STRING_EQUAL(ewkt,
 "GEOMETRYCOLLECTION(POINT(0 0),MULTIPOLYGON(((5 5,0 0,0 10,5 5)),((5 5,10 10,10 0,5 5))),LINESTRING(10 0,10 10))");*/
        gexp = lwgeom_from_wkt(
-"GEOMETRYCOLLECTION(POINT(0 0),MULTIPOLYGON(((5 5,0 0,0 10,5 5)),((5 5,10 10,10 0,5 5))),LINESTRING(10 0,10 10))",
+"GEOMETRYCOLLECTION(MULTIPOLYGON(((5 5,10 10,10 0,5 5)),((0 0,0 10,5 5,0 0))),LINESTRING(10 0,10 10),POINT(0 0))",
                LW_PARSER_CHECK_NONE);
        check_geom_equal(gout, gexp);
        lwfree(ewkt);
index 3b3f5ec4de40c83606da1d7e40416c55892c9d06..6c56064ff9cd05911cb49eac617b1440d1b92f69 100644 (file)
@@ -63,8 +63,6 @@ static void test_geos_noop(void)
                lwgeom_free(geom_out);
                lwgeom_free(geom_in);
        }
-
-
 }
 
 static void test_geos_linemerge(void)
@@ -93,6 +91,23 @@ static void test_geos_linemerge(void)
        lwgeom_free(geom2);
 }
 
+static void
+test_geos_offsetcurve(void)
+{
+       char* ewkt;
+       char* out_ewkt;
+       LWGEOM* geom1;
+       LWGEOM* geom2;
+
+       ewkt = "MULTILINESTRING((-10 0, -10 100), (0 -5, 0 0))";
+       geom1 = lwgeom_from_wkt(ewkt, LW_PARSER_CHECK_NONE);
+       geom2 = lwgeom_offsetcurve(geom1, 2, 10, 1, 1);
+       out_ewkt = lwgeom_to_ewkt((LWGEOM*)geom2);
+       ASSERT_STRING_EQUAL(out_ewkt, "MULTILINESTRING((-12 0,-12 100),(-2 -5,-2 0))");
+       lwfree(out_ewkt);
+       lwgeom_free(geom1);
+       lwgeom_free(geom2);
+}
 
 static void test_geos_subdivide(void)
 {
@@ -134,4 +149,5 @@ void geos_suite_setup(void)
        PG_ADD_TEST(suite, test_geos_noop);
        PG_ADD_TEST(suite, test_geos_subdivide);
        PG_ADD_TEST(suite, test_geos_linemerge);
+       PG_ADD_TEST(suite, test_geos_offsetcurve);
 }
index dead6b6eea7a778c246ee98bbdb61c041fe4de51..5455a57ff81af995e68fab157b09d3194e91a5c8 100644 (file)
@@ -630,6 +630,7 @@ extern LWMPOLY* lwmpoly_add_lwpoly(LWMPOLY *mobj, const LWPOLY *obj);
 extern LWPSURFACE* lwpsurface_add_lwpoly(LWPSURFACE *mobj, const LWPOLY *obj);
 extern LWTIN* lwtin_add_lwtriangle(LWTIN *mobj, const LWTRIANGLE *obj);
 
+extern LWCOLLECTION* lwcollection_concat_in_place(LWCOLLECTION* col1, const LWCOLLECTION* col2);
 
 
 /***********************************************************************
@@ -2290,16 +2291,15 @@ LWGEOM* lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2);
 /*
  * An offset curve against the input line.
  *
- * @param lwline a lineal geometry
+ * @param geom a lineal geometry or collection of them
  * @param size offset distance. Offset left if negative and right if positive
  * @param quadsegs number of quadrature segments in curves (try 8)
  * @param joinStyle (1 = round, 2 = mitre, 3 = bevel)
  * @param mitreLimit (try 5.0)
  * @return derived geometry (linestring or multilinestring)
  *
- * Requires GEOS-3.2.0+
  */
-LWGEOM* lwgeom_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit);
+LWGEOM* lwgeom_offsetcurve(const LWGEOM *geom, double size, int quadsegs, int joinStyle, double mitreLimit);
 
 /*
  * Return true if the input geometry is "simple" as per OGC defn.
index 6b59b3719ff178e81d08d7591df19c84584445f6..892bbb21331f8da25e1c1e372e3f252cf7782f77 100644 (file)
@@ -186,9 +186,10 @@ void lwcollection_reserve(LWCOLLECTION *col, uint32_t ngeoms)
 */
 LWCOLLECTION* lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
 {
-       if ( col == NULL || geom == NULL ) return NULL;
+       if (!col || !geom) return NULL;
 
-       if ( col->geoms == NULL && (col->ngeoms || col->maxgeoms) ) {
+       if (!col->geoms && (col->ngeoms || col->maxgeoms))
+       {
                lwerror("Collection is in inconsistent state. Null memory but non-zero collection counts.");
                return NULL;
        }
@@ -200,7 +201,7 @@ LWCOLLECTION* lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
        }
 
        /* In case this is a truly empty, make some initial space  */
-       if ( col->geoms == NULL )
+       if (!col->geoms)
        {
                col->maxgeoms = 2;
                col->ngeoms = 0;
@@ -214,17 +215,16 @@ LWCOLLECTION* lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
        /* See http://trac.osgeo.org/postgis/ticket/2933 */
        /* Make sure we don't already have a reference to this geom */
        {
-       int i = 0;
-       for ( i = 0; i < col->ngeoms; i++ )
-       {
-               if ( col->geoms[i] == geom )
+               uint32_t i = 0;
+               for (i = 0; i < col->ngeoms; i++)
                {
-            lwerror("%s [%d] found duplicate geometry in collection %p == %p", __FILE__, __LINE__, col->geoms[i], geom);
-                       LWDEBUGF(4, "Found duplicate geometry in collection %p == %p", col->geoms[i], geom);
-                       return col;
+                       if (col->geoms[i] == geom)
+                       {
+                               lwerror("%s [%d] found duplicate geometry in collection %p == %p", __FILE__, __LINE__, col->geoms[i], geom);
+                               return col;
+                       }
                }
        }
-       }
 #endif
 
        col->geoms[col->ngeoms] = (LWGEOM*)geom;
@@ -232,6 +232,20 @@ LWCOLLECTION* lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
        return col;
 }
 
+/**
+ * Appends all geometries from col2 to col1 in place.
+ * Caller is responsible to release col2.
+ */
+LWCOLLECTION *
+lwcollection_concat_in_place(LWCOLLECTION *col1, const LWCOLLECTION *col2)
+{
+       uint32_t i;
+       if (!col1 || !col2) return NULL;
+       for (i = 0; i < col2->ngeoms; i++)
+               col1 = lwcollection_add_lwgeom(col1, col2->geoms[i]);
+       return col1;
+}
+
 LWCOLLECTION*
 lwcollection_segmentize2d(const LWCOLLECTION* col, double dist)
 {
index daa925d30a13793b6527c2cadc17d600c5c47db5..254be6c4a2729305cc05c01d0fc42ad05f6632ff 100644 (file)
@@ -491,7 +491,11 @@ lwgeom_geos_version()
 inline static int32_t
 get_result_srid(const LWGEOM* geom1, const LWGEOM* geom2, const char* funcname)
 {
-       if (!geom1) lwerror("%s: First argument is null pointer", funcname);
+       if (!geom1)
+       {
+               lwerror("%s: First argument is null pointer", funcname);
+               return SRID_INVALID;
+       }
        if (geom2 && (geom1->srid != geom2->srid))
        {
                lwerror("%s: Operation on mixed SRID geometries (%d != %d)", funcname, geom1->srid, geom2->srid);
@@ -1206,8 +1210,8 @@ lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2)
        return result;
 }
 
-LWGEOM*
-lwgeom_offsetcurve(const LWLINE* lwline, double size, int quadsegs, int joinStyle, double mitreLimit)
+static LWGEOM *
+lwline_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit)
 {
        LWGEOM* result;
        LWGEOM* geom = lwline_as_lwgeom(lwline);
@@ -1223,13 +1227,116 @@ lwgeom_offsetcurve(const LWLINE* lwline, double size, int quadsegs, int joinStyl
 
        g3 = GEOSOffsetCurve(g1, size, quadsegs, joinStyle, mitreLimit);
 
-       if (!g3) return geos_clean_and_fail(g1, NULL, NULL, __func__);
+       if (!g3)
+       {
+               geos_clean(g1, NULL, NULL);
+               return NULL;
+       }
 
        if (!output_geos_as_lwgeom(&g3, &result, srid, is3d, __func__))
                return geos_clean_and_fail(g1, NULL, g3, __func__);
 
        geos_clean(g1, NULL, g3);
+       return result;
+}
+
+static LWGEOM *
+lwcollection_offsetcurve(const LWCOLLECTION *col, double size, int quadsegs, int joinStyle, double mitreLimit)
+{
+       const LWGEOM *geom = lwcollection_as_lwgeom(col);
+       int32_t srid = get_result_srid(geom, NULL, __func__);
+       uint8_t is3d = FLAGS_GET_Z(col->flags);
+       LWCOLLECTION *result;
+       LWGEOM *tmp;
+       uint32_t i;
+       if (srid == SRID_INVALID) return NULL;
+
+       result = lwcollection_construct_empty(MULTILINETYPE, srid, is3d, LW_FALSE);
+
+       for (i = 0; i < col->ngeoms; i++)
+       {
+               tmp = lwgeom_offsetcurve(col->geoms[i], size, quadsegs, joinStyle, mitreLimit);
+
+               if (!tmp)
+               {
+                       lwcollection_free(result);
+                       return NULL;
+               }
+
+               if (!lwgeom_is_empty(tmp))
+               {
+                       if (lwgeom_is_collection(tmp))
+                               result = lwcollection_concat_in_place(result, lwgeom_as_lwcollection(tmp));
+                       else
+                               result = lwcollection_add_lwgeom(result, tmp);
+
+                       if (!result)
+                       {
+                               lwgeom_free(tmp);
+                               return NULL;
+                       }
+               }
+       }
 
+       if (result->ngeoms == 1)
+       {
+               tmp = result->geoms[0];
+               lwcollection_release(result);
+               return tmp;
+       }
+       else
+               return lwcollection_as_lwgeom(result);
+}
+
+LWGEOM*
+lwgeom_offsetcurve(const LWGEOM* geom, double size, int quadsegs, int joinStyle, double mitreLimit)
+{
+       int32_t srid = get_result_srid(geom, NULL, __func__);
+       LWGEOM *result = NULL;
+       LWGEOM *noded = NULL;
+       if (srid == SRID_INVALID) return NULL;
+
+       if (lwgeom_dimension(geom) != 1)
+       {
+               lwerror("%s: input is not linear", __func__, lwtype_name(geom->type));
+               return NULL;
+       }
+
+       while (!result)
+       {
+               switch (geom->type)
+               {
+               case LINETYPE:
+                       result = lwline_offsetcurve(lwgeom_as_lwline(geom), size, quadsegs, joinStyle, mitreLimit);
+                       break;
+               case COLLECTIONTYPE:
+               case MULTILINETYPE:
+                       result = lwcollection_offsetcurve(lwgeom_as_lwcollection(geom), size, quadsegs, joinStyle, mitreLimit);
+                       break;
+               default:
+                       lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type));
+                       return NULL;
+               }
+
+               if (result)
+                       return result;
+               else if (!noded)
+               {
+                       noded = lwgeom_node(geom);
+                       if (!noded)
+                       {
+                               lwfree(noded);
+                               lwerror("lwgeom_offsetcurve: cannot node input");
+                               return NULL;
+                       }
+                       geom = noded;
+               }
+               else
+               {
+                       lwerror("lwgeom_offsetcurve: noded geometry cannot be offset");
+                       return NULL;
+               }
+       }
        return result;
 }
 
@@ -1334,19 +1441,17 @@ lwpoly_to_points(const LWPOLY* lwpoly, uint32_t npoints)
        }
 
        /* shuffle */
+       n = sample_height * sample_width;
+       if (n > 1)
        {
-               n = sample_height * sample_width;
-               if (n > 1)
+               for (i = 0; i < n - 1; ++i)
                {
-                       for (i = 0; i < n - 1; ++i)
-                       {
-                               size_t rnd = (size_t)rand();
-                               size_t j = i + rnd / (RAND_MAX / (n - i) + 1);
+                       size_t rnd = (size_t)rand();
+                       size_t j = i + rnd / (RAND_MAX / (n - i) + 1);
 
-                               memcpy(tmp, (char*)cells + j * stride, size);
-                               memcpy((char*)cells + j * stride, (char*)cells + i * stride, size);
-                               memcpy((char*)cells + i * stride, tmp, size);
-                       }
+                       memcpy(tmp, (char *)cells + j * stride, size);
+                       memcpy((char *)cells + j * stride, (char *)cells + i * stride, size);
+                       memcpy((char *)cells + i * stride, tmp, size);
                }
        }
 
index a2d28e55863ddf556b1a57bb2be7f74d4c118a20..9845cff11578379580ca6f7f856aba4ccb73df55 100644 (file)
@@ -35,13 +35,14 @@ LWGEOM* GEOS2LWGEOM(const GEOSGeometry* geom, uint8_t want3d);
 GEOSGeometry* LWGEOM2GEOS(const LWGEOM* g, uint8_t autofix);
 GEOSGeometry* GBOX2GEOS(const GBOX* g);
 GEOSGeometry* LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in);
+GEOSGeometry* LWGEOM_GEOS_makeValid(const GEOSGeometry*);
 
 GEOSGeometry* make_geos_point(double x, double y);
 GEOSGeometry* make_geos_segment(double x1, double y1, double x2, double y2);
 
-int cluster_intersecting(GEOSGeometry** geoms, uint32_t num_geoms, GEOSGeometry*** clusterGeoms, uint32_t* num_clusters);
-int cluster_within_distance(LWGEOM** geoms, uint32_t num_geoms, double tolerance, LWGEOM*** clusterGeoms, uint32_t* num_clusters);
-int union_dbscan(LWGEOM** geoms, uint32_t num_geoms, UNIONFIND* uf, double eps, uint32_t min_points, char** is_in_cluster_ret);
+int cluster_intersecting(GEOSGeometry **geoms, uint32_t num_geoms, GEOSGeometry ***clusterGeoms, uint32_t *num_clusters);
+int cluster_within_distance(LWGEOM **geoms, uint32_t num_geoms, double tolerance, LWGEOM ***clusterGeoms, uint32_t *num_clusters);
+int union_dbscan(LWGEOM **geoms, uint32_t num_geoms, UNIONFIND *uf, double eps, uint32_t min_points, char **is_in_cluster_ret);
 
 POINTARRAY* ptarray_from_GEOSCoordSeq(const GEOSCoordSequence* cs, uint8_t want3d);
 
index 53bc6b0d713f766fc9fa513cb389c8df38913044..30a52b12d1ba585b2fa3846154be375196b63502 100644 (file)
@@ -321,34 +321,28 @@ static GEOSGeometry*
 LWGEOM_GEOS_nodeLines(const GEOSGeometry* lines)
 {
        GEOSGeometry* noded;
-       GEOSGeometry* point;
 
-       /*
-        * Union with first geometry point, obtaining full noding
-        * and dissolving of duplicated repeated points
-        *
-        * TODO: substitute this with UnaryUnion?
-        */
-
-       point = LWGEOM_GEOS_getPointN(lines, 0);
-       if (!point) return NULL;
+       /* first, try just to node the line */
+       noded = GEOSNode(lines);
+       if (!noded) noded = (GEOSGeometry *)lines;
 
-       LWDEBUGF(3, "Boundary point: %s", lwgeom_to_ewkt(GEOS2LWGEOM(point, 0)));
+       /* GEOS3.7 UnaryUnion fails on regression tests, cannot be used here */
 
-       noded = GEOSUnion(lines, point);
-       if (NULL == noded)
+       /* fall back to union of first point with geometry */
+       if (!GEOSisValid(noded))
        {
-               GEOSGeom_destroy(point);
-               return NULL;
+               GEOSGeometry *unioned, *point;
+               point = LWGEOM_GEOS_getPointN(lines, 0);
+               if (!point) return NULL;
+               unioned = GEOSUnion(noded, point);
+               if (!unioned)
+                       return NULL;
+               else
+               {
+                       GEOSGeom_destroy(noded);
+                       return unioned;
+               }
        }
-
-       GEOSGeom_destroy(point);
-
-       LWDEBUGF(3,
-                "LWGEOM_GEOS_nodeLines: in[%s] out[%s]",
-                lwgeom_to_ewkt(GEOS2LWGEOM(lines, 0)),
-                lwgeom_to_ewkt(GEOS2LWGEOM(noded, 0)));
-
        return noded;
 }
 
@@ -712,8 +706,6 @@ LWGEOM_GEOS_makeValidMultiLine(const GEOSGeometry* gin)
        return gout;
 }
 
-static GEOSGeometry* LWGEOM_GEOS_makeValid(const GEOSGeometry*);
-
 /*
  * We expect initGEOS being called already.
  * Will return NULL on error (expect error handler being called by then)
@@ -770,7 +762,7 @@ LWGEOM_GEOS_makeValidCollection(const GEOSGeometry* gin)
        return gout;
 }
 
-static GEOSGeometry*
+GEOSGeometry*
 LWGEOM_GEOS_makeValid(const GEOSGeometry* gin)
 {
        GEOSGeometry* gout;
@@ -919,7 +911,7 @@ lwgeom_make_valid(LWGEOM* lwgeom_in)
        initGEOS(lwgeom_geos_error, lwgeom_geos_error);
 
        lwgeom_out = lwgeom_in;
-       geosgeom = LWGEOM2GEOS(lwgeom_out, 0);
+       geosgeom = LWGEOM2GEOS(lwgeom_out, 1);
        if (!geosgeom)
        {
                LWDEBUGF(4,
index c94fae72ef5173cecfa6042e2fda7d23f5c41ecc..8e5e8c3bc9e11110008a40ee94d7b099347b6f2b 100644 (file)
@@ -244,7 +244,7 @@ lwgeom_node(const LWGEOM* lwgeom_in)
        lwgeom_free(ep);
        lwcollection_free(col);
 
-       lines->srid = lwgeom_in->srid;
+       lwgeom_set_srid(lines, lwgeom_in->srid);
        return (LWGEOM*)lines;
 }
 
index e6eead38198bd05ea0aa88c3e0545362a84e5e92..4306eba3aa101d926a12dffd80c278c6b7177989 100644 (file)
@@ -822,7 +822,7 @@ lwgeom_clip_to_ordinate_range(const LWGEOM *lwin, char ordinate, double from, do
                else if ( type == LINETYPE )
                {
                        /* lwgeom_offsetcurve(line, offset, quadsegs, joinstyle (round), mitrelimit) */
-                       LWGEOM *lwoff = lwgeom_offsetcurve(lwgeom_as_lwline(out_col->geoms[i]), offset, 8, 1, 5.0);
+                       LWGEOM *lwoff = lwgeom_offsetcurve(out_col->geoms[i], offset, 8, 1, 5.0);
                        if ( ! lwoff )
                        {
                                lwerror("lwgeom_offsetcurve returned null");
index 43d091c42b10c9380c71e5d3e21fd116f539756d..82a3a1832d493842b7e90c7014b0e1728a8a3fb4 100644 (file)
@@ -1112,13 +1112,6 @@ Datum ST_OffsetCurve(PG_FUNCTION_ARGS)
        gser_input = PG_GETARG_GSERIALIZED_P(0);
        size = PG_GETARG_FLOAT8(1);
 
-       /* Check for a useable type */
-       if ( gserialized_get_type(gser_input) != LINETYPE )
-       {
-               lwpgerror("ST_OffsetCurve only works with LineStrings");
-               PG_RETURN_NULL();
-       }
-
        /*
        * For distance == 0, just return the input.
        * Note that due to a bug, GEOS 3.3.0 would return EMPTY.
@@ -1207,7 +1200,7 @@ Datum ST_OffsetCurve(PG_FUNCTION_ARGS)
                pfree(paramstr); /* alloc'ed in text_to_cstring */
        }
 
-       lwgeom_result = lwgeom_offsetcurve(lwgeom_as_lwline(lwgeom_input), size, quadsegs, joinStyle, mitreLimit);
+       lwgeom_result = lwgeom_offsetcurve(lwgeom_input, size, quadsegs, joinStyle, mitreLimit);
 
        if (!lwgeom_result)
                lwpgerror("ST_OffsetCurve: lwgeom_offsetcurve returned NULL");
index f8acbdd326ac3bd739f5da64027856fac5b2a95e..72339185608bb1d76c7b47e8ea69e48786705f89 100644 (file)
@@ -51,3 +51,11 @@ SELECT 't14', ST_AsEWKT(ST_OffsetCurve(
  'LINESTRING(0 0,0 20, 10 20, 10 10, 0 10)', -2,
  ''
 ));
+SELECT 't15', ST_AsEWKT(ST_OffsetCurve(
+ 'GEOMETRYCOLLECTION(LINESTRING(0 0,0 20, 10 20, 10 10, 0 10),MULTILINESTRING((2 0,2 20, 12 20, 12 10, 2 10),(3 0,3 20, 13 20, 13 10, 3 10)))', -2,
+ ''
+));
+select '#2508', ST_IsValid(ST_OffsetCurve(
+       '0102000020BB0B000010000000FBB019D1AD1537414A733C4E5333534167CE8F06B815374151F4926C4D335341C4899405B61537413DB009254A335341513EE234AD1537413689A27947335341E38CCA31AB1537415D00E28E44335341951F7F0BB315374104E4CA2441335341A581F041BF153741D46F9F8A3F33534100C27968CD153741C6CAAFE83F335341493DB10CDA1537418919897142335341FCA312FCE01537415D1A1F8045335341C62D3822DD153741554B118E483353411B98FE61D1153741FC35CEE14A33534106DCFDA5C5153741573BD3584B33534167CE8F06B815374151F4926C4D335341FBB019D1AD1537414A733C4E533353414AEB33644E153741595A854786335341',
+       10
+));
index f70e5db90c10451fb623a8dfded70368ca0ceaa4..cc2d0afbcd7d21b521df0d6220dae6070df913bb 100644 (file)
@@ -1,4 +1,4 @@
-ERROR:  ST_OffsetCurve only works with LineStrings
+ERROR:  lwgeom_offsetcurve: input is not linear
 t0|SRID=42;LINESTRING(0 0,10 0)
 t1|SRID=42;LINESTRING(0 10,10 10)
 t2|SRID=42;LINESTRING(10 -10,0 -10)
@@ -16,3 +16,5 @@ t11|LINESTRING(37.6 39.2,39.2 36.6,42 35.8,43 34.8,46.4 33.6,48.2 30,48.8 30,50.
 t12|LINESTRING(57.4 31,53.4 30.2,51.2 26,45.8 26,43.6 30.4,41 31.2,40 32.2,36.8 33.4,34.4 36.8)
 t13|LINESTRING(-2 0,-2 22,12 22,12 8,2 8)
 t14|MULTILINESTRING((2 12,8 12,8 18,2 18,2 12),(2 8,2 0))
+t15|MULTILINESTRING((2 12,8 12,8 18,2 18,2 12),(2 8,2 0),(4 12,10 12,10 18,4 18,4 12),(4 8,4 0),(5 12,11 12,11 18,5 18,5 12),(5 8,5 0))
+#2508|t