[postgis-tickets] r16444 - ST_OffsetCurve: support for MULTILINESTRING, GEOMETRYCOLLECTION and non-simple inputs

Darafei komzpa at gmail.com
Mon Mar 5 08:54:13 PST 2018


Author: komzpa
Date: 2018-03-05 08:54:13 -0800 (Mon, 05 Mar 2018)
New Revision: 16444

Modified:
   trunk/NEWS
   trunk/liblwgeom/cunit/cu_clean.c
   trunk/liblwgeom/cunit/cu_geos.c
   trunk/liblwgeom/liblwgeom.h.in
   trunk/liblwgeom/lwcollection.c
   trunk/liblwgeom/lwgeom_geos.c
   trunk/liblwgeom/lwgeom_geos.h
   trunk/liblwgeom/lwgeom_geos_clean.c
   trunk/liblwgeom/lwgeom_geos_node.c
   trunk/liblwgeom/lwlinearreferencing.c
   trunk/postgis/lwgeom_geos.c
   trunk/regress/offsetcurve.sql
   trunk/regress/offsetcurve_expected
Log:
ST_OffsetCurve: support for MULTILINESTRING, GEOMETRYCOLLECTION and non-simple inputs

Closes #2508
Closes https://github.com/postgis/postgis/pull/224



Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/NEWS	2018-03-05 16:54:13 UTC (rev 16444)
@@ -37,10 +37,11 @@
   - #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

Modified: trunk/liblwgeom/cunit/cu_clean.c
===================================================================
--- trunk/liblwgeom/cunit/cu_clean.c	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/cunit/cu_clean.c	2018-03-05 16:54:13 UTC (rev 16444)
@@ -101,7 +101,7 @@
 /*	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);

Modified: trunk/liblwgeom/cunit/cu_geos.c
===================================================================
--- trunk/liblwgeom/cunit/cu_geos.c	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/cunit/cu_geos.c	2018-03-05 16:54:13 UTC (rev 16444)
@@ -63,8 +63,6 @@
 		lwgeom_free(geom_out);
 		lwgeom_free(geom_in);
 	}
-
-
 }
 
 static void test_geos_linemerge(void)
@@ -93,7 +91,24 @@
 	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)
 {
 #if POSTGIS_GEOS_VERSION < 35
@@ -134,4 +149,5 @@
 	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);
 }

Modified: trunk/liblwgeom/liblwgeom.h.in
===================================================================
--- trunk/liblwgeom/liblwgeom.h.in	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/liblwgeom.h.in	2018-03-05 16:54:13 UTC (rev 16444)
@@ -630,6 +630,7 @@
 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,7 +2291,7 @@
 /*
  * 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)
@@ -2297,9 +2298,8 @@
  * @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.

Modified: trunk/liblwgeom/lwcollection.c
===================================================================
--- trunk/liblwgeom/lwcollection.c	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/lwcollection.c	2018-03-05 16:54:13 UTC (rev 16444)
@@ -186,9 +186,10 @@
 */
 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 @@
 	}
 
 	/* 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 @@
 	/* 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 @@
 	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)
 {

Modified: trunk/liblwgeom/lwgeom_geos.c
===================================================================
--- trunk/liblwgeom/lwgeom_geos.c	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/lwgeom_geos.c	2018-03-05 16:54:13 UTC (rev 16444)
@@ -491,7 +491,11 @@
 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 @@
 	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 @@
 
 	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 @@
 	}
 
 	/* 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);
 		}
 	}
 

Modified: trunk/liblwgeom/lwgeom_geos.h
===================================================================
--- trunk/liblwgeom/lwgeom_geos.h	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/lwgeom_geos.h	2018-03-05 16:54:13 UTC (rev 16444)
@@ -35,13 +35,14 @@
 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);
 

Modified: trunk/liblwgeom/lwgeom_geos_clean.c
===================================================================
--- trunk/liblwgeom/lwgeom_geos_clean.c	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/lwgeom_geos_clean.c	2018-03-05 16:54:13 UTC (rev 16444)
@@ -321,34 +321,28 @@
 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?
-	 */
+	/* first, try just to node the line */
+	noded = GEOSNode(lines);
+	if (!noded) noded = (GEOSGeometry *)lines;
 
-	point = LWGEOM_GEOS_getPointN(lines, 0);
-	if (!point) return NULL;
+	/* GEOS3.7 UnaryUnion fails on regression tests, cannot be used here */
 
-	LWDEBUGF(3, "Boundary point: %s", lwgeom_to_ewkt(GEOS2LWGEOM(point, 0)));
-
-	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 @@
 	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 @@
 	return gout;
 }
 
-static GEOSGeometry*
+GEOSGeometry*
 LWGEOM_GEOS_makeValid(const GEOSGeometry* gin)
 {
 	GEOSGeometry* gout;
@@ -919,7 +911,7 @@
 	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,

Modified: trunk/liblwgeom/lwgeom_geos_node.c
===================================================================
--- trunk/liblwgeom/lwgeom_geos_node.c	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/lwgeom_geos_node.c	2018-03-05 16:54:13 UTC (rev 16444)
@@ -244,7 +244,7 @@
 	lwgeom_free(ep);
 	lwcollection_free(col);
 
-	lines->srid = lwgeom_in->srid;
+	lwgeom_set_srid(lines, lwgeom_in->srid);
 	return (LWGEOM*)lines;
 }
 

Modified: trunk/liblwgeom/lwlinearreferencing.c
===================================================================
--- trunk/liblwgeom/lwlinearreferencing.c	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/liblwgeom/lwlinearreferencing.c	2018-03-05 16:54:13 UTC (rev 16444)
@@ -822,7 +822,7 @@
 		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");

Modified: trunk/postgis/lwgeom_geos.c
===================================================================
--- trunk/postgis/lwgeom_geos.c	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/postgis/lwgeom_geos.c	2018-03-05 16:54:13 UTC (rev 16444)
@@ -1112,13 +1112,6 @@
 	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 @@
 		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");

Modified: trunk/regress/offsetcurve.sql
===================================================================
--- trunk/regress/offsetcurve.sql	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/regress/offsetcurve.sql	2018-03-05 16:54:13 UTC (rev 16444)
@@ -51,3 +51,11 @@
  '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
+));

Modified: trunk/regress/offsetcurve_expected
===================================================================
--- trunk/regress/offsetcurve_expected	2018-03-05 14:14:49 UTC (rev 16443)
+++ trunk/regress/offsetcurve_expected	2018-03-05 16:54:13 UTC (rev 16444)
@@ -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 @@
 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



More information about the postgis-tickets mailing list