6

I have a river network (Line with arrow as shown in image below) and I need to create perpendicular lines to the river at every 15 meters as you walk along it (see image below).

Is there a function in PostGIS capable of doing this?

Taras
  • 32,823
  • 4
  • 66
  • 137
user1655130
  • 650
  • 5
  • 13

4 Answers4

3

No clear existing function to do this, but working back from the great suggestions on this post: Making perpendicular lines through each polygon edge using PostGIS?

Assuming you are good with PostGIS syntax, this might help you:

  1. Split the line into 15m segments, like this: https://www.northrivergeographic.com/qgis-split-a-line-at-an-interval
  2. Loop every segment in the newly segmented line and for each segment:
  3. Create parallel lines with ST_OffsetCurve with a desired distance D (can be as longer than the lake/sea as shown above, we will clip them later), on both sides of the line
  4. For every 15m segment, make a new line from the centroid of that segment to the centroid of the corresponding parallel lines with ST_MakeLine
  5. You should have perpendicular lines, spaced 15m extending beyond the river basin/lake
  6. Clip the lines with the sea region
Sorin RUSU
  • 640
  • 3
  • 12
2

Unfortunately, I can't see your figure and as a result, I suggest to create the following pgSQL geo-function for implementation, as I perceived the content of the question.

Create the geo-function:

CREATE OR REPLACE FUNCTION ST_PerpendicularTransectsFromLine(
    geom GEOMETRY,
    sl integer)
    RETURNS TABLE (geom GEOMETRY) AS 
$BODY$
WITH
geodata AS (SELECT row_number() over() AS id, geom),
linecut AS (SELECT id, ST_LineSubstring(d.geom, substart, CASE WHEN subend > 1 THEN 1 ELSE subend END) geom
FROM (SELECT id, geom, ST_Length(((geom)::geometry)) len, sl sublen FROM geodata) AS d
CROSS JOIN LATERAL (SELECT i,  (sublen * i)/len AS substart, (sublen * (i+1))/len AS subend
FROM generate_series(0, floor( d.len / sublen)::integer) AS t(i) WHERE (sublen * i)/len <> 1.0) AS d2),
rotate AS (SELECT id, (ST_Rotate(ST_Collect(geom), -pi()/2, ST_Centroid(geom))) geom FROM linecut GROUP BY id, geom),
tbld AS (SELECT id, (ST_Dump(geom)).geom geom FROM rotate)
        SELECT ST_MakeLine(ST_StartPoint(geom), ST_EndPoint(geom)) geom FROM tbld;
$BODY$
LANGUAGE SQL

Run for your geodata (the function action is tested for geodata located in a flat rectangular coordinate system).

SELECT ST_PerpendicularTransectsFromLine(geom, 15) geom FROM <name_line_table>

As a result you should get a geo image of a river with almost (rivers are usually curves :-)) perpendicular transects of 15 meters long, as shown in the figure below.

enter image description here

Note: If necessary, crop them with the ST_Buffer function with the required size.

(-: FOGS :-)...

Cyril Mikhalchenko
  • 4,397
  • 7
  • 14
  • 45
  • That's a clever solution. It might be nice to return the result as a MultiLineString. And allow supplying a length for the transects. – dr_jts Jan 04 '24 at 00:52
  • Using the centroid as the centre point of the rotated transect line is problematic, since the centroid may not lie on the linestring. – dr_jts Jan 04 '24 at 05:54
  • @dr-jts (Martin Davis), for now this function can be called ST_SimplifyPerpendicularTransectsFromLine and this is the basis for its development. Of course you can move perpendicular transects to their centroids and control their size. But I would like someone else to develop it, especially since it can now be done on GitHub, although I haven't mastered it yet :-)..... – Cyril Mikhalchenko Jan 04 '24 at 11:14
1

Here's another function to create transects, providing control over the transect length as well as the transect separation length

It operates as follows:

  • split the input line into sections of length secLen
  • for each section compute the transect angle and centre point of the section (the angle is Pi - azimuth(startPt, endPt))
  • compute the transect lines from two points offset from the centre point
CREATE OR REPLACE FUNCTION ST_Transects(
    lineGeom geometry,
    secLen float,
    transectLen float)
    RETURNS geometry 
    LANGUAGE sql AS 
$BODY$
WITH
geodata AS (SELECT lineGeom, ST_Length(lineGeom) AS lineLen),
sections AS (SELECT ST_LineSubstring(lineGeom, secStart, CASE WHEN secEnd > 1 THEN 1 ELSE secEnd END) geom
    FROM geodata AS t
    CROSS JOIN LATERAL 
        (SELECT i * secLen/lineLen AS secStart, (i+1) * secLen/lineLen AS secEnd
            FROM generate_series(0, floor(lineLen / secLen)::integer) AS t(i) 
            WHERE (secLen * i)/lineLen <> 1.0) AS t2 ),
sectAnglePt AS (SELECT pi() - ST_Azimuth(ST_StartPoint(geom), ST_EndPoint(geom)) AS ang,
                  ST_LineInterpolatePoint(geom, 0.5) AS centre
                  FROM sections)
SELECT ST_Collect(ST_MakeLine(
                    ST_Point( ST_X(centre) - transectLen * cos(ang), 
                              ST_Y(centre) - transectLen * sin(ang)),
                    ST_Point( ST_X(centre) + transectLen * cos(ang), 
                              ST_Y(centre) + transectLen * sin(ang)) )) AS geom 
  FROM sectAnglePt;
$BODY$;

Call it like:

SELECT ST_Transects( line, 0.01, 0.005);

enter image description here

dr_jts
  • 5,038
  • 18
  • 15
  • I always like your solutions very much! But, it is always easier to criticize than to create :-).... 1) Geoinstrument will work only for LINESTRING type lines, as it is required by ST_LineSubstring function; 2) Transects are not always perpendicular to the curved line.... 3) I would specify the name of the geoinstrument, because, transects 1) can extend the line; 2) can be perpendicular; 3) can be inclined at a certain angle to the line; 4) can be built from geo-point(s).... – Cyril Mikhalchenko Jan 12 '24 at 19:33
  • So you are suggesting calling this say ST_LineTransects? Makes sense to me. – dr_jts Jan 12 '24 at 21:05
  • Indeed, this function creates transects which are perpendicular to the line section. A transect may not be perpedicular to the single line segment it intersects (i.e. the one containing the centre point). If desired this behaviour could be provided by a slightly different implementation. – dr_jts Jan 12 '24 at 21:11
  • The geo-tool name variations of your choice can be: 1) ST_LineRectangularTransects; ST_LinePerpendicularTransects; 3) ST_LineTransects.
  • In any case, to create a new geoinstrument, it's a great job and the OP and others will find it easier to solve similar problems...
  • – Cyril Mikhalchenko Jan 13 '24 at 13:45