]> granicus.if.org Git - postgis/commitdiff
Move offset curve generation into liblwgeom with other geos functionality
authorPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 6 Jan 2012 21:39:50 +0000 (21:39 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 6 Jan 2012 21:39:50 +0000 (21:39 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@8689 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/Makefile.in
liblwgeom/liblwgeom.h.in
liblwgeom/lwalgorithm.c
liblwgeom/lwgeom.c
liblwgeom/lwgeom_geos.c
liblwgeom/lwlinearreferencing.c [new file with mode: 0644]
postgis/lwgeom_geos.c
postgis/lwgeom_in_geojson.c
postgis/postgis.sql.in.c
regress/offsetcurve_expected

index 7156f8a649c24fb21c84c2c9c987a0b8f0c1ee52..1e0f16ff8ca41dfd369ab7788e1eec6a38dbf217 100644 (file)
@@ -65,6 +65,7 @@ SA_OBJS = \
        lwhomogenize.o \
        lwalgorithm.o \
        lwsegmentize.o \
+       lwlinearreferencing.o \
        lwprint.o \
        vsprintf.o \
        g_box.o \
index eaf9ba5ce259127de0ec4e840cf6399d0f081aef..c59541fc33d615665de780f4ad73a101afb4333b 100644 (file)
@@ -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
index 1f74876a6f4e7ca2e242284485bdd7b47f5d2134..7ee4e563fbc53fbbf63b971d697c333494fba25c 100644 (file)
@@ -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";
 
index da3904552ad5217fe76ea1d6c9802419f49e0b89..5b796f907ec330ed7fd77c138864451bbb9fb692 100644 (file)
@@ -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)
 {
index c8a901a87ebb49e2852e6b05be02c0c725f6210d..7960d521657b6f6d963b64b7bcfd4599e4e97db2 100644 (file)
@@ -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 (file)
index 0000000..d7c9762
--- /dev/null
@@ -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
index aeffa5b679acbcd8fc040f8ee80f15571b92391c..621cd3e98cddeafa919e136091c07f3e07ea9f62 100644 (file)
@@ -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);
index 42ee636d50aff9265f42b0854b82fd9237d75c0e..1c20ba4165fcef5c0a59fc46eb6de801536a22f4 100644 (file)
@@ -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);
        }
index 835ed5517c51dd4d528c88a7e231774f06a75f61..1536cdf0de574fc6d39821d6b283d0b90e688274 100644 (file)
@@ -2567,7 +2567,7 @@ CREATE OR REPLACE FUNCTION ST_Buffer(geometry,float8,text)
 -- Availability: 2.0.0 - requires GEOS-3.2 or higher\r
 CREATE OR REPLACE FUNCTION ST_OffsetCurve(line geometry, distance float8, params text DEFAULT '')\r
        RETURNS geometry\r
-       AS 'MODULE_PATHNAME','offsetcurve'\r
+       AS 'MODULE_PATHNAME','ST_OffsetCurve'\r
        LANGUAGE 'C' IMMUTABLE STRICT\r
        COST 100;\r
 \r
index bf62391e9fa12a82182679dab902a5b186cd7e83..f70e5db90c10451fb623a8dfded70368ca0ceaa4 100644 (file)
@@ -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)