Skip to content

Commit

Permalink
Fix cloning a vector layer loses all features if the layer is a memor…
Browse files Browse the repository at this point in the history
…y layer
  • Loading branch information
nyalldawson committed Jul 1, 2019
1 parent e9fbe3a commit 0c6dc87
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 0 deletions.
8 changes: 8 additions & 0 deletions python/core/auto_generated/qgsvectordataprovider.sip.in
Expand Up @@ -605,6 +605,14 @@ Returns ``True`` if the data source has metadata, ``False`` otherwise.
:return: ``True`` if data source has metadata, ``False`` otherwise.

.. versionadded:: 3.0
%End

virtual void handlePostCloneOperations( QgsVectorDataProvider *source );
%Docstring
Handles any post-clone operations required after this vector data provider was cloned
from the ``source`` provider.

.. versionadded:: 3.8.1
%End

signals:
Expand Down
11 changes: 11 additions & 0 deletions src/core/providers/memory/qgsmemoryprovider.cpp
Expand Up @@ -359,6 +359,17 @@ QgsCoordinateReferenceSystem QgsMemoryProvider::crs() const
return mCrs; // return default CRS
}

void QgsMemoryProvider::handlePostCloneOperations( QgsVectorDataProvider *source )
{
if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
{
// these properties aren't copied when cloning a memory provider by uri, so we need to do it manually
mFeatures = other->mFeatures;
mNextFeatureId = other->mNextFeatureId;
mExtent = other->mExtent;
}
}


bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist, Flags )
{
Expand Down
1 change: 1 addition & 0 deletions src/core/providers/memory/qgsmemoryprovider.h
Expand Up @@ -78,6 +78,7 @@ class QgsMemoryProvider : public QgsVectorDataProvider
void updateExtents() override;
bool isValid() const override;
QgsCoordinateReferenceSystem crs() const override;
void handlePostCloneOperations( QgsVectorDataProvider *source ) override;

private:
// Coordinate reference system
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsvectordataprovider.cpp
Expand Up @@ -841,3 +841,8 @@ QList<QgsRelation> QgsVectorDataProvider::discoverRelations( const QgsVectorLaye
{
return QList<QgsRelation>();
}

void QgsVectorDataProvider::handlePostCloneOperations( QgsVectorDataProvider * )
{

}
8 changes: 8 additions & 0 deletions src/core/qgsvectordataprovider.h
Expand Up @@ -601,6 +601,14 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
*/
virtual bool hasMetadata() const { return true; }

/**
* Handles any post-clone operations required after this vector data provider was cloned
* from the \a source provider.
*
* \since QGIS 3.8.1
*/
virtual void handlePostCloneOperations( QgsVectorDataProvider *source );

signals:

/**
Expand Down
4 changes: 4 additions & 0 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -217,6 +217,10 @@ QgsVectorLayer *QgsVectorLayer::clone() const
options.transformContext = mDataProvider->transformContext();
}
QgsVectorLayer *layer = new QgsVectorLayer( source(), name(), mProviderKey, options );
if ( mDataProvider && layer->dataProvider() )
{
layer->dataProvider()->handlePostCloneOperations( mDataProvider );
}
QgsMapLayer::clone( layer );

QList<QgsVectorLayerJoinInfo> joins = vectorJoins();
Expand Down
3 changes: 3 additions & 0 deletions tests/src/python/provider_python.py
Expand Up @@ -422,3 +422,6 @@ def isValid(self):

def crs(self):
return self._crs

def handlePostCloneOperations(self, source):
self._features = source._features
11 changes: 11 additions & 0 deletions tests/src/python/providertestbase.py
Expand Up @@ -278,6 +278,17 @@ def testOpenIteratorAfterLayerRemoval(self):
pks.append(f['pk'])
self.assertEqual(set(pks), {1, 2, 3, 4, 5})

def testCloneLayer(self):
"""
Test that cloning layer works and has all expected features
"""
l = self.vl.clone()

pks = []
for f in l.getFeatures():
pks.append(f['pk'])
self.assertEqual(set(pks), {1, 2, 3, 4, 5})

def testGetFeaturesPolyFilterRectTests(self):
""" Test fetching features from a polygon layer with filter rect"""
try:
Expand Down
25 changes: 25 additions & 0 deletions tests/src/python/test_provider_memory.py
Expand Up @@ -226,6 +226,31 @@ def testAddFeatures(self):

assert compareWkt(str(geom.asWkt()), "Point (10 10)"), myMessage

def testClone(self):
"""
Test that cloning a memory layer also clones features
"""
vl = QgsVectorLayer(
'Point?crs=epsg:4326&field=f1:integer&field=f2:integer',
'test', 'memory')
self.assertTrue(vl.isValid())

f1 = QgsFeature()
f1.setAttributes([5, -200])
f2 = QgsFeature()
f2.setAttributes([3, 300])
f3 = QgsFeature()
f3.setAttributes([1, 100])
res, [f1, f2, f3] = vl.dataProvider().addFeatures([f1, f2, f3])
self.assertEqual(vl.featureCount(), 3)

vl2 = vl.clone()
self.assertEqual(vl2.featureCount(), 3)
features = [f for f in vl2.getFeatures()]
self.assertTrue([f for f in features if f['f1'] == 5])
self.assertTrue([f for f in features if f['f1'] == 3])
self.assertTrue([f for f in features if f['f1'] == 1])

def testGetFields(self):
layer = QgsVectorLayer("Point", "test", "memory")
provider = layer.dataProvider()
Expand Down

0 comments on commit 0c6dc87

Please sign in to comment.