I've been going a bit crazy in trying to create a "setback" buffer on QGIS for coastal lines in a map projecting this buffer outwards towards the sea. I've managed to create a normal Bidirectional buffer, but that's not the outcome that I'm looking for.
[GIS] How to create a “setback” buffer for coastal lines in QGIS
bufferqgis
Related Solutions
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)
You can use QGIS' quadrant placement specifier determined from the line's azimuth to place a better label. The quadrant specifies 8 positions around a point:
[ 0=Above Left | 1=Above | 2=Above Right |
3=Left | 4=Over | 5=Right |
6=Below Left | 7=Below | 8=Below Right ]
Here's an example around Null Island, creating a table and two views.
CREATE TABLE points (
gid serial PRIMARY KEY,
geom geometry(Point, 4326),
label_geom geometry(Point, 4326),
label text
);
INSERT INTO points(geom, label_geom, label)
SELECT origin, pt, round(degrees(ST_Azimuth(origin, pt))) || ' degrees'
FROM (
SELECT
ST_SetSRID(ST_MakePoint(0, 0), 4326) AS origin,
ST_SetSRID(ST_MakePoint(cos(radians(x)), sin(radians(x))), 4326) AS pt
FROM generate_series(0, 350, 15) AS x
) AS f;
CREATE OR REPLACE VIEW point_labels AS
SELECT gid, label_geom AS geom,
CASE
WHEN ST_Azimuth(geom, label_geom) ISNULL THEN 2 -- default if azimuth cannot be determined
WHEN degrees(ST_Azimuth(geom, label_geom)) < 22.5 THEN 1 -- Above
WHEN degrees(ST_Azimuth(geom, label_geom)) < 67.5 THEN 2 -- Above Right
WHEN degrees(ST_Azimuth(geom, label_geom)) < 112.5 THEN 5 -- Right
WHEN degrees(ST_Azimuth(geom, label_geom)) < 157.5 THEN 8 -- Below Right
WHEN degrees(ST_Azimuth(geom, label_geom)) < 202.5 THEN 7 -- Below
WHEN degrees(ST_Azimuth(geom, label_geom)) < 247.5 THEN 6 -- Below Left
WHEN degrees(ST_Azimuth(geom, label_geom)) < 292.5 THEN 3 -- Left
WHEN degrees(ST_Azimuth(geom, label_geom)) < 337.5 THEN 0 -- Above Left
ELSE 1 -- >= 337.5 Above
END AS quadrant, label
FROM points;
CREATE OR REPLACE VIEW leader_line AS
SELECT gid, ST_MakeLine(geom, label_geom)::geometry(LineString, 4326) AS geom, label
FROM points;
Then in QGIS, add:
points
–geom
leader_line
–geom
– primary key needs to begid
point_labels
–geom
– primary key needs to begid
Now configure the layer properties for point_labels
:
- Change style so the point is not drawn, e.g., change size to 0.0
- Label this layer with
label
, and change placement to "Offset from point", modifying the "Quadrant" to use the attribute fieldquadrant
Bingo!
Note that a slightly different approach is required for geography
types, since ST_Azimuth behaves differently.
Update: When adding new points to the points
layer, the geom
field is updated as usual, but the label_geom
is not. To populate a default value of label_geom
with new points, a trigger needs to be created. But if a trigger function is used, the quadrant
specifier can be stored in the points
table and the point_labels
view can be ignored:
For example, let's start again with a slightly different example with one table and one view:
-- DROP TABLE points CASCADE;
CREATE TABLE points (
gid serial PRIMARY KEY,
geom geometry(Point, 4326),
label_geom geometry(Point, 4326),
quadrant integer,
label text
);
CREATE FUNCTION label_geom_tg_fn() RETURNS trigger AS
$BODY$
DECLARE
azimuth float8;
BEGIN
-- Set a default label_geom
IF NEW.label_geom ISNULL THEN
NEW.label_geom := NEW.geom;
END IF;
-- Determine quadrant
azimuth := degrees(ST_Azimuth(NEW.geom, NEW.label_geom));
NEW.quadrant := CASE
WHEN azimuth ISNULL THEN 2 -- azimuth cannot be determined, so put Above Right
WHEN azimuth < 22.5 THEN 1 -- Above
WHEN azimuth < 67.5 THEN 2 -- Above Right
WHEN azimuth < 112.5 THEN 5 -- Right
WHEN azimuth < 157.5 THEN 8 -- Below Right
WHEN azimuth < 202.5 THEN 7 -- Below
WHEN azimuth < 247.5 THEN 6 -- Below Left
WHEN azimuth < 292.5 THEN 3 -- Left
WHEN azimuth < 337.5 THEN 0 -- Above Left
ELSE 1 END;-- >= 337.5 Above
RETURN NEW;
END;$BODY$ LANGUAGE plpgsql;
CREATE TRIGGER label_geom_tg BEFORE INSERT OR UPDATE
ON points FOR EACH ROW
EXECUTE PROCEDURE label_geom_tg_fn();
The from the first example, re-do the INSERT INTO points
and CREATE OR REPLACE VIEW leader_line
statements, as these do not require modification. But ignore the leader_line
view.
Then in QGIS, add:
points
–geom
points
–label_geom
leader_line
–geom
– primary key needs to begid
Now configure the layer properties for points
with label_geom
as the first example did for point_labels
. The quadrant
specifier will be modified automatically for new and moved points, but you will only notice these changes each time you save your edits.
Best Answer
As far as I know, there is no way to create a one-sided buffer for lines in QGIS.
You'll have an easier time converting your coast line to a land polygon an buffering that.