5

I'm currently trying to compare species sample points vs their actual geographic range.

I have:

  • a points layer with species names and each species has multiple corresponding points/records
  • a polygon layer with the same species names showing the area each occupies

To avoid too many clicks from switching and toggling between vectors, I'd like to combine these layers so that when I select the species name, it shows both the points and polygon in one layer (e.g. third attached image).

Any suggestions? enter image description here

combined example

enter image description here

enter image description here

1
  • You could create "point polygons" by buffering the points layer, then merge these with your "regular" polygons into one layer. Symboblize point polys & regular polys differently, such as by color on a category value. You may need to control rendering to keep the point polys showing above the larger perhaps by transparency or by controlling rendering order in the symbology based on area. Other methods to that are here: gis.stackexchange.com/questions/199430/…
    – John
    Commented Nov 3, 2021 at 14:13

2 Answers 2

9

You can use pyqgis to 'link' the check state of category legend nodes for categorised layers by catching the styleChanged signal.

Copy the script below and paste it into an editor in the QGIS Python console. With this approach there are two layer name variables which need to match the names of your point and polygon layers. I used your layer names based on your comments below; just double check that they match. Click the Run button to connect the styleChanged signal of both your point and polygon layers to a slot function which will sync the visibility check state of the category child nodes.

from functools import partial

# The two lines below are the only ones you may need to edit    
lyr1_name = 'Occurrences'# This needs to match your points layer name
lyr2_name = 'IUCNRange'# This needs to match your polygon layer name

project = QgsProject.instance()
lyr1 = project.mapLayersByName(lyr1_name)[0]
lyr2 = project.mapLayersByName(lyr2_name)[0]

tree_model = iface.layerTreeView().layerTreeModel()

def on_change(v_layer):
    ltl = project.layerTreeRoot().findLayer(v_layer.id())
    lln = tree_model.layerLegendNodes(ltl)
    states = dict()
    for node in lln:
        sp_name = node.data(Qt.DisplayRole)
        check_state = node.data(Qt.CheckStateRole)
        states[sp_name] = check_state
    vector_layers = [l for l in QgsProject.instance().mapLayers().values() if l.type() == QgsMapLayer.VectorLayer]
    for layer in vector_layers:
        if layer.renderer().type() == 'categorizedSymbol':
            if not layer.id() == v_layer.id():
                ltl2 = project.layerTreeRoot().findLayer(layer.id())
                lln2 = tree_model.layerLegendNodes(ltl2)
                for n in lln2:
                    n_name = n.data(Qt.DisplayRole)
                    if n_name in states:
                        if n.data(Qt.CheckStateRole) != states[n_name]:
                            n.setData(states[n_name], Qt.CheckStateRole)
                    
conn1 = lyr1.styleChanged.connect(partial(on_change, lyr1))
conn2 = lyr2.styleChanged.connect(partial(on_change, lyr2))

See the short screencast below for expected results:

enter image description here

To disconnect the signal/slot connections, run the two lines below (you can simply run them in a different editor (see end of screencast above).

QObject.disconnect(conn1)
QObject.disconnect(conn2)

For reference I found a couple of similar questions/answers here and here. My answer incorporates some of the code presented therein.

8
  • This is great, thank you! I've copied and pasted your exact script into my Python Console - apart from adding "from qgis.utils import iface" in Line 1. However, I'm getting an error "Seems there is no valid script in the file." Any idea what's causing this? Do I need to set the "l" and "a" variables to match my layer names? I'm relatively new to Q and have yet to process using Python. My poly layer is 'IUCNRange' and my points is "Occurrences" and my project name is "OC".
    – Fben980
    Commented Nov 3, 2021 at 21:12
  • I also get the following error - "AttributeError: 'QgsLayerTreeModel' object has no attribute 'mapToSource' Traceback (most recent call last): File "", line 11, in on_change AttributeError: 'QgsLayerTreeModel' object has no attribute 'mapToSource'"
    – Fben980
    Commented Nov 3, 2021 at 21:59
  • I'm using "3.16.3-Hannover".
    – Fben980
    Commented Nov 3, 2021 at 22:11
  • thanks! I'm still getting error "NameError: name 'QgsProject' is not defined Traceback (most recent call last): File "", line 8, in was_changed NameError: name 'QgsProject' is not defined"
    – Fben980
    Commented Nov 3, 2021 at 22:30
  • 1
    @Fben980, I have updated my answer with a different approach which will work with the category nodes of your vector layers categorized by species. Hopefully you will have better luck with this approach!
    – Ben W
    Commented Nov 5, 2021 at 5:22
8

A layer can only contain one type of geometry: either points or polygons (or lines), but not both.

However, using Geometry generators, you can create symbolization (visualization, not actual geometries) of another geometry type.

enter image description here

Let's say you have a polygon layer named poly with an attribute polygon_species and a point layer point with an attribute point_species.

On the polygon layer, add a new symbol layer of type Geometry generator / Geometry type: point and insert this expression. When you then (manually) select a polygon, all the points containing the same attribute value are created as additional symbols on the polygon layer (see screenshot):

with_variable (
    'selected',
    attribute('polygon_species'),
    if (
        is_selected( ),
        aggregate( 
            'points',
            'collect',
            $geometry,
            filter:=point_species=@selected
        ),
        ''
    )
)

Polygon layer: blue, initial point layer: light blue dots; selected polygon (B) highlighted in yellow, points with the same attribute (B) highlighted in big red dots (style can be changed manually as you like. If you toggle visibility of the point layer, only the red B_points will be visible (as long as polygon B is selected): enter image description here

5
  • "One layer can only contain one type of geometry: either points or polygons (or lines), but not both." it is True when using the "Merge vector layers". What about the Geometry Collection then?
    – Taras
    Commented Nov 3, 2021 at 6:38
  • Which Geometry Collection do you mean / how to imply? If you create a layer, you don't have any option to but to stick to one type of geometry: i.sstatic.net/rTjKE.png
    – Babel
    Commented Nov 3, 2021 at 7:39
  • 1
    Bad me ... indeed QGIS is not ready for Geometry Collection
    – Taras
    Commented Nov 3, 2021 at 7:43
  • If you mean the "Collect geometries" algorithm, this accepts only one layer as input and does just collect single to multipart geometries (based on attribute values to group let's say all islands belonging to the same country together to a multipart geometry). The help says: "This algorithm takes a vector layer and collects its geometries into new multipart geometries. (...) All output geometries will be converted to multi geometries, even those with just a single part."
    – Babel
    Commented Nov 3, 2021 at 7:45
  • Geometry collection: maybe a feature request?
    – Babel
    Commented Nov 3, 2021 at 7:46

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.