1

I am tracking progress made each day along a path, so that I can show in QGIS each day's progress in a different colour with a label showing the day number and distance covered.

However in this case each line segment is a separate feature (required in order to label and colour the line segments differently using categories) so I am not sure how to apply a global label that contains the total distance of all the segments combined.

It is also difficult because using the LineString geometry and having a separate feature for each segment means they don't exactly start at the same point as the previous segment finished, depending on how accurate my mouse clicks are or how willing I am to copy and paste coordinates when editing the vertices.

Is there a way to record this geometry such that it can be treated as a single feature with only one long line, but also to colour and label the individual segments differently, based on an arbitrary value like the day the work was performed?

I do not mind having to change the geometry of the layer to make this work, but I don't want to have to do any double data entry or otherwise synchronise between two copies of the data each time I add a new segment.

I looked at the MultiLine geometry but this looks like I can have unconnected line segments all sharing a single label which is the opposite of what I'm after - I need only connected line segments each of which has a different label, but where I can provide a global label (and length measurement) to the entire line as well.

Is there any way to achieve this?

2 Answers 2

4

For the problem that each segment don't exactly start at the same point as the previous segment finished that is easily solved by using snapping.

Then for getting the total length while keeping each part a different color I suggest using a virtual layer that will union all geometry and give you the total length. As virtual layer are dynamic (they are based on an SQL query) it will recalculate each time you modify your original line layer (so you do have two layer but without having to do any double data entry or otherwise synchronise between two copies of the data each time you add a new segment)

The SQL query for the virtual layer could be something like this :

Select
    Count(NAME)              as 'NUMBER_OF_SEGMENT'
  , sum(st_length(geometry)) as 'TOTAL_DISTANCE'
  , st_union(geometry)       as geometry
from
    LINE_LAYER

(Change NAME by any attribute column from your line layer and LINE_LAYER by the actual name of your line layer)

Below you can see the result with the colored individual segment and on top the black virtual layer

enter image description here

3

An option that requires no new layers is to use a Geometry Generator expression to generate the total line for labelling, as well as its length.

To set this up, under Labels, enable Rule-based labeling, with two labels:

enter image description here

Global label

The first label is the Global label, which uses collect to aggregate all of the individual features into one for measuring the length, via the following formula for the label:

'Total length: ' || round(length(collect( $geometry, group_by:=overlay_nearest(@layer, $geometry))),2)

Under Placement, you also need to enable Geometry Generator:

enter image description here

This uses a cut down version of the formula above:

collect(
$geometry,
group_by:=overlay_nearest(@layer, $geometry))

This will create one label for each feature (4 in the example). This can be restricted to 1 label total by using a generic filter, which labels the feature with the lowest ID only:

to_string(@id) = 
array_min(
    string_to_array(
        aggregate(
            @layer,
            aggregate:='concatenate',
            expression:=to_string(@id),
            concatenator:=','),
        delimiter:=',')
    )

Individual label

The second label is the Individual label for each segment, which just uses the formula:

'Length: ' || round($length,2)

Final result is as follows:

enter image description here

As mentioned elsewhere, use Snapping to ensure features start/finish at the same points.

8
  • This looks promising except that length() returns e.g. 0.0075 where $length for the same feature returns 80. It looks like the two functions calculate the length quite differently, and $length is the one that produces the correct answer for me. So using your formula the total length of the line comes out as a very small number, which gets rounded to 0. Any idea how to get it to match the $length values for each feature?
    – Malvineous
    Commented Oct 19, 2023 at 14:36
  • 1
    I assume you are using a Geographic co-ordinate system? 0.0075 is most likely in degrees (not metres or whatever units you are using). You should use a Projected co-ordinate system eg UTM for your area, if you are only interested in XY distance. You'd probably want to change all the formulas to length() for consistency. Commented Oct 19, 2023 at 21:49
  • Ah, my apologies. I was using a GCS (GDA2020) and I tried switching to a projected one (GDA2020 / BCSG2020) but it didn't work. It turns out I had to reproject the layer via the processing toolbox (then export/overwrite it). Just adjusting assigned CRS in the layer properties to see if it would make a difference, was not enough to fix the length() calculations - it required a full reprojection.
    – Malvineous
    Commented Oct 20, 2023 at 9:07
  • 1
    I worked out what was happening. The global label is still trying to be displayed once per feature, so with six features (line segments) I get six "total" labels. In your example I think there was no room for the extra 'total' labels to be seen. I changed my layer so that I had an "order" field (because I wanted to make odd and even segments different colours) so I added a filter to the global label rule saying "order"=1 which meant it would only try to draw a single label for each group of line segments. This solved the problem and I only have one 'total' label now.
    – Malvineous
    Commented Oct 20, 2023 at 9:36
  • 1
    You are right that some global labels were simply not being shown. I've added a generic filter solution for the global label issue that will work for any layer, not just one that has an order field. Commented Oct 22, 2023 at 2:50

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.