I'm using QGIS

I've got a basemap with the Texas landgrid for all of the counties in the Permian Basin. There are various attributes associated with the sections in the grid, but I only need 2 to display (Section # & Abstract #). I can get them to display just fine, however they both display in the same location within the polygon, and they have the same font size no matter how far I am zoomed in or out of the map. I need "Section #" to display in the Northeastern most corner of the section and resize dynamically based on the size of the polygon, and Abstract to display in the Southeastern most corner with the same dynamic function.

Here is an example of how the attributes display in my basemap: ("S" is Section, "B" is Block & "A" is Abstract. I'll be getting rid of "B") QGIS Basemap Display

And here is an example from a software called DrillingInfo: (Notice the Section # is displayed in the NE corner, and the Abstract # is in the SE corner) enter image description here

I don't know much about Python, but based on my limited experience, I think it should be possible to manipulate and position these attributes using Python functions. Does anyone know of a way to do this, or if it is even worth my time trying to learn it?

My research has consisted of:

I've spoken with a couple people who have more experience using GIS software, and they have both told me they don't know of a way to do it. I've been taking a Python course at Codecademy.com to try and become familiar with the language. I've also read multiple "How To's" regarding labeling and positioning. I know there is a way to manually move each label into the position that I want, but I've got 1,000s of polygons that all need the same positioning with their labels. In other words, I have done some research before asking this question, and haven't found an answer. I also looked for other questions in this forum to see if this is a duplicate question, and I think I have unique attributes to my question. Sorry if it IS a duplicate!

2 Answers 2


I can only give you a partial answer here without Python, but if most of your geometries are regular then the following might work.

1. Rule-based Labeling

Firstly, to independently manage two separate labels, use Rule-based labeling. This lets you have separate labels for the same feature with settings independent of each other. Below I have two separate labels for the fields PFI and GeomArea.

enter image description here

2. Data-defined label coordinates and rotation

Secondly, you can set the X and Y coordinates and rotation of the labels relative to the feature geometry if you go to the Placement tab of the label and use a Data-Defined override in the Coordinate X, Coordinate Y and Rotation fields. Below is a screenshot of these settings in the PFI label I have set up above.

enter image description here

Now, for the expressions to use in the data-defined override.

3. Setting coordinates

Determining the top-right and bottom-left corner of the polygons wasn't straightforward - if anyone has a better approach please share, this probably isn't optimal.

I found the top-right and bottom-left corners of the bounding box of each polygon - which was often outside the polygon itself - then looked for the closest nodes on the polygon to those corners, and used those two locations as the "anchor point" for the respective labels.

Here is the X coordinate expression (for bottom left. For top right change x/y_min to x/y_max):


And the Y coordinate expression (for bottom left. For top right change x/y_min to x/y_max):


4. Setting rotation

This was more complex and convoluted as the label "anchor point" location would sometimes be on the very corner of the polygon, so the interpolated angle at that point would be really off sometimes.

So I would get the angle of a point just a little more "rightwards" along the "bottom line" of the polygon to make sure it was not at a corner.

To do so I had to

  1. make sure the polygon boundaries were all drawn in the same order using force_rhr()

  2. get the distance value of the "anchor point" location along the polygon boundary with line_locate_point()

  3. subtract -0.5 projected CRS units from that distance (plus and modulo the polygon boundary length - in case the anchor point is on or near the starting point of the polygon boundary)

  4. find the point along the polygon boundary with the distance from #3 - this is the "rightward point"

  5. get the angle of that point with line_interpolate_angle()

This is the expression I ended up using (for bottom left. For top right change x/y_min to x/y_max):

y_min(bounds($geometry)))))-0.5 + perimeter($geometry)) 
% perimeter($geometry))+90

Here are the results with some very regular polygons. It's not perfect when the bottom edge is all slanted, but it's a start.

enter image description here

Note the red and green dots are the "anchor points" and the yellow and blue dots are the points used for getting label rotation.

4. More effective application

Calculating all of the above on the fly will be very resource-intensive. So I suggest you create 4 fields in your data for each of the 4 values (top-right label anchor coords, top-right rotation, bottom-left label anchor coords, bottom-left rotation) using the above expressions in Field Calculator.

5. Further modification

To increase or decrease the "padding" you may have to add or subtract a couple of units from the X and Y coordinates appropriately. You might want this to be proportionate to the geometry area ($area).

As for the scalable size, explore using Map Units instead of Millimeters, and the Size Assistant (click the data-defined box next to Size when setting labels, and click Assistant...), and for Source try $area and use that to help scale your labels to the size of the polygon.

  • Brilliant! I had a go myself, I'd gotten as far as locating the anchor points, but was completely stumped by the rotation - I'm still trying to pick that apart to understand how the rotation point is created. And thanks for the rule based labelling tip to get two labels - no more duplicating layers for me!
    – M Bain
    Commented Jun 15, 2019 at 22:30
  • If you want to create the actual rotation point for visualisation like the above, just change line_interpolate_angle() to line_interpolate_point() and remove the +90 at the end, then use that expression in a geometry generator symbol for the polygon layer (remembering to change geometry generator type to point). Play around with the - 0.5 to see the point "walk" along the polygon boundary. That second parameter to line_interpolate_angle() - from line_locate_point() to the closing bracket - is just a distance value along the perimeter.
    – she_weeds
    Commented Jun 16, 2019 at 1:11
  • Thanks, I had to rewrite each instance of force_rhr(boundary($geometry)) as boundary(force_rhr($geometry)) - seems like force_rhr was returning the boundary LineString unchanged. I think there needs to be a closing bracket in the x and y expressions for the anchor point: x(closest_point(nodes_to_points($geometry ) ,make_point(x_min(bounds($geometry)), y_min(bounds($geometry)))))
    – M Bain
    Commented Jun 16, 2019 at 1:31
  • I've been messing around with this for the past couple days. It's close to what I'm wanting, but it puts the label outside the polygons. When I use x_max, it puts the label outside of the top right corner of the polygon, and when I use x_min, it puts it outside the bottom left. Commented Jun 18, 2019 at 13:57
  • Try messing around with the text quadrant? (The 9 squares you see in the first screenshot) I believe it still applies even if you have manually set the coordinates using data driven override
    – she_weeds
    Commented Jun 18, 2019 at 21:19

The easiest way to do this is to duplicate the layer and then set the brush and pen styles to none.

enter image description here

You can then have two sets of labels,and use data defined Coordinates to control the placement of them.

enter image description here

I used x_min( $geometry) and y_max( $geometry) for one and the centroid for the other.

enter image description here

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.