3

I have a layer of points (Waterfalls) and a layer of lines (Watercourses) with no linking information between them.

I am looking to use a QGIS expression to estimate the angle of the waterfall for the purpose of symbolising it.

The intention is to use the angle of the segment of the watercourse (line) nearest to the waterfall (point) as a proxy for the angle of the stream, and that the waterfall angle will be this rotated by 90 degrees - like the black line across the stream below.

enter image description here

I can easily find the geometry of nearest watercourse for a given waterfall using overlay_nearest:

overlay_nearest('Watercourse',@geometry)

However, I don't see an easy way of getting the nearest segment of that geometry, nor its angle. segments_to_lines would break the line into segments, but I'm not sure how to then get the closest.

Using line_locate_point and then line_interpolate_angle would work but seems a very indirect method. Hopefully someone can point out an alternative.

3
  • 1
    Why do you consider line_locate_point and line_interpolate_angle to be an indirect method? I think it's the best in this case, quite straightforward
    – Babel
    Nov 9, 2023 at 6:48
  • My mathematical mind thought that line_locate_point / line_interpolate_angle lacked elegance. I was hoping for an easy way of identifying the closest segment. It appears that it's doable but not that easy. Nov 9, 2023 at 21:20
  • My geometrical mind thinks it's quite elegant... ;-)
    – Babel
    Nov 9, 2023 at 21:32

1 Answer 1

2

If you specifically do not want to use line_locate_point and line_interpolate_angle, you can do it like so:

  • First create an array of the segments as geometries.
  • The distance from each segment to your waterfall points are calculated in a separate array and this is used to index the array of geometries to find the closest segment.
  • The main_angle of the segment + 90 is then your perpendicular angle (in my case, set to the Rotation property of the 'line' simple marker type.
with_variable('geoms',
    geometries_to_array(
        segments_to_lines(
            aggregate(
                'watercourse', 
                'collect',
                @geometry
            )
        )
    ),  
    with_variable(
        'dists',
        array_foreach(@geoms, distance(@element, @geometry)),       
        main_angle(array_get(@geoms, array_find(@dists, array_min(@dists)))) + 90
    )   
)

Note: the geometries_to_array function was added relatively recently. For earlier versions of QGIS the following snippet can be substituted for with_variable('geoms', geometries_to_array(segments_to_lines(...))

with_variable('segments',
    segments_to_lines(aggregate('watercourse', 'collect',   @geometry)),
    with_variable('geoms',  
        array_foreach(
            generate_series(1, num_geometries(@segments)),
            geometry_n(@segments, @element)
        ),
        
        ...

    )
)

However, I would agree with @Babel (credit for a simplified expression) that it is less fiddly to use line_locate_point and line_interpolate_angle:

line_interpolate_angle(     
    overlay_nearest('watercourse',  @geometry)[0],  
    line_locate_point(
        overlay_nearest('watercourse', @geometry)[0],       
        @geometry   
    ) 
) + 90

enter image description here

Notes: the positions of the waterfall points were snapped to the watercourses using a Geometry Generator expression: closest_point(overlay_nearest('watercourse', @geometry)[0], @geometry)

1
  • Yeah, it does seem simpler to stick with line_interpolate_angle! For simplifying that expression slightly, I'd gone with: with_variable('watercourse',overlay_nearest('watercourse',@geometry)[0], line_interpolate_angle(@watercourse,line_locate_point(@watercourse,$geometry)))-90 Nov 9, 2023 at 21:28

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.