Varying size buffer along line with PostGIS

My answer is not very elegant from the programming point of view but perhaps it is also one of the variants of its solution, actually the idea and only then the tools are important.

So the source data is a line of the type-multilinestring, its trajectory should be the same, it's important to adjust the size of buffers and density of points on the line, I played with the data in EPSG:4326.

Run the script:

WITH tbla AS (WITH atbl AS (SELECT id, (((ST_Dump(geom)).geom)) geom FROM line),
    intervals as (SELECT generate_series (0, 999) as steps, generate_series (1, 1000) as gid)
    SELECT steps AS stp, gid, ST_LineInterpolatePoint(geom, steps/(SELECT count(steps)::float-1 FROM intervals)) as geom FROM atbl, intervals GROUP BY id, gid, intervals.steps, geom),
    tblb AS (SELECT generate_series (1, 1000) as gs),
    tblc AS (SELECT * FROM tbla, tblb where tbla.gid=tblb.gs),
    tbld AS (SELECT DISTINCT b.gid, ST_Buffer((ST_DumpPoints(b.geom)).geom, b.gid*0.003) geom FROM tbla a, tblc b GROUP BY b.gid, b.geom),
    tble AS (SELECT ST_ConvexHull(ST_Union(geom, LEAD(geom) OVER(ORDER BY gid))) geom FROM tbld) 
    SELECT ST_Union(geom) geom FROM tble;

The result is shown in the figure.

enter image description here

Original solutions ...

The script is called - ST_VariableBufferFromLine.


I've improved the answer given by @Cyril, it is way faster, here is the solution:

WITH 
step1 AS 
    (SELECT gid, 
        ST_DumpPoints(geom) AS dump, 
        ST_Length(geom) AS len, 
        geom 
     FROM mylines),
step2 AS
    (SELECT gid, 
        (dump).path[1], 
        ST_Buffer((dump).geom, ST_LineLocatePoint(geom, (dump).geom)*len/10 + 0.01) AS geom 
     FROM step1),
step3 AS
    (SELECT gid, 
        ST_ConvexHull(ST_Union(geom, LEAD(geom) OVER(PARTITION BY gid ORDER BY gid, path))) AS geom 
    FROM step2)

SELECT gid, ST_Union(geom) AS geom FROM step3 GROUP BY gid

What this request does ?

Step 1:

SELECT gid, ST_DumpPoints(geom) AS dump, ST_Length(geom) AS len, geom FROM mylines

We extract every points of each line. I keep the GID so I will be able to perform the same operation on several line at the same time.

Step 2:

SELECT gid, (dump).path[1], st_buffer((dump).geom, ST_LineLocatePoint(geom, (dump).geom)*len/10 + 0.01) AS geom FROM step1

We apply a buffer to each point, the buffer size correspond to the corresponding length of the line at this point divided by ten (ten is arbitrary). I need to add a small constant to the buffer size (here 0.01) so the first buffer size is not 0. We keep the path (order) of each point.

Step 3:

SELECT gid, ST_ConvexHull(ST_Union(geom, LEAD(geom) OVER(PARTITION BY gid ORDER BY gid, path))) AS geom FROM step2

We use ST_ConvexHull for each consecutive pair of circular buffer.

SQL FUNCTION:

CREATE OR REPLACE FUNCTION ST_VariableBufferFromLine(
    geom GEOMETRY, 
    Length_BufferSize_Ratio NUMERIC
)

RETURNS GEOMETRY AS

$BODY$

WITH 
    step1 AS 
        (SELECT ST_DumpPoints(geom) AS dump, 
        ST_Length(geom) AS len, 
        geom),
    step2 AS
        (SELECT (dump).path[1], 
        ST_Buffer((dump).geom, GREATEST(ST_LineLocatePoint(geom, (dump).geom)*len/Length_BufferSize_Ratio,0.001)) AS geom 
         FROM step1),
    step3 AS
        (SELECT 
        ST_ConvexHull(ST_Union(geom, LEAD(geom) OVER(ORDER BY path))) AS geom 
        FROM step2)
SELECT ST_Union(geom) AS geom FROM step3

$BODY$

LANGUAGE SQL;

So now we can simply use:

SELECT gid, ST_VariableBufferFromLine(geom,10.0) AS geom FROM mylines

If you need a fixed buffer size (for example the end of the line should have of buffer of 100m) we can simply replace this part in the function:

GREATEST(ST_LineLocatePoint(geom, (dump).geom)*len/Length_BufferSize_Ratio,0.001))

by this one:

GREATEST(ST_LineLocatePoint(geom, (dump).geom)*end_width,0.001))

With end_width = the buffer size at the end of the line. Don't forget to adapt the variable name.

Example result with 2 lines:

enter image description here

Tags:

Buffer

Postgis