3

I would like to add a number label only to features visible in the Print layout extent. For example, I have a layer with building footprints, and I would like to itemize buildings with a simple numbering system (always starting at 1), and have them correspond to a table that contains additional attribute data.

Is this possible?

Taras
  • 32,823
  • 4
  • 66
  • 137
Neil
  • 31
  • 1

1 Answers1

2

You can use a combination of custom qgis functions, virtual layers and virtual fields to achieve your goal.

Below is an example workflow which you can follow. You will just need to change a few details to match your own, such as layout name, layer names etc.

To start with, I am using the Natural Earth populated places dataset for this example which I have loaded into a project. I have also created a new print layout called 'Test', and added a new map item to the layout.

The populated places layer is the full dataset we will be querying to create our virtual layer. However, we do not want to actually display this layer so, first of all, go into Layer Properties -> Symbology and change symbol type to No Symbols.

enter image description here

Next, we need to create our custom QgsFunction. To do this we need to open an Expression Builder dialog. This can be done from many places within QGIS by clicking the enter image description here icon. However, a quick, easy way to access this dialog is by using the Select By Expression icon enter image description here

With the Expression Builder dialog open, switch to the Function Editor tab. Use the green 'plus' button to create a new file, give it a name and paste in the following script (making sure to replace the layout name 'Test' with the name of your own layout):

from qgis.core import QgsProject, QgsGeometry, QgsLayoutItemMap

@qgsfunction(args='auto', group='Custom') def layoutMapExtent(feature, parent): lo = QgsProject.instance().layoutManager().layoutByName('Test') map_item = [i for i in lo.items() if isinstance(i, QgsLayoutItemMap)][0] map_geom = QgsGeometry().fromRect(map_item.extent()) return map_geom

Then click 'Save and Load Functions'

enter image description here

Now it's time to create a virtual layer via the menu options shown below:

enter image description here

In the virtual layer dialog, click 'Import' and select the layer you are working with. In my case, I select my Populated Places layer. Click OK.

enter image description here

Now, paste the following SQL query into the query window. Note: here you will need to change the layer name in the query to match yours! Also note that in the query we are using the custom function we created earlier to return the current extent of the map item in our print layout. You could also try a different geometric predicate for the query, so instead of st_intersects you could try st_within.

SELECT * 
FROM ne_10m_populated_places_simple
WHERE st_intersects(ne_10m_populated_places_simple.geometry, layoutMapExtent());

enter image description here

You can use the 'Test' button to check that there are no errors and, if all is well, click 'Add'.

Next, select the virtual layer in the TOC and open the Field Calculator:

enter image description here

Create a Virtual Field (I named it 'Label_No'). This field will dynamically number the features in the virtual layer depending on the extent of the associated layout map. Populate the field with the expression $id+1. This will ensure that the numbering starts from 1 instead of 0.

enter image description here

We can now use this virtual field to label the features in our virtual layer:

enter image description here

Now you can open your print layout and add an attribute table. In the screenshot below you can see that I have selected the virtual layer in the 'Layer' combo box. You can also see that I click the 'Attributes' button, highlight rows in the 'Columns' table and use the red 'Minus' button to remove columns I don't want in order to make the layout table a more manageable size.

enter image description here

The short screencast below shows the final result. As you drag (or zoom) the contents of the layout map, the features shown should be dynamically numbered starting from 1. Note that to update the attribute table, you need to select it then under Item properties -> Main properties click 'Refresh Table Data'.

enter image description here

Acknowledgement: I adapted the custom function and SQL query in my answer from @JGH's answer to this question.

Ben W
  • 21,426
  • 3
  • 15
  • 39