I'm looking for a way to create so-called one-sided buffers or parallel lines in PostGIS. Example: 1. buffer, 2. one-side, 3. both-sides

alt text

I've found some discussion on the mailing list from 2009 and information that it has been implemented in GEOS, but nothing about the current PostGIS status.

In case the function is not implemented yet, do you know any workarounds? Is it possible to cut off one side of a normal buffer?

  • So you're looking for an equivalent to "offset" in AutoCAD?
    – dassouki
    Aug 15, 2010 at 23:06
  • @dassouki: Sorry, I don't know AutoCAD. But I think UMN Mapserver has an "offset" option that would do what I'm looking for.
    – underdark
    Aug 17, 2010 at 10:11
  • How to create one-sided buffers or parallel lines, from a table that have 600 lines?
    – user3230
    Jun 10, 2011 at 0:35
  • Welcome to the site. If the current set of answers doesn't solve your problem you should open this as a new question (and state why this question does not answer your problem). Within the format of the site this is not an appropriate place to make this statement, as it is not an answer to the question.
    – Andy W
    Jun 10, 2011 at 1:26
  • How can I create a parallel lines in a MULTILINESTRING table? Jun 23, 2011 at 13:35

4 Answers 4


Proper one-sided buffers were supposed to have landed in 1.5, but it looks to me that while the styles did land, sidedness didn't make it in. There is however a current patchset which exposes GEOSSingleSidedBuffer and performs the one-sided buffer as expected, under the name ST_OffsetCurve; see further background in ticket #413. In use:

select ST_AsText(ST_OffsetCurve(
ST_GeomFromText('LINESTRING(10 10,10 20, 20 20 )'),
1,'right', 'join=mitre mitre_limit=5.0'));
LINESTRING(20 19,11 19,11 10)
  • Hi I'm trying to use this solution but don't use postgis often - I have tested this query and get some ouput values, but how do i translate those into features? I just want the lines in the end, either as part of the original table, or a new one.This is my query: select ST_AsText(ST_OffsetCurve( test_data_.geom, test_data_.buffer_dis,'join=mitre mitre_limit=5.0')) from test_data_;
    – kflaw
    Apr 18, 2016 at 21:07
  • @kflaw - you've probably already figured this out, but you just need to add to the beginning of the query: "create newtable as" or for a view, "create or replace view newview as" followed by the select statement.
    – jbalk
    Mar 10, 2017 at 2:03

This sample creates two polygons on either side of a linestring. It requires PostGIS 1.5 or greater. I'm not sure how well it will cope with lines that cross themselves.

SELECT ST_AsText(geom)

FROM ST_Dump ((

  ST_Polygonize(GEOMUNION(ST_Boundary(ST_Buffer(the_geom, 0.5, 'endcap=flat join=round')), the_geom)) AS buffer_sides 
  (SELECT ST_GeomFromText('LINESTRING(1 1, 1 5, 5 5)') AS the_geom) AS table1


It outputs:


 POLYGON((0.5 5,0.509607359798385 5.09754516100806,0.538060233744357 5.19134171618254,0.584265193848727 5.2777851165098,
0.646446609406726 5.35355339059327,0.722214883490199 5.41573480615127,0.808658283817455 5.46193976625564,0.9024548389919
36 5.49039264020162,1 5.5,5 5.5,5 5,1 5,1 1,0.5 1,0.5 5))
 POLYGON((5 5,5 4.5,1.5 4.5,1.5 1,1 1,1 5,5 5))
(2 rows)

The code works the following way:

  1. Buffer the linestring using ST_Buffer. We take advantage of the PostGIS 1.5 feature supporting custom endcaps in order to specify no end cap at all. See example below.
  2. Split the buffered polygon in two, using the original line, using the method documented in the wiki.

This could be improved to cope with self-crossing lines in future.

A buffered linestring with a flat endcap


This modification creates two parallel linestring. It requires PostGIS 1.5 or greater.

required geometry or wkt, and distance in the buffer

SELECT astext(
     st_removepoint(        st_linemerge(ST_Difference(st_boundary(geom),ST_GeomFromText('LINESTRING(1 1, 1 5, 5 5)')))         ,0),
      st_npoints(st_linemerge(ST_Difference(st_boundary(geom),ST_GeomFromText('LINESTRING(1 1, 1 5, 5 5)'))))-2)

    ) as parallel
    FROM ST_Dump ((
    ST_Polygonize(st_union(ST_Boundary(ST_Buffer(geometria, 0.5, 'endcap=flat join=mitre mitre_limit=5.0')), geometria)) AS buffer_sides 
    (SELECT ST_GeomFromText('LINESTRING(1 1, 1 5, 5 5)') 
        AS geometria) AS tabla))

-------------- RESULTS

"LINESTRING(0.5 1,0.5 5.5,5 5.5)"
"LINESTRING(5 4.5,1.5 4.5,1.5 1)"


Because i still can't comment here, i add this answer

SCW give the best answer,

select ST_AsText(ST_OffsetCurve(
ST_GeomFromText('LINESTRING(10 10,10 20, 20 20 )'),
1, 'right', 'join=mitre mitre_limit=5.0'));
LINESTRING(20 19,11 19,11 10)

But looks like the function change

Now the 'right' parameter isn't necessary. Using positive distance will create left side and negative distance will create the right side

Also no patch needed with my postgis

SELECT PostGIS_full_version();
"POSTGIS="2.0.3 r11132" GEOS="3.3.8-CAPI-1.7.8" 
PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.2, 
released 2012/10/08" LIBXML="2.7.8" LIBJSON="UNKNOWN" RASTER"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.