[GIS] How to create a buffer on a linestring with varying or increasing/decreasing width

bufferpostgispostgis-2.0postgresqltopography

I want to buffer rivers/streams for a topographic map. I'm using a PostgreSQL/PostGIS database. Usually, I create buffer with ST_Buffer with a constant width.

For example, the width of a river buffer should be increasing from the spring to the estuary with a distinct value.

enter image description here

Is there a way to create buffer with an increasing or decreasing width starting from a distinct point and ending at a distinct point?

UPDATE

With the help of simplexico I tried to create some geometries that could help to create a thinning buffer around a linestring:

First I create the buffer lines parallel to the river geom and insert them into a table called offsetcurve:

WITH geometries AS (
    SELECT
        gid,
        (ST_Dump(geom)).geom AS geom
    FROM waterways
        WHERE name='river_name')

    SELECT
        gid,
        ST_OffsetCurve(geom, 10, 'quad_segs=4 join=mitre') AS pos_curve, 
        ST_Reverse(ST_OffsetCurve(geom,-10, 'quad_segs=4 join=mitre')) AS neg_curve
    FROM geometries;

I have to use the reverse function for a further step.

The next step is to dump the points from the buffer lines and the river geom and insert them into a table called offsetpoints:

SELECT
    offsetcurve.gid,
    (ST_DumpPoints(offsetcurve.pos_curve)).geom AS pos_curve_dump,
    (ST_DumpPoints(offsetcurve.neg_curve)).geom AS neg_curve_dump,
    ST_SetSRID((ST_DumpPoints(geometries.geom)).geom, 31468) AS river_dump
FROM offsetcurve, geometries

After that I create lines between the buffer line points and the river geom points and store them in a table called offsetazimuth:

SELECT
    offsetpoints.gid,
    ST_Azimuth(offsetpoints.pos_curve_dump, river_dump) AS pos_curve_azimuth,
    ST_MakeLine(offsetpoints.pos_curve_dump, river_dump) AS pos_azimuth_line,
    ST_Azimuth(offsetpoints.neg_curve_dump, river_dump) AS neg_curve_azimuth,
    ST_MakeLine(offsetpoints.neg_curve_dump, river_dump) AS neg_azimuth_line
FROM offsetpoints

Visualizing all new geometries, I have the following picture:

enter image description here

The red and blue points are dumps from the buffer line.
The green points are the river geom dumps.

The blue and red points are connected to the green points with the offsetazimuth line.

I can change the position of the buffer points (e.g. for one buffer line) along the azimuth line with:

SELECT 
    ST_MakePoint(
        ST_X(st_endpoint(pos_azimuth_line)) - (ST_X(st_endpoint(pos_azimuth_line))-st_x(st_startpoint(pos_azimuth_line)))/6, 
        st_y(st_endpoint(pos_azimuth_line)) - (st_y(st_endpoint(pos_azimuth_line))-st_y(st_startpoint(pos_azimuth_line)))/6
                ) as geom 
FROM offsetazimuth

The results are new points with a constant distance from the river geom:

enter image description here

The first step of a final solution has to be that the first point has the distance like (buffer_width(B)-buffer_width(A))/count(river_dump_points) (see first picture) from the river geom on the offsetazimuth line (see the last but one query).

For example:
The first new point has a distance of (200m-100m)/100=1m from the river geom, the second one ((200m-100m)/100)*2=2m and so on. 200m is the maximum width,100m is the minimum width of the wanted buffer. 100 is the number of dumped points from the river geom.

It's the question if this (increasing or decreasing distance value for every buffer line point) is possible with PostGIS!?

The next idea could be a function: setting parameters like the gradient or the length where the buffer has to take place.

Best Answer

One way is re-use this answer

this returns start and endpoint for line , use negative offset for other side

SELECT ST_StartPoint(ST_OffsetCurve(center_geom, startpointoffset) as start, ST_StartPoint(ST_OffsetCurve(center_geom, endpointoffset) as end,  from xx where yyy

this makes line

SELECT ST_Makeline(l.start, l.end) FROM (SELECT ST_StartPoint(ST_OffsetCurve(center_geom, startpointoffset) as start, ST_StartPoint(ST_OffsetCurve(center_geom, endpointoffset) as end,  from xx where yyy) as l

solution for whole area

First take long line , split it to lines containing only 2 points (start and end) and then use offsetcurve 4 times, once for right side startpoint , once right side endpoint and same for left side, then create two lines. After you have all lines, draw line from right side endpoint to left side endpoint (start does not need if you use 0 offset at very first point). After that ST_Polygonize may be solution for creating polygon from lines.

If this answer is useful i can help with final SQL later