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?

MrXsquared
  • 34,292
  • 21
  • 67
  • 117
Ril8772
  • 375
  • 2
  • 6

3 Answers3

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.

Taras
  • 32,823
  • 4
  • 66
  • 137
Leo Cardona
  • 1,363
  • 10
  • 14
  • 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 '20 at 16:49
  • Great @Jonathan. I've already updated the answer. – Leo Cardona Oct 15 '20 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)

Taras
  • 32,823
  • 4
  • 66
  • 137
Joseph
  • 75,746
  • 7
  • 171
  • 282
  • 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 08 '16 at 08:42
  • 1
    @Ril8772 - Most welcome! Not sure if it is the only way but it's the only way I know =) – Joseph Aug 08 '16 at 08:48
6

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:

MrXsquared
  • 34,292
  • 21
  • 67
  • 117
Taras
  • 32,823
  • 4
  • 66
  • 137