2

I would like to duplicate a layer and apply a filter on the new layer that contains only selected features.

Requirements are:

  • using a memory layer is not an option, I want to keep the original datasource
  • I want to use a filter, so later changes should not be an issue

How can I use setSubsetString() to execute a query that refers to the internal fid obtained from feature.id()?

Here is my attempt (which doesn't work, because obviously there is no column named "fid"):

lyr = self.cb_layer.currentLayer()
        
# copy layer and apply style
iface.setActiveLayer(lyr)
iface.actionCopyLayerStyle().trigger()
lyr_copy = iface.addVectorLayer(lyr.source(), lyr.name() + '_' + str('temp'), lyr.providerType())
iface.actionPasteLayerStyle().trigger()
        
# create list with ids from selected features 
sel_f = lyr.selectedFeatures()
l = []
for f in sel_f:
   l.append(f.id())
            
query = f"fid in ({tuple(l)})"
         
# Filter
lyr_copy.setSubsetString(query)

Is there an alternative method to setSubsetString() to filter features by the internal id set by QGIS?

2
  • I think the syntax with $id is used inside the expression builder, not in in the query builder. Correct me if I'm wrong, but it doesn't seem to work. I tried it using the query builder inserting $id IN(20160480) (which is a valid id) and I got a syntax error.
    – pascatl
    Commented Jan 27, 2022 at 17:25
  • 2
    This notation won't work in the query builder because it hands plain sql to the data provider, see answers to gis.stackexchange.com/questions/228967/… Commented Jan 28, 2022 at 8:22

1 Answer 1

4

Perhaps the strategy is promising to first obtain the unique identifier column for your source layer (on the data provider side) and then filter for the attribute values in this column, in your example:

lyr = self.cb_layer.currentLayer()
# get the key column
key_col = lyr.dataProvider().uri().keyColumn()

if not key_col == '':
    # copy layer and apply style
    iface.setActiveLayer(lyr)
    iface.actionCopyLayerStyle().trigger()
    lyr_copy = iface.addVectorLayer(lyr.source(), lyr.name() + '_' + str('temp'), lyr.providerType())
    iface.actionPasteLayerStyle().trigger()
        
    # create list with ids from selected features 
    sel_f = lyr.selectedFeatures()
    l = []
    for f in sel_f:
        l.append(f[key_col])
    # shorter version to save lines ;-)
    # l = [f[key_col] for f in lyr.selectedFeatures()]

query = f"{key_col} in {tuple(l)}" #leave away the roud brackets around the tuple (cp. below)!
         
# Filter
lyr_copy.setSubsetString(query)

Note(1) that this will only work for layers the have a unique identifier column at all.

Note(2) f"id in ({tuple(...)})" -> "id in ((...))" which is a syntax error.

4
  • 1
    Erlangen helps Erlangen @_@
    – Taras
    Commented Jan 28, 2022 at 8:15
  • 1
    corona requires new communication channels... ;-) Commented Jan 28, 2022 at 8:27
  • ok, back to our topic here: your suggestion works, but with the restriction that a key column must be present. To circumvent this case, one could search the layer for attribute fields that are both unique and complete. However, this seems to me to be more of a workaround. It would still be more elegant to be able to fall back on an internal ID or something similar.
    – pascatl
    Commented Jan 28, 2022 at 11:15
  • considered to provide your solution as another answer? Commented Jan 31, 2022 at 7:17

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.