From: Paul Ramsey Date: Fri, 6 Jan 2012 21:39:50 +0000 (+0000) Subject: Move offset curve generation into liblwgeom with other geos functionality X-Git-Tag: 2.0.0alpha1~186 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c71f36af42b951da32dc78c802968a02a2222574;p=postgis Move offset curve generation into liblwgeom with other geos functionality git-svn-id: http://svn.osgeo.org/postgis/trunk@8689 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/liblwgeom/Makefile.in b/liblwgeom/Makefile.in index 7156f8a64..1e0f16ff8 100644 --- a/liblwgeom/Makefile.in +++ b/liblwgeom/Makefile.in @@ -65,6 +65,7 @@ SA_OBJS = \ lwhomogenize.o \ lwalgorithm.o \ lwsegmentize.o \ + lwlinearreferencing.o \ lwprint.o \ vsprintf.o \ g_box.o \ diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in index eaf9ba5ce..c59541fc3 100644 --- a/liblwgeom/liblwgeom.h.in +++ b/liblwgeom/liblwgeom.h.in @@ -1199,6 +1199,11 @@ extern double lwpoint_get_y(const LWPOINT *point); extern double lwpoint_get_z(const LWPOINT *point); extern double lwpoint_get_m(const LWPOINT *point); +/** +* Return SRID number +*/ +extern int32_t lwgeom_get_srid(const LWGEOM *geom); + /** * Return #LW_TRUE if geometry has Z ordinates */ @@ -1773,6 +1778,20 @@ LWGEOM* lwgeom_snap(const LWGEOM* geom1, const LWGEOM* geom2, double tolerance); */ LWGEOM* lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2); +/* + * An offset curve against the input line. + * + * @param lwline a lineal geometry + * @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 + * + * Requires GEOS-3.2.0+ + */ +LWLINE* lwgeom_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit); + /******************************************************************************* * PROJ4-dependent extra functions on LWGEOM diff --git a/liblwgeom/lwalgorithm.c b/liblwgeom/lwalgorithm.c index 1f74876a6..7ee4e563f 100644 --- a/liblwgeom/lwalgorithm.c +++ b/liblwgeom/lwalgorithm.c @@ -277,406 +277,7 @@ int lwline_crossing_direction(LWLINE *l1, LWLINE *l2) -/* -** lwpoint_get_ordinate(point, ordinate) => double -*/ -double lwpoint_get_ordinate(const POINT4D *p, int ordinate) -{ - if ( ! p ) - { - lwerror("Null input geometry."); - return 0.0; - } - - if ( ordinate > 3 || ordinate < 0 ) - { - lwerror("Cannot extract ordinate %d.", ordinate); - return 0.0; - } - - if ( ordinate == 3 ) - return p->m; - if ( ordinate == 2 ) - return p->z; - if ( ordinate == 1 ) - return p->y; - - return p->x; - -} -void lwpoint_set_ordinate(POINT4D *p, int ordinate, double value) -{ - if ( ! p ) - { - lwerror("Null input geometry."); - return; - } - - if ( ordinate > 3 || ordinate < 0 ) - { - lwerror("Cannot extract ordinate %d.", ordinate); - return; - } - - LWDEBUGF(4, " setting ordinate %d to %g", ordinate, value); - - switch ( ordinate ) - { - case 3: - p->m = value; - return; - case 2: - p->z = value; - return; - case 1: - p->y = value; - return; - case 0: - p->x = value; - return; - } -} - - -int lwpoint_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int ndims, int ordinate, double interpolation_value) -{ - double p1_value = lwpoint_get_ordinate(p1, ordinate); - double p2_value = lwpoint_get_ordinate(p2, ordinate); - double proportion; - int i = 0; - - if ( ordinate < 0 || ordinate >= ndims ) - { - lwerror("Ordinate (%d) is not within ndims (%d).", ordinate, ndims); - return 0; - } - - if ( FP_MIN(p1_value, p2_value) > interpolation_value || - FP_MAX(p1_value, p2_value) < interpolation_value ) - { - lwerror("Cannot interpolate to a value (%g) not between the input points (%g, %g).", interpolation_value, p1_value, p2_value); - return 0; - } - - proportion = fabs((interpolation_value - p1_value) / (p2_value - p1_value)); - - for ( i = 0; i < ndims; i++ ) - { - double newordinate = 0.0; - p1_value = lwpoint_get_ordinate(p1, i); - p2_value = lwpoint_get_ordinate(p2, i); - newordinate = p1_value + proportion * (p2_value - p1_value); - lwpoint_set_ordinate(p, i, newordinate); - LWDEBUGF(4, " clip ordinate(%d) p1_value(%g) p2_value(%g) proportion(%g) newordinate(%g) ", i, p1_value, p2_value, proportion, newordinate ); - } - - return 1; -} - -LWCOLLECTION *lwmline_clip_to_ordinate_range(LWMLINE *mline, int ordinate, double from, double to) -{ - LWCOLLECTION *lwgeom_out = NULL; - - if ( ! mline ) - { - lwerror("Null input geometry."); - return NULL; - } - - if ( mline->ngeoms == 1) - { - lwgeom_out = lwline_clip_to_ordinate_range(mline->geoms[0], ordinate, from, to); - } - else - { - LWCOLLECTION *col; - char hasz = FLAGS_GET_Z(mline->flags); - char hasm = FLAGS_GET_M(mline->flags); - int i, j; - char homogeneous = 1; - size_t geoms_size = 0; - lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, mline->srid, hasz, hasm); - FLAGS_SET_Z(lwgeom_out->flags, hasz); - FLAGS_SET_M(lwgeom_out->flags, hasm); - for ( i = 0; i < mline->ngeoms; i ++ ) - { - col = lwline_clip_to_ordinate_range(mline->geoms[i], ordinate, from, to); - if ( col ) - { - /* Something was left after the clip. */ - if ( lwgeom_out->ngeoms + col->ngeoms > geoms_size ) - { - geoms_size += 16; - if ( lwgeom_out->geoms ) - { - lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, geoms_size * sizeof(LWGEOM*)); - } - else - { - lwgeom_out->geoms = lwalloc(geoms_size * sizeof(LWGEOM*)); - } - } - for ( j = 0; j < col->ngeoms; j++ ) - { - lwgeom_out->geoms[lwgeom_out->ngeoms] = col->geoms[j]; - lwgeom_out->ngeoms++; - } - if ( col->type != mline->type ) - { - homogeneous = 0; - } - /* Shallow free the struct, leaving the geoms behind. */ - if ( col->bbox ) lwfree(col->bbox); - lwfree(col->geoms); - lwfree(col); - } - } - lwgeom_drop_bbox((LWGEOM*)lwgeom_out); - lwgeom_add_bbox((LWGEOM*)lwgeom_out); - if ( ! homogeneous ) - { - lwgeom_out->type = COLLECTIONTYPE; - } - } - - if ( ! lwgeom_out || lwgeom_out->ngeoms == 0 ) /* Nothing left after clip. */ - { - return NULL; - } - - return lwgeom_out; - -} - -/* -** lwline_clip_to_ordinate_range(line, ordinate, from, to) => lwmline -** -** Take in a LINESTRING and return a MULTILINESTRING of those portions of the -** LINESTRING between the from/to range for the specified ordinate (XYZM) -*/ -LWCOLLECTION *lwline_clip_to_ordinate_range(LWLINE *line, int ordinate, double from, double to) -{ - - POINTARRAY *pa_in = NULL; - LWCOLLECTION *lwgeom_out = NULL; - POINTARRAY *dp = NULL; - int i, rv; - int added_last_point = 0; - POINT4D *p = NULL, *q = NULL, *r = NULL; - double ordinate_value_p = 0.0, ordinate_value_q = 0.0; - char hasz = FLAGS_GET_Z(line->flags); - char hasm = FLAGS_GET_M(line->flags); - char dims = FLAGS_NDIMS(line->flags); - - /* Null input, nothing we can do. */ - if ( ! line ) - { - lwerror("Null input geometry."); - return NULL; - } - - /* Ensure 'from' is less than 'to'. */ - if ( to < from ) - { - double t = from; - from = to; - to = t; - } - - LWDEBUGF(4, "from = %g, to = %g, ordinate = %d", from, to, ordinate); - LWDEBUGF(4, "%s", lwgeom_to_ewkt((LWGEOM*)line)); - - /* Asking for an ordinate we don't have. Error. */ - if ( ordinate >= dims ) - { - lwerror("Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims); - return NULL; - } - - /* Prepare our working point objects. */ - p = lwalloc(sizeof(POINT4D)); - q = lwalloc(sizeof(POINT4D)); - r = lwalloc(sizeof(POINT4D)); - - /* Construct a collection to hold our outputs. */ - lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, line->srid, hasz, hasm); - - /* Get our input point array */ - pa_in = line->points; - - for ( i = 0; i < pa_in->npoints; i++ ) - { - LWDEBUGF(4, "Point #%d", i); - LWDEBUGF(4, "added_last_point %d", added_last_point); - if ( i > 0 ) - { - *q = *p; - ordinate_value_q = ordinate_value_p; - } - rv = getPoint4d_p(pa_in, i, p); - ordinate_value_p = lwpoint_get_ordinate(p, ordinate); - LWDEBUGF(4, " ordinate_value_p %g (current)", ordinate_value_p); - LWDEBUGF(4, " ordinate_value_q %g (previous)", ordinate_value_q); - - /* Is this point inside the ordinate range? Yes. */ - if ( ordinate_value_p >= from && ordinate_value_p <= to ) - { - LWDEBUGF(4, " inside ordinate range (%g, %g)", from, to); - - if ( ! added_last_point ) - { - LWDEBUG(4," new ptarray required"); - /* We didn't add the previous point, so this is a new segment. - * Make a new point array. */ - dp = ptarray_construct_empty(hasz, hasm, 32); - - /* We're transiting into the range so add an interpolated - * point at the range boundary. - * If we're on a boundary and crossing from the far side, - * we also need an interpolated point. */ - if ( i > 0 && ( /* Don't try to interpolate if this is the first point */ - ( ordinate_value_p > from && ordinate_value_p < to ) || /* Inside */ - ( ordinate_value_p == from && ordinate_value_q > to ) || /* Hopping from above */ - ( ordinate_value_p == to && ordinate_value_q < from ) ) ) /* Hopping from below */ - { - double interpolation_value; - (ordinate_value_q > to) ? (interpolation_value = to) : (interpolation_value = from); - rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value); - rv = ptarray_append_point(dp, r, LW_FALSE); - LWDEBUGF(4, "[0] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); - } - } - /* Add the current vertex to the point array. */ - rv = ptarray_append_point(dp, p, LW_FALSE); - if ( ordinate_value_p == from || ordinate_value_p == to ) - { - added_last_point = 2; /* Added on boundary. */ - } - else - { - added_last_point = 1; /* Added inside range. */ - } - } - /* Is this point inside the ordinate range? No. */ - else - { - LWDEBUGF(4, " added_last_point (%d)", added_last_point); - if ( added_last_point == 1 ) - { - /* We're transiting out of the range, so add an interpolated point - * to the point array at the range boundary. */ - double interpolation_value; - (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); - rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value); - rv = ptarray_append_point(dp, r, LW_FALSE); - LWDEBUGF(4, " [1] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); - } - else if ( added_last_point == 2 ) - { - /* We're out and the last point was on the boundary. - * If the last point was the near boundary, nothing to do. - * If it was the far boundary, we need an interpolated point. */ - if ( from != to && ( - (ordinate_value_q == from && ordinate_value_p > from) || - (ordinate_value_q == to && ordinate_value_p < to) ) ) - { - double interpolation_value; - (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); - rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value); - rv = ptarray_append_point(dp, r, LW_FALSE); - LWDEBUGF(4, " [2] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); - } - } - else if ( i && ordinate_value_q < from && ordinate_value_p > to ) - { - /* We just hopped over the whole range, from bottom to top, - * so we need to add *two* interpolated points! */ - dp = ptarray_construct(hasz, hasm, 2); - /* Interpolate lower point. */ - rv = lwpoint_interpolate(p, q, r, dims, ordinate, from); - ptarray_set_point4d(dp, 0, r); - /* Interpolate upper point. */ - rv = lwpoint_interpolate(p, q, r, dims, ordinate, to); - ptarray_set_point4d(dp, 1, r); - } - else if ( i && ordinate_value_q > to && ordinate_value_p < from ) - { - /* We just hopped over the whole range, from top to bottom, - * so we need to add *two* interpolated points! */ - dp = ptarray_construct(hasz, hasm, 2); - /* Interpolate upper point. */ - rv = lwpoint_interpolate(p, q, r, dims, ordinate, to); - ptarray_set_point4d(dp, 0, r); - /* Interpolate lower point. */ - rv = lwpoint_interpolate(p, q, r, dims, ordinate, from); - ptarray_set_point4d(dp, 1, r); - } - /* We have an extant point-array, save it out to a multi-line. */ - if ( dp ) - { - LWDEBUG(4, "saving pointarray to multi-line (1)"); - - /* Only one point, so we have to make an lwpoint to hold this - * and set the overall output type to a generic collection. */ - if ( dp->npoints == 1 ) - { - LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp); - lwgeom_out->type = COLLECTIONTYPE; - lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint)); - - } - else - { - LWLINE *oline = lwline_construct(line->srid, NULL, dp); - lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline)); - } - - /* Pointarray is now owned by lwgeom_out, so drop reference to it */ - dp = NULL; - } - added_last_point = 0; - - } - } - - /* Still some points left to be saved out. */ - if ( dp && dp->npoints > 0 ) - { - LWDEBUG(4, "saving pointarray to multi-line (2)"); - LWDEBUGF(4, "dp->npoints == %d", dp->npoints); - LWDEBUGF(4, "lwgeom_out->ngeoms == %d", lwgeom_out->ngeoms); - - if ( dp->npoints == 1 ) - { - LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp); - lwgeom_out->type = COLLECTIONTYPE; - lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint)); - } - else - { - LWLINE *oline = lwline_construct(line->srid, NULL, dp); - lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline)); - } - - /* Pointarray is now owned by lwgeom_out, so drop reference to it */ - dp = NULL; - } - - lwfree(p); - lwfree(q); - lwfree(r); - - if ( lwgeom_out->ngeoms > 0 ) - { - lwgeom_drop_bbox((LWGEOM*)lwgeom_out); - lwgeom_add_bbox((LWGEOM*)lwgeom_out); - return lwgeom_out; - } - - return NULL; - -} static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz"; diff --git a/liblwgeom/lwgeom.c b/liblwgeom/lwgeom.c index da3904552..5b796f907 100644 --- a/liblwgeom/lwgeom.c +++ b/liblwgeom/lwgeom.c @@ -655,6 +655,13 @@ lwgeom_force_dims(const LWGEOM *geom, int hasz, int hasm) } } +int32_t +lwgeom_get_srid(const LWGEOM *geom) +{ + if ( ! geom ) return SRID_UNKNOWN; + return geom->srid; +} + int lwgeom_has_z(const LWGEOM *geom) { diff --git a/liblwgeom/lwgeom_geos.c b/liblwgeom/lwgeom_geos.c index c8a901a87..7960d5216 100644 --- a/liblwgeom/lwgeom_geos.c +++ b/liblwgeom/lwgeom_geos.c @@ -406,8 +406,6 @@ lwgeom_geos_version() return ver; } - - LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2) { @@ -1023,28 +1021,79 @@ lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2) g3 = GEOSSharedPaths(g1,g2); + GEOSGeom_destroy(g1); + GEOSGeom_destroy(g2); + if (g3 == NULL) { - GEOSGeom_destroy(g1); - GEOSGeom_destroy(g2); lwerror("GEOSSharedPaths: %s", lwgeom_geos_errmsg); return NULL; } - GEOSGeom_destroy(g1); - GEOSGeom_destroy(g2); - GEOSSetSRID(g3, srid); out = GEOS2LWGEOM(g3, is3d); + GEOSGeom_destroy(g3); if (out == NULL) { - GEOSGeom_destroy(g3); lwerror("GEOS2LWGEOM threw an error"); return NULL; } - GEOSGeom_destroy(g3); return out; #endif /* POSTGIS_GEOS_VERSION >= 33 */ } + +LWLINE* +lwgeom_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit) +{ +#if POSTGIS_GEOS_VERSION < 32 + lwerror("lwgeom_offsetcurve: GEOS 3.2 or higher required"); +#else + GEOSGeometry *g1, *g3; + LWGEOM *lwgeom_result; + + initGEOS(lwnotice, lwgeom_geos_error); + + g1 = (GEOSGeometry *)LWGEOM2GEOS(lwline); + if ( ! g1 ) + { + lwerror("lwgeom_offsetcurve: Geometry could not be converted to GEOS: %s", lwgeom_geos_errmsg); + return NULL; + } + +#if POSTGIS_GEOS_VERSION < 33 + /* Size is always positive for GEOSSingleSidedBuffer, and a flag determines left/right */ + g3 = GEOSSingleSidedBuffer(g1, size < 0 ? -size : size, + quadsegs, joinStyle, mitreLimit, + size < 0 ? 0 : 1); +#else + g3 = GEOSOffsetCurve(g1, size, quadsegs, joinStyle, mitreLimit); +#endif + /* Don't need input geometry anymore */ + GEOSGeom_destroy(g1); + + if (g3 == NULL) + { + GEOSGeom_destroy(g1); + lwerror("GEOSOffsetCurve: %s", lwgeom_geos_errmsg); + return NULL; + } + + LWDEBUGF(3, "result: %s", GEOSGeomToWKT(g3)); + + GEOSSetSRID(g3, lwgeom_get_srid(lwline)); + + lwgeom_result = GEOS2LWGEOM(g3, lwgeom_has_z(lwline)); + GEOSGeom_destroy(g3); + + if (lwgeom_result == NULL) + { + lwerror("lwgeom_offsetcurve: GEOS2LWGEOM returned null"); + return NULL; + } + + return lwgeom_result; + +#endif /* POSTGIS_GEOS_VERSION < 32 */ +} \ No newline at end of file diff --git a/liblwgeom/lwlinearreferencing.c b/liblwgeom/lwlinearreferencing.c new file mode 100644 index 000000000..d7c9762c8 --- /dev/null +++ b/liblwgeom/lwlinearreferencing.c @@ -0,0 +1,430 @@ +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2011 Paul Ramsey + * + * 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 "liblwgeom_internal.h" +#include "lwgeom_log.h" + + +/** +* Given a POINT4D and an ordinate number, return +* the value of the ordinate. +* @param p input point +* @param ordinate number (1=x, 2=y, 3=z, 4=m) +* @return d value at that ordinate +*/ +double lwpoint_get_ordinate(const POINT4D *p, int ordinate) +{ + if ( ! p ) + { + lwerror("Null input geometry."); + return 0.0; + } + + if ( ordinate > 3 || ordinate < 0 ) + { + lwerror("Cannot extract ordinate %d.", ordinate); + return 0.0; + } + + if ( ordinate == 3 ) + return p->m; + if ( ordinate == 2 ) + return p->z; + if ( ordinate == 1 ) + return p->y; + + return p->x; + +} + +/** +* Given a point, ordinate number and value, set that ordinate on the +* point. +*/ +void lwpoint_set_ordinate(POINT4D *p, int ordinate, double value) +{ + if ( ! p ) + { + lwerror("Null input geometry."); + return; + } + + if ( ordinate > 3 || ordinate < 0 ) + { + lwerror("Cannot extract ordinate %d.", ordinate); + return; + } + + LWDEBUGF(4, " setting ordinate %d to %g", ordinate, value); + + switch ( ordinate ) + { + case 3: + p->m = value; + return; + case 2: + p->z = value; + return; + case 1: + p->y = value; + return; + case 0: + p->x = value; + return; + } +} + +/** +* Given two points, a dimensionality, an ordinate, and an interpolation value +* generate a new point that is proportionally between the input points, +* using the values in the provided dimension as the scaling factors. +*/ +int lwpoint_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int ndims, int ordinate, double interpolation_value) +{ + double p1_value = lwpoint_get_ordinate(p1, ordinate); + double p2_value = lwpoint_get_ordinate(p2, ordinate); + double proportion; + int i = 0; + + if ( ordinate < 0 || ordinate >= ndims ) + { + lwerror("Ordinate (%d) is not within ndims (%d).", ordinate, ndims); + return 0; + } + + if ( FP_MIN(p1_value, p2_value) > interpolation_value || + FP_MAX(p1_value, p2_value) < interpolation_value ) + { + lwerror("Cannot interpolate to a value (%g) not between the input points (%g, %g).", interpolation_value, p1_value, p2_value); + return 0; + } + + proportion = fabs((interpolation_value - p1_value) / (p2_value - p1_value)); + + for ( i = 0; i < ndims; i++ ) + { + double newordinate = 0.0; + p1_value = lwpoint_get_ordinate(p1, i); + p2_value = lwpoint_get_ordinate(p2, i); + newordinate = p1_value + proportion * (p2_value - p1_value); + lwpoint_set_ordinate(p, i, newordinate); + LWDEBUGF(4, " clip ordinate(%d) p1_value(%g) p2_value(%g) proportion(%g) newordinate(%g) ", i, p1_value, p2_value, proportion, newordinate ); + } + + return 1; +} + +/** +* Clip an input MULTILINESTRING between two values, on any ordinate input. +*/ +LWCOLLECTION *lwmline_clip_to_ordinate_range(LWMLINE *mline, int ordinate, double from, double to) +{ + LWCOLLECTION *lwgeom_out = NULL; + + if ( ! mline ) + { + lwerror("Null input geometry."); + return NULL; + } + + if ( mline->ngeoms == 1) + { + lwgeom_out = lwline_clip_to_ordinate_range(mline->geoms[0], ordinate, from, to); + } + else + { + LWCOLLECTION *col; + char hasz = FLAGS_GET_Z(mline->flags); + char hasm = FLAGS_GET_M(mline->flags); + int i, j; + char homogeneous = 1; + size_t geoms_size = 0; + lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, mline->srid, hasz, hasm); + FLAGS_SET_Z(lwgeom_out->flags, hasz); + FLAGS_SET_M(lwgeom_out->flags, hasm); + for ( i = 0; i < mline->ngeoms; i ++ ) + { + col = lwline_clip_to_ordinate_range(mline->geoms[i], ordinate, from, to); + if ( col ) + { + /* Something was left after the clip. */ + if ( lwgeom_out->ngeoms + col->ngeoms > geoms_size ) + { + geoms_size += 16; + if ( lwgeom_out->geoms ) + { + lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, geoms_size * sizeof(LWGEOM*)); + } + else + { + lwgeom_out->geoms = lwalloc(geoms_size * sizeof(LWGEOM*)); + } + } + for ( j = 0; j < col->ngeoms; j++ ) + { + lwgeom_out->geoms[lwgeom_out->ngeoms] = col->geoms[j]; + lwgeom_out->ngeoms++; + } + if ( col->type != mline->type ) + { + homogeneous = 0; + } + /* Shallow free the struct, leaving the geoms behind. */ + if ( col->bbox ) lwfree(col->bbox); + lwfree(col->geoms); + lwfree(col); + } + } + lwgeom_drop_bbox((LWGEOM*)lwgeom_out); + lwgeom_add_bbox((LWGEOM*)lwgeom_out); + if ( ! homogeneous ) + { + lwgeom_out->type = COLLECTIONTYPE; + } + } + + if ( ! lwgeom_out || lwgeom_out->ngeoms == 0 ) /* Nothing left after clip. */ + { + return NULL; + } + + return lwgeom_out; + +} + + +/** +* Take in a LINESTRING and return a MULTILINESTRING of those portions of the +* LINESTRING between the from/to range for the specified ordinate (XYZM) +*/ +LWCOLLECTION *lwline_clip_to_ordinate_range(LWLINE *line, int ordinate, double from, double to) +{ + + POINTARRAY *pa_in = NULL; + LWCOLLECTION *lwgeom_out = NULL; + POINTARRAY *dp = NULL; + int i, rv; + int added_last_point = 0; + POINT4D *p = NULL, *q = NULL, *r = NULL; + double ordinate_value_p = 0.0, ordinate_value_q = 0.0; + char hasz = FLAGS_GET_Z(line->flags); + char hasm = FLAGS_GET_M(line->flags); + char dims = FLAGS_NDIMS(line->flags); + + /* Null input, nothing we can do. */ + if ( ! line ) + { + lwerror("Null input geometry."); + return NULL; + } + + /* Ensure 'from' is less than 'to'. */ + if ( to < from ) + { + double t = from; + from = to; + to = t; + } + + LWDEBUGF(4, "from = %g, to = %g, ordinate = %d", from, to, ordinate); + LWDEBUGF(4, "%s", lwgeom_to_ewkt((LWGEOM*)line)); + + /* Asking for an ordinate we don't have. Error. */ + if ( ordinate >= dims ) + { + lwerror("Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims); + return NULL; + } + + /* Prepare our working point objects. */ + p = lwalloc(sizeof(POINT4D)); + q = lwalloc(sizeof(POINT4D)); + r = lwalloc(sizeof(POINT4D)); + + /* Construct a collection to hold our outputs. */ + lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, line->srid, hasz, hasm); + + /* Get our input point array */ + pa_in = line->points; + + for ( i = 0; i < pa_in->npoints; i++ ) + { + LWDEBUGF(4, "Point #%d", i); + LWDEBUGF(4, "added_last_point %d", added_last_point); + if ( i > 0 ) + { + *q = *p; + ordinate_value_q = ordinate_value_p; + } + rv = getPoint4d_p(pa_in, i, p); + ordinate_value_p = lwpoint_get_ordinate(p, ordinate); + LWDEBUGF(4, " ordinate_value_p %g (current)", ordinate_value_p); + LWDEBUGF(4, " ordinate_value_q %g (previous)", ordinate_value_q); + + /* Is this point inside the ordinate range? Yes. */ + if ( ordinate_value_p >= from && ordinate_value_p <= to ) + { + LWDEBUGF(4, " inside ordinate range (%g, %g)", from, to); + + if ( ! added_last_point ) + { + LWDEBUG(4," new ptarray required"); + /* We didn't add the previous point, so this is a new segment. + * Make a new point array. */ + dp = ptarray_construct_empty(hasz, hasm, 32); + + /* We're transiting into the range so add an interpolated + * point at the range boundary. + * If we're on a boundary and crossing from the far side, + * we also need an interpolated point. */ + if ( i > 0 && ( /* Don't try to interpolate if this is the first point */ + ( ordinate_value_p > from && ordinate_value_p < to ) || /* Inside */ + ( ordinate_value_p == from && ordinate_value_q > to ) || /* Hopping from above */ + ( ordinate_value_p == to && ordinate_value_q < from ) ) ) /* Hopping from below */ + { + double interpolation_value; + (ordinate_value_q > to) ? (interpolation_value = to) : (interpolation_value = from); + rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value); + rv = ptarray_append_point(dp, r, LW_FALSE); + LWDEBUGF(4, "[0] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); + } + } + /* Add the current vertex to the point array. */ + rv = ptarray_append_point(dp, p, LW_FALSE); + if ( ordinate_value_p == from || ordinate_value_p == to ) + { + added_last_point = 2; /* Added on boundary. */ + } + else + { + added_last_point = 1; /* Added inside range. */ + } + } + /* Is this point inside the ordinate range? No. */ + else + { + LWDEBUGF(4, " added_last_point (%d)", added_last_point); + if ( added_last_point == 1 ) + { + /* We're transiting out of the range, so add an interpolated point + * to the point array at the range boundary. */ + double interpolation_value; + (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); + rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value); + rv = ptarray_append_point(dp, r, LW_FALSE); + LWDEBUGF(4, " [1] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); + } + else if ( added_last_point == 2 ) + { + /* We're out and the last point was on the boundary. + * If the last point was the near boundary, nothing to do. + * If it was the far boundary, we need an interpolated point. */ + if ( from != to && ( + (ordinate_value_q == from && ordinate_value_p > from) || + (ordinate_value_q == to && ordinate_value_p < to) ) ) + { + double interpolation_value; + (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); + rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value); + rv = ptarray_append_point(dp, r, LW_FALSE); + LWDEBUGF(4, " [2] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); + } + } + else if ( i && ordinate_value_q < from && ordinate_value_p > to ) + { + /* We just hopped over the whole range, from bottom to top, + * so we need to add *two* interpolated points! */ + dp = ptarray_construct(hasz, hasm, 2); + /* Interpolate lower point. */ + rv = lwpoint_interpolate(p, q, r, dims, ordinate, from); + ptarray_set_point4d(dp, 0, r); + /* Interpolate upper point. */ + rv = lwpoint_interpolate(p, q, r, dims, ordinate, to); + ptarray_set_point4d(dp, 1, r); + } + else if ( i && ordinate_value_q > to && ordinate_value_p < from ) + { + /* We just hopped over the whole range, from top to bottom, + * so we need to add *two* interpolated points! */ + dp = ptarray_construct(hasz, hasm, 2); + /* Interpolate upper point. */ + rv = lwpoint_interpolate(p, q, r, dims, ordinate, to); + ptarray_set_point4d(dp, 0, r); + /* Interpolate lower point. */ + rv = lwpoint_interpolate(p, q, r, dims, ordinate, from); + ptarray_set_point4d(dp, 1, r); + } + /* We have an extant point-array, save it out to a multi-line. */ + if ( dp ) + { + LWDEBUG(4, "saving pointarray to multi-line (1)"); + + /* Only one point, so we have to make an lwpoint to hold this + * and set the overall output type to a generic collection. */ + if ( dp->npoints == 1 ) + { + LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp); + lwgeom_out->type = COLLECTIONTYPE; + lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint)); + + } + else + { + LWLINE *oline = lwline_construct(line->srid, NULL, dp); + lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline)); + } + + /* Pointarray is now owned by lwgeom_out, so drop reference to it */ + dp = NULL; + } + added_last_point = 0; + + } + } + + /* Still some points left to be saved out. */ + if ( dp && dp->npoints > 0 ) + { + LWDEBUG(4, "saving pointarray to multi-line (2)"); + LWDEBUGF(4, "dp->npoints == %d", dp->npoints); + LWDEBUGF(4, "lwgeom_out->ngeoms == %d", lwgeom_out->ngeoms); + + if ( dp->npoints == 1 ) + { + LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp); + lwgeom_out->type = COLLECTIONTYPE; + lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint)); + } + else + { + LWLINE *oline = lwline_construct(line->srid, NULL, dp); + lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline)); + } + + /* Pointarray is now owned by lwgeom_out, so drop reference to it */ + dp = NULL; + } + + lwfree(p); + lwfree(q); + lwfree(r); + + if ( lwgeom_out->ngeoms > 0 ) + { + lwgeom_drop_bbox((LWGEOM*)lwgeom_out); + lwgeom_add_bbox((LWGEOM*)lwgeom_out); + return lwgeom_out; + } + + return NULL; + +} \ No newline at end of file diff --git a/postgis/lwgeom_geos.c b/postgis/lwgeom_geos.c index aeffa5b67..621cd3e98 100644 --- a/postgis/lwgeom_geos.c +++ b/postgis/lwgeom_geos.c @@ -54,7 +54,6 @@ Datum isvalid(PG_FUNCTION_ARGS); Datum isvalidreason(PG_FUNCTION_ARGS); Datum isvaliddetail(PG_FUNCTION_ARGS); Datum buffer(PG_FUNCTION_ARGS); -Datum offsetcurve(PG_FUNCTION_ARGS); Datum intersection(PG_FUNCTION_ARGS); Datum convexhull(PG_FUNCTION_ARGS); Datum topologypreservesimplify(PG_FUNCTION_ARGS); @@ -107,7 +106,7 @@ PG_FUNCTION_INFO_V1(hausdorffdistance); Datum hausdorffdistance(PG_FUNCTION_ARGS) { #if POSTGIS_GEOS_VERSION < 32 - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "'ST_HausdorffDistance' function (3.2.0+ required)", POSTGIS_GEOS_VERSION); @@ -173,7 +172,7 @@ PG_FUNCTION_INFO_V1(hausdorffdistancedensify); Datum hausdorffdistancedensify(PG_FUNCTION_ARGS) { #if POSTGIS_GEOS_VERSION < 32 - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "'ST_HausdorffDistance' function (3.2.0+ required)", POSTGIS_GEOS_VERSION); @@ -566,7 +565,7 @@ Datum ST_UnaryUnion(PG_FUNCTION_ARGS) { #if POSTGIS_GEOS_VERSION < 33 PG_RETURN_NULL(); - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "'GEOSUnaryUnion' function (3.3.0+ required)", POSTGIS_GEOS_VERSION); @@ -1070,7 +1069,7 @@ Datum buffer(PG_FUNCTION_ARGS) endCapStyle != DEFAULT_ENDCAP_STYLE || joinStyle != DEFAULT_JOIN_STYLE ) { - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "specifying a mitre limit != %d or styles different " "from 'round' (needs 3.2 or higher)", @@ -1109,14 +1108,27 @@ Datum buffer(PG_FUNCTION_ARGS) PG_RETURN_POINTER(result); } -PG_FUNCTION_INFO_V1(offsetcurve); -Datum offsetcurve(PG_FUNCTION_ARGS) +/* +* Compute at offset curve to a line +*/ +Datum ST_OffsetCurve(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(ST_OffsetCurve); +Datum ST_OffsetCurve(PG_FUNCTION_ARGS) { -#if POSTGIS_GEOS_VERSION >= 32 - GSERIALIZED *geom1; - double size; - GEOSGeometry *g1, *g3; - GSERIALIZED *result; +#if POSTGIS_GEOS_VERSION < 32 + lwerror("The GEOS version this PostGIS binary " + "was compiled against (%d) doesn't support " + "ST_OffsetCurve function " + "(needs 3.2 or higher)", + POSTGIS_GEOS_VERSION); + PG_RETURN_NULL(); /* never get here */ +#else + + GSERIALIZED *gser_input; + GSERIALIZED *gser_result; + LWGEOM *lwgeom_input; + LWGEOM *lwgeom_result; + double size; int quadsegs = 8; /* the default */ int nargs; @@ -1126,54 +1138,52 @@ Datum offsetcurve(PG_FUNCTION_ARGS) JOIN_MITRE = 2, JOIN_BEVEL = 3 }; + static const double DEFAULT_MITRE_LIMIT = 5.0; static const int DEFAULT_JOIN_STYLE = JOIN_ROUND; - double mitreLimit = DEFAULT_MITRE_LIMIT; int joinStyle = DEFAULT_JOIN_STYLE; - char *param; - char *params = NULL; - - - geom1 = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + char *param = NULL; + char *paramstr = NULL; + + /* Read SQL arguments */ + nargs = PG_NARGS(); + gser_input = (GSERIALIZED*) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); size = PG_GETARG_FLOAT8(1); - /* - * For distance = 0 we just return the input. - * Note that due to a bug, GEOS 3.3.0 would return EMPTY. - * See http://trac.osgeo.org/geos/ticket/454 - */ - if ( size == 0 ) { - PG_RETURN_POINTER(geom1); + /* Check for a useable type */ + if ( gserialized_get_type(gser_input) != LINETYPE ) + { + lwerror("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. + * See http://trac.osgeo.org/geos/ticket/454 + */ + if ( size == 0 ) + PG_RETURN_POINTER(gser_input); + /* Read the lwgeom, check for errors */ + lwgeom_input = lwgeom_from_gserialized(gser_input); + if ( ! lwgeom_input ) + lwerror("ST_OffsetCurve: lwgeom_from_gserialized returned NULL"); + + /* For empty inputs, just echo them back */ + if ( lwgeom_is_empty(lwgeom_input) ) + PG_RETURN_POINTER(gser_input); - nargs = PG_NARGS(); - - initGEOS(lwnotice, lwnotice); - initGEOS(lwnotice, lwgeom_geos_error); - - g1 = (GEOSGeometry *)POSTGIS2GEOS(geom1); - if ( ! g1 ) { - lwerror("Geometry could not be converted to GEOS: %s", - lwgeom_geos_errmsg); - PG_RETURN_NULL(); - } - - // options arg (optional) - if (nargs > 2) + /* Process the optional arguments */ + if ( nargs > 2 ) { - /* We strdup `cause we're going to modify it */ - { - text *wkttext = PG_GETARG_TEXT_P(2); - params = text2cstring(wkttext); - } - /*params = pstrdup(PG_GETARG_CSTRING(2)); */ + text *wkttext = PG_GETARG_TEXT_P(2); + paramstr = text2cstring(wkttext); - POSTGIS_DEBUGF(3, "Params: %s", params); + POSTGIS_DEBUGF(3, "paramstr: %s", paramstr); - for (param=params; ; param=NULL) + for ( param=paramstr; ; param=NULL ) { char *key, *val; param = strtok(param, " "); @@ -1184,8 +1194,7 @@ Datum offsetcurve(PG_FUNCTION_ARGS) val = strchr(key, '='); if ( val == NULL || *(val+1) == '\0' ) { - lwerror("Missing value for buffer " - "parameter %s", key); + lwerror("ST_OffsetCurve: Missing value for buffer parameter %s", key); break; } *val = '\0'; @@ -1199,22 +1208,18 @@ Datum offsetcurve(PG_FUNCTION_ARGS) { joinStyle = JOIN_ROUND; } - else if ( !strcmp(val, "mitre") || - !strcmp(val, "miter") ) + else if ( !(strcmp(val, "mitre") && strcmp(val, "miter")) ) { joinStyle = JOIN_MITRE; } - else if ( !strcmp(val, "bevel") ) + else if ( ! strcmp(val, "bevel") ) { joinStyle = JOIN_BEVEL; } else { - lwerror("Invalid buffer end cap " - "style: %s (accept: " - "'round', 'mitre', 'miter' " - " or 'bevel'" - ")", val); + lwerror("Invalid buffer end cap style: %s (accept: " + "'round', 'mitre', 'miter' or 'bevel')", val); break; } } @@ -1232,64 +1237,25 @@ Datum offsetcurve(PG_FUNCTION_ARGS) else { lwerror("Invalid buffer parameter: %s (accept: " - "'join', 'mitre_limit', " - "'miter_limit and " + "'join', 'mitre_limit', 'miter_limit and " "'quad_segs')", key); break; } } - - pfree(params); /* was pstrduped */ - - POSTGIS_DEBUGF(3, "joinStyle:%d mitreLimit:%g", - joinStyle, mitreLimit); - + POSTGIS_DEBUGF(3, "joinStyle:%d mitreLimit:%g", joinStyle, mitreLimit); + pfree(paramstr); /* alloc'ed in text2cstring */ } + + lwgeom_result = lwline_as_lwgeom(lwgeom_offsetcurve(lwgeom_as_lwline(lwgeom_input), size, quadsegs, joinStyle, mitreLimit)); + + if (lwgeom_result == NULL) + lwerror("ST_OffsetCurve: lwgeom_offsetcurve returned NULL"); -#if POSTGIS_GEOS_VERSION < 33 - g3 = GEOSSingleSidedBuffer(g1, size < 0 ? -size : size, - quadsegs, joinStyle, mitreLimit, - size < 0 ? 0 : 1); -#else - g3 = GEOSOffsetCurve(g1, size, quadsegs, joinStyle, mitreLimit); -#endif - - if (g3 == NULL) - { - lwerror("GEOSOffsetCurve: %s", lwgeom_geos_errmsg); - GEOSGeom_destroy(g1); - PG_RETURN_NULL(); /* never get here */ - } - - POSTGIS_DEBUGF(3, "result: %s", GEOSGeomToWKT(g3)); - - GEOSSetSRID(g3, gserialized_get_srid(geom1)); - - result = GEOS2POSTGIS(g3, gserialized_has_z(geom1)); - - if (result == NULL) - { - GEOSGeom_destroy(g1); - GEOSGeom_destroy(g3); - lwerror("ST_OffsetCurve() threw an error (result postgis geometry formation)!"); - PG_RETURN_NULL(); /* never get here */ - } - GEOSGeom_destroy(g1); - GEOSGeom_destroy(g3); - - - /* compressType(result); */ - - PG_FREE_IF_COPY(geom1, 0); + gser_result = gserialized_from_lwgeom(lwgeom_result, 0, 0); + lwgeom_free(lwgeom_input); + lwgeom_free(lwgeom_result); + PG_RETURN_POINTER(gser_result); - PG_RETURN_POINTER(result); -#else /* POSTGIS_GEOS_VERSION < 32 */ - lwerror("The GEOS version this postgis binary " - "was compiled against (%d) doesn't support " - "ST_OffsetCurve function " - "(needs 3.2 or higher)", - POSTGIS_GEOS_VERSION); - PG_RETURN_NULL(); /* never get here */ #endif /* POSTGIS_GEOS_VERSION < 32 */ } @@ -1648,7 +1614,7 @@ PG_FUNCTION_INFO_V1(isvaliddetail); Datum isvaliddetail(PG_FUNCTION_ARGS) { #if POSTGIS_GEOS_VERSION < 33 - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "'isValidDetail' function (3.3.0+ required)", POSTGIS_GEOS_VERSION); @@ -2956,7 +2922,7 @@ Datum relate_full(PG_FUNCTION_ARGS) #if POSTGIS_GEOS_VERSION >= 33 bnr = PG_GETARG_INT32(2); #else - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "specifying a boundary node rule with ST_Relate" " (3.3.0+ required)", @@ -3428,7 +3394,7 @@ PG_FUNCTION_INFO_V1(ST_Snap); Datum ST_Snap(PG_FUNCTION_ARGS) { #if POSTGIS_GEOS_VERSION < 33 - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "'ST_Snap' function (3.3.0+ required)", POSTGIS_GEOS_VERSION); @@ -3537,7 +3503,7 @@ PG_FUNCTION_INFO_V1(ST_SharedPaths); Datum ST_SharedPaths(PG_FUNCTION_ARGS) { #if POSTGIS_GEOS_VERSION < 33 - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "'ST_SharedPaths' function (3.3.0+ required)", POSTGIS_GEOS_VERSION); @@ -3584,7 +3550,7 @@ PG_FUNCTION_INFO_V1(ST_Node); Datum ST_Node(PG_FUNCTION_ARGS) { #if POSTGIS_GEOS_VERSION < 33 - lwerror("The GEOS version this postgis binary " + lwerror("The GEOS version this PostGIS binary " "was compiled against (%d) doesn't support " "'ST_Node' function (3.3.0+ required)", POSTGIS_GEOS_VERSION); diff --git a/postgis/lwgeom_in_geojson.c b/postgis/lwgeom_in_geojson.c index 42ee636d5..1c20ba416 100644 --- a/postgis/lwgeom_in_geojson.c +++ b/postgis/lwgeom_in_geojson.c @@ -476,8 +476,8 @@ Datum geom_from_geojson(PG_FUNCTION_ARGS) poObj = json_tokener_parse_ex(jstok, geojson, -1); if( jstok->err != json_tokener_success) { - char *err; - err += sprintf(err, "%s (at offset %d)", json_tokener_errors[jstok->err], jstok->char_offset); + char err[256]; + snprintf(err, 256, "%s (at offset %d)", json_tokener_errors[jstok->err], jstok->char_offset); json_tokener_free(jstok); geojson_lwerror(err, 1); } diff --git a/postgis/postgis.sql.in.c b/postgis/postgis.sql.in.c index 835ed5517..1536cdf0d 100644 --- a/postgis/postgis.sql.in.c +++ b/postgis/postgis.sql.in.c @@ -2567,7 +2567,7 @@ CREATE OR REPLACE FUNCTION ST_Buffer(geometry,float8,text) -- Availability: 2.0.0 - requires GEOS-3.2 or higher CREATE OR REPLACE FUNCTION ST_OffsetCurve(line geometry, distance float8, params text DEFAULT '') RETURNS geometry - AS 'MODULE_PATHNAME','offsetcurve' + AS 'MODULE_PATHNAME','ST_OffsetCurve' LANGUAGE 'C' IMMUTABLE STRICT COST 100; diff --git a/regress/offsetcurve_expected b/regress/offsetcurve_expected index bf62391e9..f70e5db90 100644 --- a/regress/offsetcurve_expected +++ b/regress/offsetcurve_expected @@ -1,4 +1,4 @@ -ERROR: GEOSOffsetCurve: IllegalArgumentException: BufferBuilder::bufferLineSingleSided only accept linestrings +ERROR: ST_OffsetCurve only works with LineStrings t0|SRID=42;LINESTRING(0 0,10 0) t1|SRID=42;LINESTRING(0 10,10 10) t2|SRID=42;LINESTRING(10 -10,0 -10)