3

I have LineLayer with "start_point" and "end_point" field attributes which can snap to either PointLayer1 or PointLayer2.

Using the Field calculator, I would like to make an expression which would take "id" attribute from the point it is snapped, regardless of which point layer it is snapped to.

"start_point":

aggregate(
    layer:='PointLayer1 name',
    aggregate:='max',
    expression:="id",
    filter:=intersects(buffer($geometry, 0.05), start_point(geometry(@parent)))
    )

"end_point" attribute field is the same with end_point(geometry(@parent)) instead.

I'm just trying to cover PointLayer2 so I get its "id" field value as well. So with line layer having a "start_point" and "end_point" to its geometry, to which can be snapped either a PointLayer1 or PointLayer2 feature, I want the start_point and end_point attribute fields to receive the "id" feature from the point features snapped to them.

I use basic QGIS and I don't use RefFunctions as I need it as simple and reusable as possible for multiple PCs.

if (PointLayer1 feature is snapped to LineLayer's geometry start_point)
    {
    LineLayer start_point attribute field receives PointLayer1 feature's id value
    }
else if (PointLayer2 feature is snapped to LineLayer's geometry start_point)
    {
    LineLayer start_point attribute field receives PointLayer2 feature's id value
    }
Taras
  • 32,823
  • 4
  • 66
  • 137
IKindaNeedAHand
  • 311
  • 1
  • 9
  • 2
    Sorry, not really clear what you want to do, how many layer (and which ones) you have, if your points are already snapped to a point or if you want to achieve that etc. Can you add a screenshot? – Babel Aug 25 '21 at 12:16
  • Yes, the points are already snapped to the line layer. I have 3 layers: LineLayer, PointLayer1, PointLayer2.

    LineLayer possesses 2 field attributes (start_point, end_point) which I want to take the id attribute from the point feature which is snapped them.

    Do I need to upload the screenshot on a filesharing platform?

    – IKindaNeedAHand Aug 25 '21 at 12:23
  • Unfortunately it doesn't. ||' - '|| seems to simply join the two features' values which are snapped to the line into one string or something like that. I need an OR condition. Either point layer feature that is snapped to the line's start_point/end_point should have its id given to the line's start_point attribute – IKindaNeedAHand Aug 25 '21 at 14:58

1 Answers1

2

Edit

For the additional information you provided in the comments, the solution has to be modified (old solution below).

First create a virtual layer that collects the two point layers in one layer. That makes it easier to get the id using expressions, as we only have to check one layer (the virtual one).

  1. Create a "Virtual Layer" through Layer > Add Layer > Add/Edit Virtual Layer... called virtual with this query:
SELECT
    a.geometry,
    a.id
FROM
    point1 a
UNION
SELECT
    b.geometry,
    b.id
FROM
    point2 b
  1. On the line layer, use this expression that creates the values for start- and end-point at once:
    array_to_string (
        array_foreach (
            array(1,2),
            with_variable (
                'count',
                @element,
                if (@count=1,'start: ' , 'end: ') || 
                attribute (
                    get_feature_by_id (
                        'virtual',
                        array_first (
                            array_remove_all(
                                array_foreach (
                                    overlay_touches('virtual',$id),
                                    if (
                                        length(
                                            make_line(
                                                if (@count=1, start_point($geometry),end_point($geometry)),
                                                geometry(get_feature_by_id ('virtual',@element))
                                        ))=0,
                                        @element,
                                        ''
                                )),
                                ''
                    ))),
                    'id'
    ))))

enter image description here

Old solution

If you have three layers: line, PointLayer1, and PointLayer2, use this expression on the line layer for the start_point (and replace start_point with end_point for the end_point):

if(
    within(
        array_first(overlay_touches('PointLayer1', $geometry)),
        buffer(end_point($geometry), 0.00001)),
    array_first(overlay_touches('PointLayer1', $id)),
    array_first(overlay_touches('PointLayer2', $id))
    )

Screenshot: PointLayer1 in red, PointLayer2 in blue; line in black with arrow-symbol at the end-point and labeled with the expression from above (highlighted in yellow, with an additional text-string stating start_/end_point = PointLayer1/2, id: ): enter image description here

Taras
  • 32,823
  • 4
  • 66
  • 137
Babel
  • 71,072
  • 14
  • 78
  • 208
  • Hi! It does work though there seems to be an issue where PointLayer2's id is retrieved for both the start_point and end_point of a given line (despite there being only 1 id, the other feature has its id set as NULL) – IKindaNeedAHand Aug 25 '21 at 15:39
  • OK, does this appear in all or just some cases? The expression is based on the overlapping of the point layers with a (small) buffer around the start_/end_points. If the buffer size is too large, both start_ and end_points are covered. Try to reduce the size of the buffer at the end of line 2 from 0.00001 to a smaller number. Does this solve the problem? Otherwise: can you post a screenshot or share a sample of your data for testing? – Babel Aug 25 '21 at 15:42
  • It did happen in most cases and there would be instances where the start_point and end_point for a given line are both snapped to 2 features from the same point layer and in that casem it would take the id of only one of them.

    Also reducing the size of the buffer doesn't seem to change the results

    – IKindaNeedAHand Aug 25 '21 at 16:12
  • 1
    Hi, I just wanted to say that your solution works perfectly, I wasn't using the expressions properly. Sorry for the confusion and thank you! – IKindaNeedAHand Aug 27 '21 at 09:44
  • Hello, I'm adding an update. Eventually, using your expression, I end up with the same result I mentioned in my previous comment (start_point and end_point snapped to features from the same layer) I end up with both start and end attributes receiving the same value, here's a screenshot: https://i.stack.imgur.com/APFyo.png – IKindaNeedAHand Sep 29 '21 at 11:42
  • OK, in two cases (3/1 and 2/4) it seems to work correct. What happened in the other cases is impossible to say without inspecting your data/project: can you share it? What attribute do you access with the expression and what values does in contain? – Babel Sep 29 '21 at 12:18
  • it was merely a sample project, 2 point layers with 2 features each with an ID field (1,2,3,4 which is what is being accessed by the expression) and a line layer which has 2 fields "start" and "end". I used the exact expression you provided to me and used start_point for start field attribute and end_point for end field. – IKindaNeedAHand Sep 29 '21 at 14:40
  • within( array_first (overlay_touches( 'points1',$geometry)), buffer (start_point ($geometry), 0.00001)), array_first (overlay_touches( 'points1',$id)), array_first (overlay_touches( 'points2',$id)))``` (and end_point version) – IKindaNeedAHand Sep 29 '21 at 14:49
  • See updated answer – Babel Sep 29 '21 at 21:27
  • Hello, I am trying to use basic QGIS as much as possible for reusability which means avoiding SQL queries or multiple operations. For instance, I need the expression for the sake using it as a QgsExpression variable to use as part of function in a plugin. I am sorry I should have made this clear in my initial post. – IKindaNeedAHand Sep 30 '21 at 13:19
  • Replacing array_first for array_last for the end_point seems to do the trick as it takes the last point of the vertices' list from a given line but I'm not sure it would in every instance.

    if (within( array_first (overlay_touches( 'points1',$geometry)), buffer (end_point ($geometry), 0.00001)), array_first (overlay_touches( 'points1',"id")),array_last (overlay_touches( 'points2',"id")))

    – IKindaNeedAHand Sep 30 '21 at 13:45