15

Using native QGIS layer property definitions, I have successfully labelled each polygon in my layer with its area and perimeter.

Never satisfied, I now wish to label each side of each polygon with its length.

It would seem to be a common-enough requirement but I'm not getting good hits.

Taras
  • 32,823
  • 4
  • 66
  • 137
Bad Loser
  • 439
  • 6
  • 12

3 Answers3

22

Dec 2020 update

The method posted in this Twitter link included in the original version of this reply using nodes_to_points() doesn't render properly in QGIS 3.12+.

So I have rewritten this reply. You can, it turns out, just use segments_to_lines(). The method below works in QGIS 3.6 onwards.

Method

  1. Create a geometry generator symbology (geometry type LineString/MultiLineString) with the following expression:

segments_to_lines(force_rhr($geometry))
  1. Create a Marker Line symbology under #1. Set marker placement to "on central point", line offset to -5 millimetres (or as preferred), and set marker symbology to 'Font marker'

  2. Under 'Font marker' go 'Character', select the data-defined override > Edit and enter the following expression

format_number(length(geometry_n($geometry,@geometry_part_num)),1)||'m'
  1. Optional - To prevent upside down labels, disable 'Rotate marker to follow line direction' under the Marker Line symbology (QGIS 3.10 onwards) and use the following data defined override for Rotation under the Font Marker symbology

CASE WHEN azimuth(
        start_point(geometry_n($geometry,@geometry_part_num)),
        end_point(geometry_n($geometry,@geometry_part_num))
    )> pi()  THEN degrees(azimuth(
        start_point(geometry_n($geometry,@geometry_part_num)),
        end_point(geometry_n($geometry,@geometry_part_num))
    ))+90+ @map_rotation 
    ELSE
    degrees(azimuth(
        start_point(geometry_n($geometry,@geometry_part_num)),
        end_point(geometry_n($geometry,@geometry_part_num))
    ))-90+ @map_rotation 
    END

enter image description here

You can also download and import the 'Polygon_Dimension_QGIS3x' xml style from QGIS Style Hub as a base with the CAD-style dimension styling, but I recommend changing the geometry generator to include force_rhr()

QGIS 2.18 - 3.6

The style can also work in QGIS 2.18, however you need to remove force_rhr() from the above expressions and ensure all your polygons observe the right hand rule.

See example screenshot from QGIS 2.18 (with polygons observing RHR) which is how it looks like in QGIS 3.6 onwards (regardless of polygon orientation) with additional CAD dimension styling.

enter image description here

she_weeds
  • 12,488
  • 1
  • 28
  • 58
  • Do you know why it doesn't work in the Geometry Generator inside Labels->Placement? – Felippe M. Feb 03 '21 at 19:35
  • 1
    Because each individual side is not its own geometry so the label engine can't calculate the length of each side and put that value in the right location. You can use Labels->Placement to have a label placed on every side of the polygon of course, but the label text won't dynamically change. The only other work around afaik is to use Rule-based Labels to manually set up a label for every potential side. Which is hugely time consuming – she_weeds Feb 04 '21 at 06:19
  • Thanks a lot for the detailed explanation. Works like a charm. Is there a ways to disable the labeling of segments that shorter than X meters? I am labeling buildings and some side of the builds are quite short and then there are too many labels and it is get to crowded. – Corninski Feb 07 '22 at 14:32
  • @Cominski this is a very late reply but you can use length(geometry_n($geometry,@geometry_part_num)) > X (replace X with your desired length) at 'Enable symbol layer' data defined override for all the 'Marker Line' symbology. I will update this reply eventually as I think with newer versions of QGIS the font marker rotation no longer needs an expression – she_weeds Dec 05 '22 at 00:48
  • @she_weeds for me all values in a polygon lengths are 0 with QGis 3.30. Do you know any fix for this? – Hedge Apr 16 '23 at 22:06
15

The "networks" plugin in QGIS has a facility to split polygons into individual line segments which you can then label with length. I discovered this by trying various search terms in the plugin installer menu. Searching for "split" there did the trick. Install "networks" from the plugins menu.

First convert your polygons to lines using the standard "Vector -> Geometry Tools -> Polygons to Lines" menu option, creating a new shapefile and layer in the process. This won't touch your polygons.

Then select the new lines layer, turn editing on via the pencil icon, and select all the features, then run the "Vector -> Networks -> Split" item. Now you have many line segments! Turn editing off, and save the changes.

Turn on labelling, and use $length for the label. There were too many decimals when I first did this, so I used substr($length, 1, 4) to get this:

labelled line segment lengths

You could also use round($length, 2) for the same effect, but if your line lengths are in thousands or millions of units then you'll still get long labels. You could scale them by 1000000 in the labelling expression, but you will have difficulty getting consistent length labels if your line lengths span several orders of magnitude. It is possible to write a custom python function for formatting numbers in scientific notation to a constant width, and I've just done that and it looks okay.

If you give your line layer an invisible line style then you'll just get your polygon styling with labels on the edges.

enter image description here

Note the 0.37 and 0.10 on the upper left feature seem to be labelling the same side, but that's because there's a node splitting that line. If this is a problem with your case then I suspect a generalisation with a very small, or even zero, tolerance should be enough to remove nodes where you have perfectly straight sides defined by more than one line segment. You can map the nodes of your polygons by using "Vector -> Geometry Tools -> Extract Nodes" to create a point shapefile if you can't see where they are.

Spacedman
  • 63,755
  • 5
  • 81
  • 115
6

If you want to automate that, it can all be done with Processing.

1) Convert to lines using the "Polygons to lines" algorithm

2) split single segments using the "Explode lines" algorithm

Style your layer as explained above, and save the style as a qml file

3) apply the style using the "Set style for vector layer" and the style that you just saved

Now if you put the three steps above in a model using the Processing modeler, you can create a new algorithm that takes a polygon layer and generates your labels in a single step

It should look something like this

enter image description here

Victor Olaya
  • 679
  • 4
  • 4
  • I really like the Processing Modeller approach but it is causing me severe grief. According to this [wiki] (https://docs.qgis.org/2.6/en/docs/user_manual/processing_algs/qgis/vector_general_tools/setstyleforvectorlayer.html) the Set style for vector layer algorithm should have a definable output Styled layer. The dialog is allowing me to define the parameters Vector layer and Style file but that is all. Consequently the styling is never rendered and nor is it possible to follow the algorithm with another. Do you see the results of the styling on your canvas? – Bad Loser Oct 08 '15 at 07:57
  • Will open this as a new question in order to easily provide further data. – Bad Loser Oct 08 '15 at 20:40
  • New question link: http://gis.stackexchange.com/questions/165888/qgis-modeler-where-is-output-from-algorithm-set-style-for-vector-layer – Bad Loser Oct 08 '15 at 21:54
  • hmm, it works fine here, but you are right that this is a strange case, since the output is hidden. Reason for this is that the output is the input itself, not a different file, so the UI should not let the user select the output filename. – Victor Olaya Oct 09 '15 at 10:50