Implement querySublayers for mbtiles vector tile provider

This commit is contained in:
Nyall Dawson 2023-04-02 10:29:05 +10:00
parent 2fb4f894c7
commit 4810e0c9bd
4 changed files with 109 additions and 2 deletions

View File

@ -37,10 +37,12 @@ bool QgsProviderUtils::sublayerDetailsAreIncomplete( const QList<QgsProviderSubl
return true;
break;
case Qgis::LayerType::VectorTile:
return sublayer.skippedContainerScan();
case Qgis::LayerType::Raster:
case Qgis::LayerType::Plugin:
case Qgis::LayerType::Mesh:
case Qgis::LayerType::VectorTile:
case Qgis::LayerType::Annotation:
case Qgis::LayerType::PointCloud:
case Qgis::LayerType::Group:

View File

@ -22,6 +22,8 @@
#include "qgslogger.h"
#include "qgsapplication.h"
#include "qgscoordinatetransform.h"
#include "qgsproviderutils.h"
#include "qgsprovidersublayerdetails.h"
#include <QIcon>
#include <QFileInfo>
@ -224,7 +226,8 @@ QgsMbTilesVectorTileDataProviderMetadata::QgsMbTilesVectorTileDataProviderMetada
QgsProviderMetadata::ProviderMetadataCapabilities QgsMbTilesVectorTileDataProviderMetadata::capabilities() const
{
return ProviderMetadataCapability::LayerTypesForUri
| ProviderMetadataCapability::PriorityForUri;
| ProviderMetadataCapability::PriorityForUri
| ProviderMetadataCapability::QuerySublayers;
}
QgsMbTilesVectorTileDataProvider *QgsMbTilesVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
@ -259,6 +262,60 @@ QString QgsMbTilesVectorTileDataProviderMetadata::filters( Qgis::FileFilterType
return QString();
}
QList<QgsProviderSublayerDetails> QgsMbTilesVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags, QgsFeedback * ) const
{
QString fileName;
const QFileInfo fi( uri );
if ( fi.isFile() )
{
fileName = uri;
}
else
{
const QVariantMap parts = decodeUri( uri );
fileName = parts.value( QStringLiteral( "path" ) ).toString();
}
if ( fileName.isEmpty() )
return {};
if ( QFileInfo( fileName ).suffix().compare( QLatin1String( "mbtiles" ), Qt::CaseInsensitive ) == 0 )
{
QVariantMap parts;
parts.insert( QStringLiteral( "path" ), fileName );
if ( flags & Qgis::SublayerQueryFlag::FastScan )
{
// fast scan -- assume vector tile are available
QgsProviderSublayerDetails details;
details.setUri( encodeUri( parts ) );
details.setProviderKey( key() );
details.setType( Qgis::LayerType::VectorTile );
details.setSkippedContainerScan( true );
details.setName( QgsProviderUtils::suggestLayerNameFromFilePath( fileName ) );
return {details};
}
else
{
// slower scan, check actual mbtiles format
QgsMbTiles reader( fileName );
if ( reader.open() )
{
if ( reader.metadataValue( "format" ) == QLatin1String( "pbf" ) )
{
QgsProviderSublayerDetails details;
details.setUri( encodeUri( parts ) );
details.setProviderKey( key() );
details.setType( Qgis::LayerType::VectorTile );
details.setName( QgsProviderUtils::suggestLayerNameFromFilePath( fileName ) );
return {details};
}
}
}
}
return {};
}
int QgsMbTilesVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
{
if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )

View File

@ -78,6 +78,7 @@ class QgsMbTilesVectorTileDataProviderMetadata : public QgsProviderMetadata
QIcon icon() const override;
ProviderCapabilities providerCapabilities() const override;
QString filters( Qgis::FileFilterType type ) override;
QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = nullptr ) const override;
int priorityForUri( const QString &uri ) const override;
QList< Qgis::LayerType > validLayerTypesForUri( const QString &uri ) const override;

View File

@ -35,6 +35,7 @@
#include "qgsprovidermetadata.h"
#include "qgsproviderregistry.h"
#include "qgsprovidersublayerdetails.h"
#include "qgsproviderutils.h"
/**
* \ingroup UnitTests
@ -241,6 +242,52 @@ void TestQgsVectorTileLayer::testMbtilesProviderMetadata()
QCOMPARE( vectorTileMetadata->priorityForUri( QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) ), 100 );
QCOMPARE( vectorTileMetadata->validLayerTypesForUri( QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) ), {Qgis::LayerType::VectorTile} );
// query sublayers
QList< QgsProviderSublayerDetails > sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "mbtiles_vt" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( !sublayers.at( 0 ).skippedContainerScan() );
QVERIFY( !QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers ) );
sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "mbtiles_vt" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( !sublayers.at( 0 ).skippedContainerScan() );
// fast scan flag
sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ), Qgis::SublayerQueryFlag::FastScan );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "mbtiles_vt" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( sublayers.at( 0 ).skippedContainerScan() );
QVERIFY( QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers ) );
sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ), Qgis::SublayerQueryFlag::FastScan );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "mbtiles_vt" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( sublayers.at( 0 ).skippedContainerScan() );
// fast scan mode means that any mbtile file will be reported, including those with only raster tiles
// (we are skipping a potentially expensive db open and format check)
sublayers = vectorTileMetadata->querySublayers( QStringLiteral( "%1/isle_of_man.mbtiles" ).arg( TEST_DATA_DIR ), Qgis::SublayerQueryFlag::FastScan );
QCOMPARE( sublayers.size(), 1 );
QCOMPARE( sublayers.at( 0 ).providerKey(), QStringLiteral( "mbtilesvectortiles" ) );
QCOMPARE( sublayers.at( 0 ).name(), QStringLiteral( "isle_of_man" ) );
QCOMPARE( sublayers.at( 0 ).uri(), QStringLiteral( "type=mbtiles&url=%1/isle_of_man.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( sublayers.at( 0 ).type(), Qgis::LayerType::VectorTile );
QVERIFY( sublayers.at( 0 ).skippedContainerScan() );
// test that mbtilesvectortiles provider is the preferred provider for vector tile mbtiles files
QList<QgsProviderRegistry::ProviderCandidateDetails> candidates = QgsProviderRegistry::instance()->preferredProvidersForUri( QStringLiteral( "type=mbtiles&url=%1/vector_tile/mbtiles_vt.mbtiles" ).arg( TEST_DATA_DIR ) );
QCOMPARE( candidates.size(), 1 );