</refsection>
</refentry>
+ <refentry id="ST_OffsetCurve">
+ <refnamediv>
+ <refname>ST_OffsetCurve</refname>
+
+ <refpurpose>
+Return an offset line at a given distance and side from an input line.
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>geometry <function>ST_OffsetCurve</function></funcdef>
+ <paramdef><type>geometry </type> <parameter>line</parameter></paramdef>
+ <paramdef><type>float </type> <parameter>signed_distance</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>geometry <function>ST_OffsetCurve</function></funcdef>
+ <paramdef><type>geometry </type> <parameter>line</parameter></paramdef>
+ <paramdef><type>float </type> <parameter>signed_distance</parameter></paramdef>
+ <paramdef><type>text </type> <parameter>style_parameters</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsection>
+ <title>Description</title>
+
+ <para>
+Return an offset line at a given distance and side from an input line.
+All points of the returned geometries are not further than the given
+distance from the input geometry.
+ </para>
+
+ <para>
+For positive distance the offset will be at the left side of the input line
+and retain the same direction. For a negative distance it'll be at the right
+side and in the opposite direction.
+ </para>
+
+ <para>
+Availability: 2.0 - requires GEOS >= 3.2, improved with GEOS >= 3.3
+ </para>
+
+ <para>
+The optional third parameter allows specifying a list of blank-separated
+key=value pairs to tweak operations as follows:
+<itemizedlist>
+<listitem>
+<para>'quad_segs=#' : number of segments used to approximate a quarter circle (defaults to 8).</para>
+</listitem>
+<listitem>
+<para>'join=round|mitre|bevel' : join style (defaults to "round"). 'miter' is also accepted as a synonym for 'mitre'.</para>
+</listitem>
+<listitem>
+<para>'mitre_limit=#.#' : mitre ratio limit (only affects mitred join style). 'miter_limit' is also accepted as a synonym for 'mitre_limit'.</para>
+</listitem>
+</itemizedlist>
+ </para>
+
+ <para>
+Units of distance are measured in units of the spatial reference system.
+ </para>
+
+ <para>The inputs can only be LINESTRINGS.</para>
+
+ <para>Performed by the GEOS module.</para>
+
+ <note><para>
+This function ignores the third dimension (z) and will always give a
+2-d result even when presented with a 3d-geometry.</para></note>
+
+ </refsection>
+
+ <refsection>
+ <title>Examples</title>
+<para>Compute an open buffer around roads</para>
+ <programlisting>
+SELECT ST_Union(
+ ST_OffsetCurve(f.the_geom, f.width/2, "quad_segs=4 join=round"),
+ ST_OffsetCurve(f.the_geom, -f.width/2, "quad_segs=4 join=round")
+) as track
+FROM someroadstable;
+
+ </programlisting>
+ </refsection>
+
+ <refsection>
+ <title>See Also</title>
+ <para><xref linkend="ST_Buffer" /></para>
+ </refsection>
+ </refentry>
+
<refentry id="ST_BuildArea">
<refnamediv>
<refname>ST_BuildArea</refname>
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);
PG_RETURN_POINTER(result);
}
+PG_FUNCTION_INFO_V1(offsetcurve);
+Datum offsetcurve(PG_FUNCTION_ARGS)
+{
+#if POSTGIS_GEOS_VERSION >= 32
+ PG_LWGEOM *geom1;
+ double size;
+ GEOSGeometry *g1, *g3;
+ PG_LWGEOM *result;
+ int quadsegs = 8; /* the default */
+ int nargs;
+
+ enum
+ {
+ JOIN_ROUND = 1,
+ 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;
+
+
+ PROFSTART(PROF_QRUN);
+ // geom arg
+ geom1 = (PG_LWGEOM *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+ // distance/size/direction arg
+ 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);
+ }
+
+
+
+ nargs = PG_NARGS();
+
+ initGEOS(lwnotice, lwnotice);
+ initGEOS(lwnotice, lwgeom_geos_error);
+
+ PROFSTART(PROF_P2G1);
+ g1 = (GEOSGeometry *)POSTGIS2GEOS(geom1);
+ if ( ! g1 ) {
+ lwerror("Geometry could not be converted to GEOS: %s",
+ lwgeom_geos_errmsg);
+ PG_RETURN_NULL();
+ }
+ PROFSTOP(PROF_P2G1);
+
+ // options arg (optional)
+ if (nargs > 2)
+ {
+ /* We strdup `cause we're going to modify it */
+ params = pstrdup(PG_GETARG_CSTRING(2));
+
+ POSTGIS_DEBUGF(3, "Params: %s", params);
+
+ for (param=params; ; param=NULL)
+ {
+ char *key, *val;
+ param = strtok(param, " ");
+ if ( param == NULL ) break;
+ POSTGIS_DEBUGF(3, "Param: %s", param);
+
+ key = param;
+ val = strchr(key, '=');
+ if ( val == NULL || *(val+1) == '\0' )
+ {
+ lwerror("Missing value for buffer "
+ "parameter %s", key);
+ break;
+ }
+ *val = '\0';
+ ++val;
+
+ POSTGIS_DEBUGF(3, "Param: %s : %s", key, val);
+
+ if ( !strcmp(key, "join") )
+ {
+ if ( !strcmp(val, "round") )
+ {
+ joinStyle = JOIN_ROUND;
+ }
+ else if ( !strcmp(val, "mitre") ||
+ !strcmp(val, "miter") )
+ {
+ joinStyle = JOIN_MITRE;
+ }
+ else if ( !strcmp(val, "bevel") )
+ {
+ joinStyle = JOIN_BEVEL;
+ }
+ else
+ {
+ lwerror("Invalid buffer end cap "
+ "style: %s (accept: "
+ "'round', 'mitre', 'miter' "
+ " or 'bevel'"
+ ")", val);
+ break;
+ }
+ }
+ else if ( !strcmp(key, "mitre_limit") ||
+ !strcmp(key, "miter_limit") )
+ {
+ /* mitreLimit is a float */
+ mitreLimit = atof(val);
+ }
+ else if ( !strcmp(key, "quad_segs") )
+ {
+ /* quadrant segments is an int */
+ quadsegs = atoi(val);
+ }
+ else
+ {
+ lwerror("Invalid buffer parameter: %s (accept: "
+ "'join', 'mitre_limit', "
+ "'miter_limit and "
+ "'quad_segs')", key);
+ break;
+ }
+ }
+
+ pfree(params); /* was pstrduped */
+
+ POSTGIS_DEBUGF(3, "joinStyle:%d mitreLimit:%g",
+ joinStyle, mitreLimit);
+
+ }
+
+ PROFSTART(PROF_GRUN);
+
+#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
+ PROFSTOP(PROF_GRUN);
+
+ 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, pglwgeom_get_srid(geom1));
+
+ PROFSTART(PROF_G2P);
+ result = GEOS2POSTGIS(g3, pglwgeom_has_z(geom1));
+ PROFSTOP(PROF_G2P);
+
+ 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); */
+
+ PROFSTOP(PROF_QRUN);
+ PROFREPORT("geos",geom1, NULL, result);
+
+ PG_FREE_IF_COPY(geom1, 0);
+
+ 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 */
+}
+
+
PG_FUNCTION_INFO_V1(intersection);
Datum intersection(PG_FUNCTION_ARGS)
{
$$\r
LANGUAGE 'SQL' IMMUTABLE STRICT;\r
\r
+-- Availability: 2.0.0 - requires GEOS-3.2 or higher\r
+CREATE OR REPLACE FUNCTION ST_OffsetCurve(line geometry, distance float8, params cstring DEFAULT '')\r
+ RETURNS geometry\r
+ AS 'MODULE_PATHNAME','offsetcurve'\r
+ LANGUAGE 'C' IMMUTABLE STRICT\r
+ COST 100;\r
+\r
-- PostGIS equivalent function: convexhull(geometry)\r
CREATE OR REPLACE FUNCTION ST_ConvexHull(geometry)\r
RETURNS geometry\r
# ST_HausdorffDistance, ST_Buffer(params)
TESTS += \
hausdorff \
+ offsetcurve \
regress_buffer_params
endif
--- /dev/null
+\set VERBOSITY terse
+set client_min_messages to NOTICE;
+SELECT 't0', ST_OffsetCurve('POINT(0 0)', 10);
+SELECT 't0', ST_AsEWKT(ST_OffsetCurve('SRID=42;LINESTRING(0 0, 10 0)', 0));
+SELECT 't1', ST_AsEWKT(ST_OffsetCurve('SRID=42;LINESTRING(0 0, 10 0)', 10));
+SELECT 't2', ST_AsEWKT(ST_OffsetCurve('SRID=42;LINESTRING(0 0, 10 0)', -10));
+SELECT 't3', ST_AsEWKT(ST_OffsetCurve('SRID=42;LINESTRING(10 0, 0 0)', 10));
+SELECT 't4', ST_AsEWKT(ST_OffsetCurve('SRID=42;LINESTRING(10 0, 0 0)', -10));
+SELECT 't5', ST_AsEWKT(ST_SnapToGrid(ST_OffsetCurve(
+ 'SRID=42;LINESTRING(0 0, 10 0, 10 10)', -10),
+1));
+SELECT 't6', ST_AsEWKT(ST_SnapToGrid(ST_OffsetCurve(
+ 'SRID=42;LINESTRING(0 0, 10 0, 10 10)', -10,
+ 'quad_segs=2'),
+1));
+SELECT 't7', ST_AsEWKT(ST_OffsetCurve(
+ 'SRID=42;LINESTRING(0 0, 10 0, 10 10)', -10,
+ 'join=bevel')
+);
+SELECT 't8', ST_AsEWKT(ST_SnapToGrid(ST_OffsetCurve(
+ 'SRID=42;LINESTRING(0 0, 10 0, 10 10)', -10,
+ 'quad_segs=2 join=mitre'),
+1));
+SELECT 't9', ST_AsEWKT(ST_SnapToGrid(ST_OffsetCurve(
+ 'SRID=42;LINESTRING(0 0, 10 0, 5 10)', -10,
+ 'quad_segs=2 join=mitre mitre_limit=1'),
+1));
+SELECT 't10', ST_AsEWKT(ST_SnapToGrid(ST_OffsetCurve(
+ 'SRID=42;LINESTRING(0 0, 10 0, 5 10)', 2,
+ 'quad_segs=2 join=mitre mitre_limit=1'),
+1));
+SELECT 't10b', ST_AsEWKT(ST_SnapToGrid(ST_OffsetCurve(
+ 'SRID=42;LINESTRING(0 0, 10 0, 5 10)', 2,
+ 'quad_segs=2 join=miter miter_limit=1'),
+1));
+SELECT 't11', ST_AsEWKT(ST_SnapToGrid(ST_OffsetCurve(
+ 'LINESTRING(36 38,38 35,41 34,42 33,45 32,47 28,50 28,52 32,57 33)', 2,
+ 'join=mitre'),
+0.2));
+SELECT 't12', ST_AsEWKT(ST_SnapToGrid(ST_OffsetCurve(
+ 'LINESTRING(36 38,38 35,41 34,42 33,45 32,47 28,50 28,52 32,57 33)', -2,
+ 'join=mitre'),
+0.2));
--- /dev/null
+ERROR: GEOSOffsetCurve: IllegalArgumentException: BufferBuilder::bufferLineSingleSided only accept 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)
+t3|SRID=42;LINESTRING(10 -10,0 -10)
+t4|SRID=42;LINESTRING(0 10,10 10)
+t5|SRID=42;LINESTRING(20 10,20 0,20 -2,19 -4,18 -6,17 -7,16 -8,14 -9,12 -10,10 -10,0 -10)
+t6|SRID=42;LINESTRING(20 10,20 0,17 -7,10 -10,0 -10)
+t7|SRID=42;LINESTRING(20 10,20 0,10 -10,0 -10)
+t8|SRID=42;LINESTRING(20 10,20 -10,0 -10)
+t9|SRID=42;LINESTRING(14 14,21 -1,16 -9,0 -10)
+t10|SRID=42;LINESTRING(0 2,7 2,3 9)
+t10b|SRID=42;LINESTRING(0 2,7 2,3 9)
+t11|LINESTRING(37.6 39.2,39.2 36.6,42 35.8,43 34.8,46.4 33.6,48.2 30,48.8 30,50.6 33.8,56.6 35)
+t12|LINESTRING(57.4 31,53.4 30.2,51.2 26,45.8 26,43.6 30.4,41 31.2,40 32.2,36.8 33.4,34.4 36.8)