Skip to content
Permalink
Browse files
[ogr] Fallback to writing metadata as a QMD file for non-geopackage,
file based uris, and raise QgsNotSupportedException for non-file
based uris
  • Loading branch information
nyalldawson committed May 4, 2021
1 parent 667fe63 commit 4cbec54c59efe99347251a5fc228aa2db1866814
Showing with 114 additions and 24 deletions.
  1. +42 −23 src/core/providers/ogr/qgsogrprovider.cpp
  2. +72 −1 tests/src/python/test_provider_ogr.py
@@ -7323,8 +7323,27 @@ bool QgsOgrProviderMetadata::saveLayerMetadata( const QString &uri, const QgsLay
{
const QVariantMap parts = decodeUri( uri );
const QString path = parts.value( QStringLiteral( "path" ) ).toString();
if ( !path.isEmpty() )
if ( !path.isEmpty() && QFileInfo::exists( path ) )
{
// export metadata to XML
QDomImplementation domImplementation;
QDomDocumentType documentType = domImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
QDomDocument document( documentType );

QDomElement rootNode = document.createElement( QStringLiteral( "qgis" ) );
rootNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
document.appendChild( rootNode );

if ( !metadata.writeMetadataXml( rootNode, document ) )
{
errorMessage = QObject::tr( "Error exporting metadata to XML" );
return false;
}

QString metadataXml;
QTextStream textStream( &metadataXml );
document.save( textStream, 2 );

QFileInfo fi( path );
if ( fi.suffix().compare( QLatin1String( "gpkg" ), Qt::CaseInsensitive ) == 0 )
{
@@ -7353,25 +7372,6 @@ bool QgsOgrProviderMetadata::saveLayerMetadata( const QString &uri, const QgsLay
// creating all the metadata tables to GDAL!
if ( GDALSetMetadataItem( hLayer, "QGIS_VERSION", Qgis::version().toLocal8Bit().constData(), nullptr ) == CE_None )
{
// export metadata to XML
QDomImplementation domImplementation;
QDomDocumentType documentType = domImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
QDomDocument document( documentType );

QDomElement rootNode = document.createElement( QStringLiteral( "qgis" ) );
rootNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
document.appendChild( rootNode );

if ( !metadata.writeMetadataXml( rootNode, document ) )
{
errorMessage = QObject::tr( "Error exporting metadata to XML" );
return false;
}

QString metadataXml;
QTextStream textStream( &metadataXml );
document.save( textStream, 2 );

// so far so good, ready to throw the whole of the QGIS layer XML into the metadata table!

// first we need to check if there's already a corresponding entry in gpkg_metadata -- if so, we need to update it.
@@ -7459,11 +7459,30 @@ bool QgsOgrProviderMetadata::saveLayerMetadata( const QString &uri, const QgsLay
return false;
}
}
else
{
// file based, but not a geopackage -- store as .qmd sidecar file instead
// (possibly there's other formats outside of GPKG which also has some native means of storing metadata,
// which could be added for those formats before we resort to the sidecar approach!)
const QString qmdFileName = fi.dir().filePath( fi.completeBaseName() + QStringLiteral( ".qmd" ) );
QFile qmdFile( qmdFileName );
if ( qmdFile.open( QFile::WriteOnly | QFile::Truncate ) )
{
QTextStream fileStream( &qmdFile );
fileStream << metadataXml;
qmdFile.close();
return true;
}
else
{
errorMessage = tr( "ERROR: Failed to created default metadata file as %1. Check file permissions and retry." ).arg( qmdFileName );
return false;
}
}
}
errorMessage = QObject::tr( "Storing metadata for the specified uri is not supported" );
return false;
}

throw QgsNotSupportedException( QObject::tr( "Storing metadata for the specified uri is not supported" ) );
}

// ---------------------------------------------------------------------------

@@ -19,6 +19,8 @@

from osgeo import gdal, ogr # NOQA
from qgis.PyQt.QtCore import QVariant, QByteArray, QTemporaryDir
from qgis.PyQt.QtXml import QDomDocument

from qgis.core import (
NULL,
QgsAuthMethodConfig,
@@ -38,7 +40,9 @@
QgsVectorLayer,
QgsVectorFileWriter,
QgsWkbTypes,
QgsNetworkAccessManager
QgsNetworkAccessManager,
QgsLayerMetadata,
QgsNotSupportedException
)
from qgis.testing import start_app, unittest
from qgis.utils import spatialite_connect
@@ -1200,6 +1204,73 @@ def testShapefilesWithNoAttributes(self):
vl = QgsVectorLayer(os.path.join(d.path(), 'writetest.shp'))
self.assertEqual(vl.featureCount(), 1)

def testNonGeopackageSaveMetadata(self):
"""
Save layer metadata for a file-based format which doesn't have native metadata support.
In this case we should resort to a sidecar file instead.
"""
ml = QgsVectorLayer('Point?crs=epsg:4326&field=pk:integer&field=cnt:int8', 'test', 'memory')
self.assertTrue(ml.isValid())

d = QTemporaryDir()
options = QgsVectorFileWriter.SaveVectorOptions()
options.driverName = 'ESRI Shapefile'
options.layerName = 'metadatatest'
err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'metadatatest.shp'),
QgsCoordinateTransformContext(), options)
self.assertEqual(err, QgsVectorFileWriter.NoError)
self.assertTrue(os.path.isfile(os.path.join(d.path(), 'metadatatest.shp')))

uri = d.path() + '/metadatatest.shp'

# now save some metadata
metadata = QgsLayerMetadata()
metadata.setAbstract('my abstract')
metadata.setIdentifier('my identifier')
metadata.setLicenses(['l1', 'l2'])
ok, err = QgsProviderRegistry.instance().saveLayerMetadata('ogr', uri, metadata)
self.assertTrue(ok)

self.assertTrue(os.path.exists(os.path.join(d.path(), 'metadatatest.qmd')))
with open(os.path.join(d.path(), 'metadatatest.qmd'), 'rt') as f:
metadata_xml = ''.join(f.readlines())

metadata2 = QgsLayerMetadata()
doc = QDomDocument()
doc.setContent(metadata_xml)
self.assertTrue(metadata2.readMetadataXml(doc.documentElement()))
self.assertEqual(metadata2.abstract(), 'my abstract')
self.assertEqual(metadata2.identifier(), 'my identifier')
self.assertEqual(metadata2.licenses(), ['l1', 'l2'])

# try updating existing metadata -- file should be overwritten
metadata2.setAbstract('my abstract 2')
metadata2.setIdentifier('my identifier 2')
metadata2.setHistory(['h1', 'h2'])
ok, err = QgsProviderRegistry.instance().saveLayerMetadata('ogr', uri, metadata2)
self.assertTrue(ok)

with open(os.path.join(d.path(), 'metadatatest.qmd'), 'rt') as f:
metadata_xml = ''.join(f.readlines())

metadata3 = QgsLayerMetadata()
doc = QDomDocument()
doc.setContent(metadata_xml)
self.assertTrue(metadata3.readMetadataXml(doc.documentElement()))
self.assertEqual(metadata3.abstract(), 'my abstract 2')
self.assertEqual(metadata3.identifier(), 'my identifier 2')
self.assertEqual(metadata3.licenses(), ['l1', 'l2'])
self.assertEqual(metadata3.history(), ['h1', 'h2'])

def testSaveMetadataUnsupported(self):
"""
Test saving metadata to an unsupported URI
"""
metadata = QgsLayerMetadata()
# this should raise a QgsNotSupportedException, as we don't support writing metadata to a WFS uri
with self.assertRaises(QgsNotSupportedException):
QgsProviderRegistry.instance().saveLayerMetadata('ogr', 'WFS:http://www2.dmsolutions.ca/cgi-bin/mswfs_gmap', metadata)

def testEmbeddedSymbolsKml(self):
"""
Test retrieving embedded symbols from a KML file

0 comments on commit 4cbec54

Please sign in to comment.