19

I have a layer in QGIS, and I want to duplicate it through a plugin so I can use the copy of it as I want, without modifying the original.

Of course layer2 = layer1 will not work, because everything that happens to layer2 will also happen to layer1, as it is the same object behind all this.

The only way I found to do it is as such:

QgsVectorFileWriter.writeAsVectorFormat(layer1, r"C:\Users\ABC\AppData\Local\Temp\NewLayer.shp", "utf-8", None, "ESRI Shapefile")
layer2 = QgsVectorLayer("C:\Users\ABC\AppData\Local\Temp\NewLayer.shp", "New vector", "ogr")
#do something with layer2

Is there a simple way to duplicate the layer in memory, without having to write a new file?

3 Answers 3

22

In QGIS 3 you can make a copy of a layer without saving any reference to the parent layer in this way:

layer.selectAll()
clone_layer = processing.run("native:saveselectedfeatures", {'INPUT': layer, 'OUTPUT': 'memory:'})['OUTPUT']
layer.removeSelection()
QgsProject.instance().addMapLayer(clone_layer)

The QgsVectorLayer class has a clone() method that allows you to clone the layer in a new layer, the problem is that if you modify the geometry in the cloned layer the original layer will be affected: the reason for this is that the data source is the same for the original layer and the cloned layer.

2
  • 1
    Work great. Just wanted to add that with layer.removeSelection() the Selection can removed again after the cloning and with QgsProject.instance().addMapLayer(clone_layer) the cloned layer can be added to the map
    – Jonathan
    Oct 15, 2020 at 16:49
  • Great @Jonathan. I've already updated the answer. Oct 15, 2020 at 17:10
20

The following code works for me from both the Python Console and plugin. It takes the features from the source input layer and copies the attributes to a memory layer (in this case, a polygon layer but you can change it to LineString or Point depending on layer type):

layer = QgsVectorLayer("path/to/layer", "polygon", "ogr")
feats = [feat for feat in layer.getFeatures()]

mem_layer = QgsVectorLayer("Polygon?crs=epsg:4326", "duplicated_layer", "memory")

mem_layer_data = mem_layer.dataProvider()
attr = layer.dataProvider().fields().toList()
mem_layer_data.addAttributes(attr)
mem_layer.updateFields()
mem_layer_data.addFeatures(feats)

QgsMapLayerRegistry.instance().addMapLayer(mem_layer)
2
  • 1
    Alright I knew this one, I just find it a bit long (in terms of lines of code). But I guess it is the only way to do it ; thanks for the answer :)
    – Ril8772
    Aug 8, 2016 at 8:42
  • 1
    @Ril8772 - Most welcome! Not sure if it is the only way but it's the only way I know =)
    – Joseph
    Aug 8, 2016 at 8:48
5

Another approach is to use the materialize() method from the QgsFeatureSource class. It is available since QGIS 3.0.

Over the entire layer with the allFeatureIds() method:

#choose a layer
layer = iface.activeLayer()
#or with: layer = QgsMapLayerRegistry.instance().mapLayersByName('name_of_the_layer')[0]
#create a new layer from all features   
new_layer = layer.materialize(QgsFeatureRequest().setFilterFids(layer.allFeatureIds()))
#add a new layer to the map
QgsProject.instance().addMapLayer(new_layer)

Over the selection of all features with the selectedFeatureIds() method from the QgsVectorLayer class:

#choose a layer
layer = iface.activeLayer()
#or with: layer = QgsMapLayerRegistry.instance().mapLayersByName('name_of_the_layer')[0]
#select all features in the original layer
layer.selectAll()
#create a new layer from selected features
new_layer = layer.materialize(QgsFeatureRequest().setFilterFids(layer.selectedFeatureIds()))
#remove selection in the original layer
layer.removeSelection()
#add a new layer to the map  
QgsProject.instance().addMapLayer(new_layer)

References:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.