diff -uNr trunk/postgis/lwgeom_geos.c trunk-patched/postgis/lwgeom_geos.c
--- trunk/postgis/lwgeom_geos.c	2010-03-09 13:18:15.342151793 +0100
+++ trunk-patched/postgis/lwgeom_geos.c	2010-03-09 17:36:36.693497993 +0100
@@ -45,6 +45,7 @@
 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);
@@ -1277,6 +1278,195 @@
 	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;
+	int leftSide = 0;
+	char *param;
+	char *params = NULL;
+
+
+	PROFSTART(PROF_QRUN);
+	// geom arg
+	geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+	// distance/size arg
+	size = PG_GETARG_FLOAT8(1);
+	
+
+	nargs = PG_NARGS();
+
+	initGEOS(lwnotice, lwnotice);
+
+	PROFSTART(PROF_P2G1);
+	g1 = (GEOSGeometry *)POSTGIS2GEOS(geom1);
+	PROFSTOP(PROF_P2G1);
+
+	// side arg
+	param = pstrdup(PG_GETARG_CSTRING(2));
+	POSTGIS_DEBUGF(3, "Param side: %s", param);
+	if ( !strcmp(param, "left") )
+		leftSide = 1;
+	else if( !strcmp(param, "right") )
+		leftSide = 0;
+	else
+	{
+		lwerror("Invalid side string, "
+			"accept: "
+			"'left' or 'right' ");
+	}
+	
+	// options arg (optional)
+	if (nargs > 3)
+	{
+		/* We strdup `cause we're going to modify it */
+		params = pstrdup(PG_GETARG_CSTRING(3));
+
+		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);
+	g3 = GEOSSingleSidedBuffer(g1, size, quadsegs,
+	                         joinStyle, mitreLimit, leftSide);
+	PROFSTOP(PROF_GRUN);
+
+	if (g3 == NULL)
+	{
+		elog(ERROR,"GEOS singleSidedBuffer() threw an error!");
+		GEOSGeom_destroy(g1);
+		PG_RETURN_NULL(); /* never get here */
+	}
+
+	POSTGIS_DEBUGF(3, "result: %s", GEOSGeomToWKT(g3));
+
+	GEOSSetSRID(g3, pglwgeom_getSRID(geom1));
+
+	PROFSTART(PROF_G2P);
+	result = GEOS2POSTGIS(g3, TYPE_HASZ(geom1->type));
+	PROFSTOP(PROF_G2P);
+
+	if (result == NULL)
+	{
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g3);
+		elog(ERROR,"GEOS singleSidedBuffer() 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 "
+	"offsetcurve function "
+	"(needs 3.2 or higher)",
+	POSTGIS_GEOS_VERSION);
+			
+	elog(ERROR,"offsetcurve() only in GEOS version >= 3.2 ! Please recompile PostGIS using GEOS version >= 3.2");
+	PG_RETURN_NULL(); /* never get here */
+#endif /* POSTGIS_GEOS_VERSION < 32 */	
+}
+
+
 PG_FUNCTION_INFO_V1(intersection);
 Datum intersection(PG_FUNCTION_ARGS)
 {
diff -uNr trunk/postgis/postgis.sql.in.c trunk-patched/postgis/postgis.sql.in.c
--- trunk/postgis/postgis.sql.in.c	2010-03-09 13:18:15.347456009 +0100
+++ trunk-patched/postgis/postgis.sql.in.c	2010-03-09 17:36:48.381748110 +0100
@@ -3886,6 +3886,41 @@
 	AS 'SELECT ST_Buffer($1, $2, $3)'
 	LANGUAGE 'SQL' IMMUTABLE STRICT;
 
+-- Availability: 2.0.0 - requires GEOS-3.2 or higher
+CREATE OR REPLACE FUNCTION _ST_OffsetCurve(geometry,float8,cstring,cstring)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME','offsetcurve'
+	LANGUAGE 'C' IMMUTABLE STRICT
+	COST 100;
+
+-- Availability: 2.0.0 - requires GEOS-3.2 or higher
+CREATE OR REPLACE FUNCTION ST_OffsetCurve(geometry,float8,text,text)
+	RETURNS geometry
+	AS $$ SELECT _ST_OffsetCurve($1, $2,
+		CAST( regexp_replace($3, '^[0123456789]+$',
+			'quad_segs='||$3) AS cstring),
+		CAST( $4 AS cstring)
+		)
+	   $$
+	LANGUAGE 'SQL' IMMUTABLE STRICT;
+
+-- Availability: 2.0.0 - requires GEOS-3.2 or higher
+CREATE OR REPLACE FUNCTION _ST_OffsetCurve(geometry,float8,cstring)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME','offsetcurve'
+	LANGUAGE 'C' IMMUTABLE STRICT
+	COST 100;
+
+-- Availability: 2.0.0 - requires GEOS-3.2 or higher
+CREATE OR REPLACE FUNCTION ST_OffsetCurve(geometry,float8,text)
+	RETURNS geometry
+	AS $$ SELECT _ST_OffsetCurve($1, $2,
+		CAST( regexp_replace($3, '^[0123456789]+$',
+			'quad_segs='||$3) AS cstring)
+		)
+	   $$
+	LANGUAGE 'SQL' IMMUTABLE STRICT;
+
 -- Deprecation in 1.2.3
 CREATE OR REPLACE FUNCTION convexhull(geometry)
 	RETURNS geometry

