Skip to content

Commit

Permalink
Add utility QgsMapLayerUtils.updateLayerSourcePath for easy replacement
Browse files Browse the repository at this point in the history
of file paths in the source of a file-based layer
  • Loading branch information
nyalldawson committed Aug 6, 2021
1 parent baa9062 commit a31a674
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 0 deletions.
11 changes: 11 additions & 0 deletions python/core/auto_generated/qgsmaplayerutils.sip.in
Expand Up @@ -46,6 +46,17 @@ This method can be used to test whether a layer is associated with a file path.
.. versionadded:: 3.22
%End

static bool updateLayerSourcePath( QgsMapLayer *layer, const QString &newPath );
%Docstring
Updates a ``layer``'s data source, replacing its data source with a path refering to ``newPath``.

Returns ``True`` if the layer was updated, or ``False`` if the layer was not updated (e.g. it
uses a data provider which does not specify paths in a layer URI.

.. versionadded:: 3.22
%End


};


Expand Down
15 changes: 15 additions & 0 deletions src/core/qgsmaplayerutils.cpp
Expand Up @@ -121,3 +121,18 @@ bool QgsMapLayerUtils::layerSourceMatchesPath( const QgsMapLayer *layer, const Q
const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
return parts.value( QStringLiteral( "path" ) ).toString() == path;
}

bool QgsMapLayerUtils::updateLayerSourcePath( QgsMapLayer *layer, const QString &newPath )
{
if ( !layer || newPath.isEmpty() )
return false;

QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
if ( !parts.contains( QStringLiteral( "path" ) ) )
return false;

parts.insert( QStringLiteral( "path" ), newPath );
const QString newUri = QgsProviderRegistry::instance()->encodeUri( layer->providerType(), parts );
layer->setDataSource( newUri, layer->name(), layer->providerType() );
return true;
}
11 changes: 11 additions & 0 deletions src/core/qgsmaplayerutils.h
Expand Up @@ -60,6 +60,17 @@ class CORE_EXPORT QgsMapLayerUtils
*/
static bool layerSourceMatchesPath( const QgsMapLayer *layer, const QString &path );

/**
* Updates a \a layer's data source, replacing its data source with a path refering to \a newPath.
*
* Returns TRUE if the layer was updated, or FALSE if the layer was not updated (e.g. it
* uses a data provider which does not specify paths in a layer URI.
*
* \since QGIS 3.22
*/
static bool updateLayerSourcePath( QgsMapLayer *layer, const QString &newPath );


};

#endif // QGSMAPLAYERUTILS_H
Expand Down
32 changes: 32 additions & 0 deletions tests/src/python/test_qgsmaplayerutils.py
Expand Up @@ -91,6 +91,38 @@ def test_layerSourceMatchesPath(self):
self.assertFalse(QgsMapLayerUtils.layerSourceMatchesPath(rl, 'aaaaa'))
self.assertTrue(QgsMapLayerUtils.layerSourceMatchesPath(rl, unitTestDataPath() + '/mixed_layers.gpkg'))

def test_updateLayerSourcePath(self):
"""
Test QgsMapLayerUtils.updateLayerSourcePath()
"""
self.assertFalse(QgsMapLayerUtils.updateLayerSourcePath(None, ''))
self.assertFalse(QgsMapLayerUtils.updateLayerSourcePath(None, 'aaaaa'))

# shapefile
layer1 = QgsVectorLayer(unitTestDataPath() + '/points.shp', 'l1')
self.assertTrue(QgsMapLayerUtils.updateLayerSourcePath(layer1, unitTestDataPath() + '/points22.shp'))
self.assertEqual(layer1.source(), unitTestDataPath() + '/points22.shp')

# geopackage with layers
layer1 = QgsVectorLayer(unitTestDataPath() + '/mixed_layers.gpkg|layername=lines', 'l1')
self.assertTrue(QgsMapLayerUtils.updateLayerSourcePath(layer1, unitTestDataPath() + '/mixed_layers22.gpkg'))
self.assertEqual(layer1.source(), unitTestDataPath() + '/mixed_layers22.gpkg|layername=lines')
layer2 = QgsVectorLayer(unitTestDataPath() + '/mixed_layers.gpkg|layername=points', 'l1')
self.assertTrue(QgsMapLayerUtils.updateLayerSourcePath(layer2, unitTestDataPath() + '/mixed_layers22.gpkg'))
self.assertEqual(layer2.source(), unitTestDataPath() + '/mixed_layers22.gpkg|layername=points')

# raster layer from gpkg
rl = QgsRasterLayer(f'GPKG:{unitTestDataPath()}/mixed_layers.gpkg:band1')
self.assertTrue(QgsMapLayerUtils.updateLayerSourcePath(rl, unitTestDataPath() + '/mixed_layers22.gpkg'))
self.assertEqual(rl.source(), f'GPKG:{unitTestDataPath()}/mixed_layers22.gpkg:band1')

# a layer from a provider which doesn't use file based paths
layer = QgsVectorLayer("Point?field=x:string", 'my layer', "memory")
old_source = layer.source()
self.assertTrue(layer.isValid())
self.assertFalse(QgsMapLayerUtils.updateLayerSourcePath(layer, unitTestDataPath() + '/mixed_layers22.gpkg'))
self.assertEqual(layer.source(), old_source)


if __name__ == '__main__':
unittest.main()

0 comments on commit a31a674

Please sign in to comment.