Skip to content

Commit 338a6a3

Browse files
committedMar 23, 2023
Move tile matrix set handling to vector tile data provider
1 parent 7177549 commit 338a6a3

11 files changed

+383
-248
lines changed
 

‎src/core/vectortile/qgsarcgisvectortileservicedataprovider.cpp

Lines changed: 192 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,32 @@
1616
#include "qgsarcgisvectortileservicedataprovider.h"
1717
#include "qgsthreadingutils.h"
1818
#include "qgsapplication.h"
19+
#include "qgsblockingnetworkrequest.h"
20+
#include "qgsnetworkaccessmanager.h"
21+
#include "qgsvectortileutils.h"
22+
#include "qgsarcgisrestutils.h"
23+
#include "qgslogger.h"
24+
#include "qgscoordinatetransform.h"
25+
1926
#include <QIcon>
27+
#include <QUrl>
28+
#include <QUrlQuery>
29+
#include <QJsonParseError>
30+
#include <QJsonDocument>
31+
#include <QJsonObject>
2032

2133
///@cond PRIVATE
2234

2335
QString QgsArcGisVectorTileServiceDataProvider::ARCGIS_VT_SERVICE_DATA_PROVIDER_KEY = QStringLiteral( "arcgisvectortileservice" );
2436
QString QgsArcGisVectorTileServiceDataProvider::ARCGIS_VT_SERVICE_DATA_PROVIDER_DESCRIPTION = QObject::tr( "ArcGIS Vector Tile Service data provider" );
2537

2638

27-
QgsArcGisVectorTileServiceDataProvider::QgsArcGisVectorTileServiceDataProvider( const QString &uri, const QString &sourcePath, const ProviderOptions &providerOptions, ReadFlags flags )
39+
QgsArcGisVectorTileServiceDataProvider::QgsArcGisVectorTileServiceDataProvider( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
2840
: QgsXyzVectorTileDataProvider( uri, providerOptions, flags )
29-
, mSourcePath( sourcePath )
3041
{
42+
mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator();
3143

44+
mIsValid = setupArcgisVectorTileServiceConnection();
3245
}
3346

3447
QString QgsArcGisVectorTileServiceDataProvider::name() const
@@ -51,7 +64,7 @@ QgsVectorTileDataProvider *QgsArcGisVectorTileServiceDataProvider::clone() const
5164

5265
ProviderOptions options;
5366
options.transformContext = transformContext();
54-
return new QgsArcGisVectorTileServiceDataProvider( dataSourceUri(), mSourcePath, options, mReadFlags );
67+
return new QgsArcGisVectorTileServiceDataProvider( dataSourceUri(), options, mReadFlags );
5568
}
5669

5770
QString QgsArcGisVectorTileServiceDataProvider::sourcePath() const
@@ -65,6 +78,177 @@ bool QgsArcGisVectorTileServiceDataProvider::isValid() const
6578
{
6679
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
6780

81+
return mIsValid;
82+
}
83+
84+
QgsRectangle QgsArcGisVectorTileServiceDataProvider::extent() const
85+
{
86+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
87+
88+
return mExtent;
89+
}
90+
91+
QgsCoordinateReferenceSystem QgsArcGisVectorTileServiceDataProvider::crs() const
92+
{
93+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
94+
95+
return mCrs;
96+
}
97+
98+
bool QgsArcGisVectorTileServiceDataProvider::setupArcgisVectorTileServiceConnection()
99+
{
100+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
101+
102+
QgsDataSourceUri dsUri;
103+
dsUri.setEncodedUri( dataSourceUri() );
104+
QString tileServiceUri = dsUri.param( QStringLiteral( "url" ) );
105+
106+
QUrl url( tileServiceUri );
107+
// some services don't default to json format, while others do... so let's explicitly request it!
108+
// (refs https://github.com/qgis/QGIS/issues/4231)
109+
QUrlQuery query;
110+
query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
111+
url.setQuery( query );
112+
113+
QNetworkRequest request = QNetworkRequest( url );
114+
115+
QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
116+
117+
QgsBlockingNetworkRequest networkRequest;
118+
switch ( networkRequest.get( request ) )
119+
{
120+
case QgsBlockingNetworkRequest::NoError:
121+
break;
122+
123+
case QgsBlockingNetworkRequest::NetworkError:
124+
case QgsBlockingNetworkRequest::TimeoutError:
125+
case QgsBlockingNetworkRequest::ServerExceptionError:
126+
return false;
127+
}
128+
129+
const QgsNetworkReplyContent content = networkRequest.reply();
130+
const QByteArray raw = content.content();
131+
132+
// Parse data
133+
QJsonParseError err;
134+
const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
135+
if ( doc.isNull() )
136+
{
137+
return false;
138+
}
139+
140+
mArcgisLayerConfiguration = doc.object().toVariantMap();
141+
if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
142+
{
143+
return false;
144+
}
145+
146+
if ( !mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).isValid() )
147+
{
148+
// maybe url is pointing to a resources/styles/root.json type url, that's ok too!
149+
const QString sourceUri = mArcgisLayerConfiguration.value( QStringLiteral( "sources" ) ).toMap().value( QStringLiteral( "esri" ) ).toMap().value( QStringLiteral( "url" ) ).toString();
150+
if ( !sourceUri.isEmpty() )
151+
{
152+
QUrl url( sourceUri );
153+
// some services don't default to json format, while others do... so let's explicitly request it!
154+
// (refs https://github.com/qgis/QGIS/issues/4231)
155+
QUrlQuery query;
156+
query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
157+
url.setQuery( query );
158+
159+
QNetworkRequest request = QNetworkRequest( url );
160+
161+
QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
162+
163+
QgsBlockingNetworkRequest networkRequest;
164+
switch ( networkRequest.get( request ) )
165+
{
166+
case QgsBlockingNetworkRequest::NoError:
167+
break;
168+
169+
case QgsBlockingNetworkRequest::NetworkError:
170+
case QgsBlockingNetworkRequest::TimeoutError:
171+
case QgsBlockingNetworkRequest::ServerExceptionError:
172+
return false;
173+
}
174+
175+
const QgsNetworkReplyContent content = networkRequest.reply();
176+
const QByteArray raw = content.content();
177+
178+
// Parse data
179+
QJsonParseError err;
180+
const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
181+
if ( doc.isNull() )
182+
{
183+
return false;
184+
}
185+
186+
tileServiceUri = sourceUri;
187+
188+
// the resources/styles/root.json configuration is actually our style definition
189+
mArcgisStyleConfiguration = mArcgisLayerConfiguration;
190+
mArcgisLayerConfiguration = doc.object().toVariantMap();
191+
if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
192+
{
193+
return false;
194+
}
195+
}
196+
}
197+
198+
mSourcePath = tileServiceUri + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).toList().value( 0 ).toString();
199+
if ( !QgsVectorTileUtils::checkXYZUrlTemplate( tileServiceUri ) )
200+
{
201+
QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + tileServiceUri );
202+
return false;
203+
}
204+
205+
mArcgisLayerConfiguration.insert( QStringLiteral( "serviceUri" ), tileServiceUri );
206+
207+
mMatrixSet.fromEsriJson( mArcgisLayerConfiguration );
208+
mCrs = mMatrixSet.crs();
209+
210+
// if hardcoded zoom limits aren't specified, take them from the server
211+
if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
212+
mMatrixSet.dropMatricesOutsideZoomRange( dsUri.param( QStringLiteral( "zmin" ) ).toInt(), 99 );
213+
214+
if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
215+
mMatrixSet.dropMatricesOutsideZoomRange( 0, dsUri.param( QStringLiteral( "zmax" ) ).toInt() );
216+
217+
const QVariantMap fullExtent = mArcgisLayerConfiguration.value( QStringLiteral( "fullExtent" ) ).toMap();
218+
if ( !fullExtent.isEmpty() )
219+
{
220+
const QgsRectangle fullExtentRect(
221+
fullExtent.value( QStringLiteral( "xmin" ) ).toDouble(),
222+
fullExtent.value( QStringLiteral( "ymin" ) ).toDouble(),
223+
fullExtent.value( QStringLiteral( "xmax" ) ).toDouble(),
224+
fullExtent.value( QStringLiteral( "ymax" ) ).toDouble()
225+
);
226+
227+
const QgsCoordinateReferenceSystem fullExtentCrs = QgsArcGisRestUtils::convertSpatialReference( fullExtent.value( QStringLiteral( "spatialReference" ) ).toMap() );
228+
const QgsCoordinateTransform extentTransform( fullExtentCrs, mCrs, transformContext() );
229+
try
230+
{
231+
mExtent = extentTransform.transformBoundingBox( fullExtentRect );
232+
}
233+
catch ( QgsCsException & )
234+
{
235+
QgsDebugMsg( QStringLiteral( "Could not transform layer fullExtent to layer CRS" ) );
236+
}
237+
}
238+
else
239+
{
240+
// if no fullExtent specified in JSON, default to web mercator specs full extent
241+
const QgsCoordinateTransform extentTransform( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), mCrs, transformContext() );
242+
try
243+
{
244+
mExtent = extentTransform.transformBoundingBox( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );
245+
}
246+
catch ( QgsCsException & )
247+
{
248+
QgsDebugMsg( QStringLiteral( "Could not transform layer extent to layer CRS" ) );
249+
}
250+
}
251+
68252
return true;
69253
}
70254

@@ -89,6 +273,11 @@ QgsProviderMetadata::ProviderCapabilities QgsArcGisVectorTileServiceDataProvider
89273
return QgsProviderMetadata::ProviderCapabilities();
90274
}
91275

276+
QgsArcGisVectorTileServiceDataProvider *QgsArcGisVectorTileServiceDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
277+
{
278+
return new QgsArcGisVectorTileServiceDataProvider( uri, options, flags );
279+
}
280+
92281
QVariantMap QgsArcGisVectorTileServiceDataProviderMetadata::decodeUri( const QString &uri ) const
93282
{
94283
// TODO -- carefully thin out options which don't apply to arcgis vector tile services

‎src/core/vectortile/qgsarcgisvectortileservicedataprovider.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "qgis_core.h"
2020
#include "qgis_sip.h"
21+
#include "qgsvectortilematrixset.h"
2122
#include "qgsxyzvectortiledataprovider.h"
2223
#include "qgsprovidermetadata.h"
2324

@@ -31,21 +32,33 @@ class CORE_EXPORT QgsArcGisVectorTileServiceDataProvider : public QgsXyzVectorTi
3132

3233
public:
3334
QgsArcGisVectorTileServiceDataProvider( const QString &uri,
34-
const QString &sourcePath,
3535
const QgsDataProvider::ProviderOptions &providerOptions,
3636
QgsDataProvider::ReadFlags flags );
3737
QString name() const override;
3838
QString description() const override;
3939
QgsVectorTileDataProvider *clone() const override;
4040
QString sourcePath() const override;
4141
bool isValid() const override;
42+
QgsRectangle extent() const override;
43+
QgsCoordinateReferenceSystem crs() const override;
4244

4345
static QString ARCGIS_VT_SERVICE_DATA_PROVIDER_KEY;
4446
static QString ARCGIS_VT_SERVICE_DATA_PROVIDER_DESCRIPTION;
4547

4648
private:
4749

50+
bool setupArcgisVectorTileServiceConnection();
51+
52+
bool mIsValid = false;
4853
QString mSourcePath;
54+
55+
QVariantMap mArcgisLayerConfiguration;
56+
QVariantMap mArcgisStyleConfiguration;
57+
58+
QgsRectangle mExtent;
59+
QgsCoordinateReferenceSystem mCrs;
60+
61+
QgsVectorTileMatrixSet mMatrixSet;
4962
};
5063

5164

@@ -56,6 +69,7 @@ class QgsArcGisVectorTileServiceDataProviderMetadata : public QgsProviderMetadat
5669
QgsArcGisVectorTileServiceDataProviderMetadata();
5770
QIcon icon() const override;
5871
ProviderCapabilities providerCapabilities() const override;
72+
QgsArcGisVectorTileServiceDataProvider *createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags() ) override;
5973
QVariantMap decodeUri( const QString &uri ) const override;
6074
QString encodeUri( const QVariantMap &parts ) const override;
6175
QString absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const override;

‎src/core/vectortile/qgsmbtilesvectortiledataprovider.cpp

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "qgsziputils.h"
2222
#include "qgslogger.h"
2323
#include "qgsapplication.h"
24+
#include "qgscoordinatetransform.h"
2425
#include <QIcon>
2526

2627
///@cond PRIVATE
@@ -31,7 +32,53 @@ QString QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_DES
3132
QgsMbTilesVectorTileDataProvider::QgsMbTilesVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
3233
: QgsVectorTileDataProvider( uri, providerOptions, flags )
3334
{
35+
QgsDataSourceUri dsUri;
36+
dsUri.setEncodedUri( uri );
37+
const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
38+
39+
QgsMbTiles reader( sourcePath );
40+
if ( !reader.open() )
41+
{
42+
QgsDebugMsg( QStringLiteral( "failed to open MBTiles file: " ) + sourcePath );
43+
mIsValid = false;
44+
return;
45+
}
46+
47+
const QString format = reader.metadataValue( QStringLiteral( "format" ) );
48+
if ( format != QLatin1String( "pbf" ) )
49+
{
50+
QgsDebugMsg( QStringLiteral( "Cannot open MBTiles for vector tiles. Format = " ) + format );
51+
mIsValid = false;
52+
return;
53+
}
54+
55+
QgsDebugMsgLevel( QStringLiteral( "name: " ) + reader.metadataValue( QStringLiteral( "name" ) ), 2 );
56+
57+
mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator();
58+
59+
bool minZoomOk, maxZoomOk;
60+
const int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk );
61+
const int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk );
62+
if ( minZoomOk )
63+
mMatrixSet.dropMatricesOutsideZoomRange( minZoom, 99 );
64+
if ( maxZoomOk )
65+
mMatrixSet.dropMatricesOutsideZoomRange( 0, maxZoom );
66+
QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mMatrixSet.minimumZoom() ).arg( mMatrixSet.maximumZoom() ), 2 );
67+
68+
QgsRectangle r = reader.extent();
69+
QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ),
70+
QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), transformContext() );
71+
ct.setBallparkTransformsAreAppropriate( true );
72+
try
73+
{
74+
mExtent = ct.transformBoundingBox( r );
75+
}
76+
catch ( QgsCsException & )
77+
{
78+
QgsDebugMsg( QStringLiteral( "Could not transform layer extent to layer CRS" ) );
79+
}
3480

81+
mIsValid = true;
3582
}
3683

3784
QString QgsMbTilesVectorTileDataProvider::name() const
@@ -70,7 +117,14 @@ bool QgsMbTilesVectorTileDataProvider::isValid() const
70117
{
71118
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
72119

73-
return true;
120+
return mIsValid;
121+
}
122+
123+
QgsRectangle QgsMbTilesVectorTileDataProvider::extent() const
124+
{
125+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
126+
127+
return mExtent;
74128
}
75129

76130
QgsCoordinateReferenceSystem QgsMbTilesVectorTileDataProvider::crs() const
@@ -80,6 +134,13 @@ QgsCoordinateReferenceSystem QgsMbTilesVectorTileDataProvider::crs() const
80134
return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
81135
}
82136

137+
const QgsVectorTileMatrixSet &QgsMbTilesVectorTileDataProvider::tileMatrixSet() const
138+
{
139+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
140+
141+
return mMatrixSet;
142+
}
143+
83144
QByteArray QgsMbTilesVectorTileDataProvider::readTile( const QgsTileMatrix &, const QgsTileXYZ &id, QgsFeedback *feedback ) const
84145
{
85146
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

‎src/core/vectortile/qgsmbtilesvectortiledataprovider.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgis_sip.h"
2121
#include "qgsvectortiledataprovider.h"
2222
#include "qgsprovidermetadata.h"
23+
#include "qgsvectortilematrixset.h"
2324

2425
class QgsMbTiles;
2526

@@ -41,7 +42,9 @@ class CORE_EXPORT QgsMbTilesVectorTileDataProvider : public QgsVectorTileDataPro
4142
QgsVectorTileDataProvider *clone() const override;
4243
QString sourcePath() const override;
4344
bool isValid() const override;
45+
QgsRectangle extent() const override;
4446
QgsCoordinateReferenceSystem crs() const override;
47+
const QgsVectorTileMatrixSet &tileMatrixSet() const override;
4548
QByteArray readTile( const QgsTileMatrix &tileMatrix, const QgsTileXYZ &id, QgsFeedback *feedback = nullptr ) const override;
4649
QList<QgsVectorTileRawData> readTiles( const QgsTileMatrix &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback = nullptr ) const override;
4750

@@ -52,6 +55,9 @@ class CORE_EXPORT QgsMbTilesVectorTileDataProvider : public QgsVectorTileDataPro
5255

5356
//! Returns raw tile data for a single tile loaded from MBTiles file
5457
static QByteArray loadFromMBTiles( QgsMbTiles &mbTileReader, const QgsTileXYZ &id, QgsFeedback *feedback = nullptr );
58+
bool mIsValid = false;
59+
QgsRectangle mExtent;
60+
QgsVectorTileMatrixSet mMatrixSet;
5561

5662
};
5763

‎src/core/vectortile/qgsvectortiledataprovider.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class QgsTileMatrix;
2424
class QgsTileXYZ;
2525
class QgsVectorTileRawData;
26+
class QgsVectorTileMatrixSet;
2627

2728
#define SIP_NO_FILE
2829

@@ -60,6 +61,11 @@ class CORE_EXPORT QgsVectorTileDataProvider : public QgsDataProvider
6061
*/
6162
virtual QgsVectorTileDataProvider *clone() const = 0 SIP_FACTORY;
6263

64+
/**
65+
* Returns the tile matrix set associated with the provider.
66+
*/
67+
virtual const QgsVectorTileMatrixSet &tileMatrixSet() const = 0;
68+
6369
/**
6470
* Returns TRUE if the provider supports async tile reading.
6571
*

‎src/core/vectortile/qgsvectortilelayer.cpp

Lines changed: 10 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
#include "qgslogger.h"
1919
#include "qgsvectortilelayerrenderer.h"
20-
#include "qgsmbtiles.h"
2120
#include "qgsvtpktiles.h"
2221
#include "qgsvectortilebasiclabeling.h"
2322
#include "qgsvectortilebasicrenderer.h"
@@ -31,7 +30,6 @@
3130
#include "qgsjsonutils.h"
3231
#include "qgspainting.h"
3332
#include "qgsmaplayerfactory.h"
34-
#include "qgsarcgisrestutils.h"
3533
#include "qgsselectioncontext.h"
3634
#include "qgsgeometryengine.h"
3735
#include "qgsvectortilemvtdecoder.h"
@@ -47,8 +45,6 @@ QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseN
4745
: QgsMapLayer( Qgis::LayerType::VectorTile, baseName )
4846
, mTransformContext( options.transformContext )
4947
{
50-
mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator();
51-
5248
mDataSource = uri;
5349

5450
setValid( loadDataSource() );
@@ -85,268 +81,44 @@ bool QgsVectorTileLayer::loadDataSource()
8581
const QgsDataProvider::ReadFlags flags;
8682

8783
mSourceType = dsUri.param( QStringLiteral( "type" ) );
88-
const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
84+
QString providerKey;
8985
if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
9086
{
91-
if ( !setupArcgisVectorTileServiceConnection( sourcePath, dsUri ) )
92-
return false;
87+
providerKey = QStringLiteral( "arcgisvectortileservice" );
9388
}
9489
else if ( mSourceType == QLatin1String( "xyz" ) )
9590
{
96-
if ( !QgsVectorTileUtils::checkXYZUrlTemplate( sourcePath ) )
97-
{
98-
QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + sourcePath );
99-
return false;
100-
}
101-
102-
// online tiles
103-
int zMin = 0;
104-
if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
105-
zMin = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
106-
107-
int zMax = 14;
108-
if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
109-
zMax = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
110-
111-
mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
112-
setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );
113-
114-
mDataProvider.reset( qobject_cast<QgsVectorTileDataProvider *>( QgsProviderRegistry::instance()->createProvider( QStringLiteral( "xyzvectortiles" ), mDataSource, providerOptions, flags ) ) );
91+
providerKey = QStringLiteral( "xyzvectortiles" );
11592
}
11693
else if ( mSourceType == QLatin1String( "mbtiles" ) )
11794
{
118-
QgsMbTiles reader( sourcePath );
119-
if ( !reader.open() )
120-
{
121-
QgsDebugMsg( QStringLiteral( "failed to open MBTiles file: " ) + sourcePath );
122-
return false;
123-
}
124-
125-
const QString format = reader.metadataValue( QStringLiteral( "format" ) );
126-
if ( format != QLatin1String( "pbf" ) )
127-
{
128-
QgsDebugMsg( QStringLiteral( "Cannot open MBTiles for vector tiles. Format = " ) + format );
129-
return false;
130-
}
131-
132-
QgsDebugMsgLevel( QStringLiteral( "name: " ) + reader.metadataValue( QStringLiteral( "name" ) ), 2 );
133-
bool minZoomOk, maxZoomOk;
134-
const int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk );
135-
const int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk );
136-
if ( minZoomOk )
137-
mMatrixSet.dropMatricesOutsideZoomRange( minZoom, 99 );
138-
if ( maxZoomOk )
139-
mMatrixSet.dropMatricesOutsideZoomRange( 0, maxZoom );
140-
QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mMatrixSet.minimumZoom() ).arg( mMatrixSet.maximumZoom() ), 2 );
141-
142-
QgsRectangle r = reader.extent();
143-
QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ),
144-
QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), transformContext() );
145-
ct.setBallparkTransformsAreAppropriate( true );
146-
r = ct.transformBoundingBox( r );
147-
setExtent( r );
148-
149-
mDataProvider.reset( qobject_cast<QgsVectorTileDataProvider *>( QgsProviderRegistry::instance()->createProvider( QStringLiteral( "mbtilesvectortiles" ), mDataSource, providerOptions, flags ) ) );
95+
providerKey = QStringLiteral( "mbtilesvectortiles" );
15096
}
15197
else if ( mSourceType == QLatin1String( "vtpk" ) )
15298
{
153-
QgsVtpkTiles reader( sourcePath );
154-
if ( !reader.open() )
155-
{
156-
QgsDebugMsg( QStringLiteral( "failed to open VTPK file: " ) + sourcePath );
157-
return false;
158-
}
159-
160-
const QVariantMap metadata = reader.metadata();
161-
const QString format = metadata.value( QStringLiteral( "tileInfo" ) ).toMap().value( QStringLiteral( "format" ) ).toString();
162-
if ( format != QLatin1String( "pbf" ) )
163-
{
164-
QgsDebugMsg( QStringLiteral( "Cannot open VTPK for vector tiles. Format = " ) + format );
165-
return false;
166-
}
167-
168-
mMatrixSet = reader.matrixSet();
169-
setCrs( mMatrixSet.crs() );
170-
setExtent( reader.extent( transformContext() ) );
171-
172-
mDataProvider.reset( qobject_cast<QgsVectorTileDataProvider *>( QgsProviderRegistry::instance()->createProvider( QStringLiteral( "vtpkvectortiles" ), mDataSource, providerOptions, flags ) ) );
99+
providerKey = QStringLiteral( "vtpkvectortiles" );
173100
}
174101
else
175102
{
176103
QgsDebugMsg( QStringLiteral( "Unknown source type: " ) + mSourceType );
177104
return false;
178105
}
179106

107+
mDataProvider.reset( qobject_cast<QgsVectorTileDataProvider *>( QgsProviderRegistry::instance()->createProvider( providerKey, mDataSource, providerOptions, flags ) ) );
180108
mProviderKey = mDataProvider->name();
181109

182-
return true;
183-
}
184-
185-
bool QgsVectorTileLayer::setupArcgisVectorTileServiceConnection( const QString &uri, const QgsDataSourceUri &dataSourceUri )
186-
{
187-
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
188-
189-
QString tileServiceUri = uri;
190-
QUrl url( tileServiceUri );
191-
// some services don't default to json format, while others do... so let's explicitly request it!
192-
// (refs https://github.com/qgis/QGIS/issues/4231)
193-
QUrlQuery query;
194-
query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
195-
url.setQuery( query );
196-
197-
QNetworkRequest request = QNetworkRequest( url );
198-
199-
QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
200-
201-
QgsBlockingNetworkRequest networkRequest;
202-
switch ( networkRequest.get( request ) )
203-
{
204-
case QgsBlockingNetworkRequest::NoError:
205-
break;
206-
207-
case QgsBlockingNetworkRequest::NetworkError:
208-
case QgsBlockingNetworkRequest::TimeoutError:
209-
case QgsBlockingNetworkRequest::ServerExceptionError:
210-
return false;
211-
}
212-
213-
const QgsNetworkReplyContent content = networkRequest.reply();
214-
const QByteArray raw = content.content();
215-
216-
// Parse data
217-
QJsonParseError err;
218-
const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
219-
if ( doc.isNull() )
220-
{
221-
return false;
222-
}
223-
224-
mArcgisLayerConfiguration = doc.object().toVariantMap();
225-
if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
226-
{
227-
return false;
228-
}
229-
230-
if ( !mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).isValid() )
231-
{
232-
// maybe url is pointing to a resources/styles/root.json type url, that's ok too!
233-
const QString sourceUri = mArcgisLayerConfiguration.value( QStringLiteral( "sources" ) ).toMap().value( QStringLiteral( "esri" ) ).toMap().value( QStringLiteral( "url" ) ).toString();
234-
if ( !sourceUri.isEmpty() )
235-
{
236-
QUrl url( sourceUri );
237-
// some services don't default to json format, while others do... so let's explicitly request it!
238-
// (refs https://github.com/qgis/QGIS/issues/4231)
239-
QUrlQuery query;
240-
query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
241-
url.setQuery( query );
242-
243-
QNetworkRequest request = QNetworkRequest( url );
244-
245-
QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
246-
247-
QgsBlockingNetworkRequest networkRequest;
248-
switch ( networkRequest.get( request ) )
249-
{
250-
case QgsBlockingNetworkRequest::NoError:
251-
break;
252-
253-
case QgsBlockingNetworkRequest::NetworkError:
254-
case QgsBlockingNetworkRequest::TimeoutError:
255-
case QgsBlockingNetworkRequest::ServerExceptionError:
256-
return false;
257-
}
258-
259-
const QgsNetworkReplyContent content = networkRequest.reply();
260-
const QByteArray raw = content.content();
261-
262-
// Parse data
263-
QJsonParseError err;
264-
const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
265-
if ( doc.isNull() )
266-
{
267-
return false;
268-
}
269-
270-
tileServiceUri = sourceUri;
271-
272-
// the resources/styles/root.json configuration is actually our style definition
273-
mArcgisStyleConfiguration = mArcgisLayerConfiguration;
274-
mArcgisLayerConfiguration = doc.object().toVariantMap();
275-
if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
276-
{
277-
return false;
278-
}
279-
}
280-
}
281-
282-
const QString sourcePath = tileServiceUri + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).toList().value( 0 ).toString();
283-
if ( !QgsVectorTileUtils::checkXYZUrlTemplate( sourcePath ) )
284-
{
285-
QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + sourcePath );
286-
return false;
287-
}
288-
289-
mArcgisLayerConfiguration.insert( QStringLiteral( "serviceUri" ), tileServiceUri );
290-
291-
292-
mMatrixSet.fromEsriJson( mArcgisLayerConfiguration );
293-
setCrs( mMatrixSet.crs() );
294-
295-
// if hardcoded zoom limits aren't specified, take them from the server
296-
if ( dataSourceUri.hasParam( QStringLiteral( "zmin" ) ) )
297-
mMatrixSet.dropMatricesOutsideZoomRange( dataSourceUri.param( QStringLiteral( "zmin" ) ).toInt(), 99 );
298-
299-
if ( dataSourceUri.hasParam( QStringLiteral( "zmax" ) ) )
300-
mMatrixSet.dropMatricesOutsideZoomRange( 0, dataSourceUri.param( QStringLiteral( "zmax" ) ).toInt() );
301-
302-
const QVariantMap fullExtent = mArcgisLayerConfiguration.value( QStringLiteral( "fullExtent" ) ).toMap();
303-
if ( !fullExtent.isEmpty() )
304-
{
305-
const QgsRectangle fullExtentRect(
306-
fullExtent.value( QStringLiteral( "xmin" ) ).toDouble(),
307-
fullExtent.value( QStringLiteral( "ymin" ) ).toDouble(),
308-
fullExtent.value( QStringLiteral( "xmax" ) ).toDouble(),
309-
fullExtent.value( QStringLiteral( "ymax" ) ).toDouble()
310-
);
311-
312-
const QgsCoordinateReferenceSystem fullExtentCrs = QgsArcGisRestUtils::convertSpatialReference( fullExtent.value( QStringLiteral( "spatialReference" ) ).toMap() );
313-
const QgsCoordinateTransform extentTransform( fullExtentCrs, crs(), transformContext() );
314-
try
315-
{
316-
setExtent( extentTransform.transformBoundingBox( fullExtentRect ) );
317-
}
318-
catch ( QgsCsException & )
319-
{
320-
QgsDebugMsg( QStringLiteral( "Could not transform layer fullExtent to layer CRS" ) );
321-
}
322-
}
323-
else
110+
if ( mDataProvider )
324111
{
325-
// if no fullExtent specified in JSON, default to web mercator specs full extent
326-
const QgsCoordinateTransform extentTransform( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), crs(), transformContext() );
327-
try
328-
{
329-
setExtent( extentTransform.transformBoundingBox( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) ) );
330-
}
331-
catch ( QgsCsException & )
332-
{
333-
QgsDebugMsg( QStringLiteral( "Could not transform layer extent to layer CRS" ) );
334-
}
112+
mMatrixSet = qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->tileMatrixSet();
113+
setCrs( mDataProvider->crs() );
114+
setExtent( mDataProvider->extent() );
335115
}
336116

337-
const QgsDataProvider::ProviderOptions providerOptions { mTransformContext };
338-
const QgsDataProvider::ReadFlags flags;
339-
340-
// TODO -- call QgsProviderRegistry::instance()->createProvider instead, but that first requires moving above logic for
341-
// determination of the service URI to the data provider
342-
mDataProvider = std::make_unique< QgsArcGisVectorTileServiceDataProvider >( mDataSource, sourcePath, providerOptions, flags );
343-
344117
return true;
345118
}
346119

347120
QgsVectorTileLayer::~QgsVectorTileLayer() = default;
348121

349-
350122
QgsVectorTileLayer *QgsVectorTileLayer::clone() const
351123
{
352124
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

‎src/core/vectortile/qgsvectortilelayer.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,6 @@ class CORE_EXPORT QgsVectorTileLayer : public QgsMapLayer
304304

305305
QHash< QgsFeatureId, QgsFeature > mSelectedFeatures;
306306

307-
bool setupArcgisVectorTileServiceConnection( const QString &uri, const QgsDataSourceUri &dataSourceUri );
308-
309307
void setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider,
310308
const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags ) override;
311309

‎src/core/vectortile/qgsvtpkvectortiledataprovider.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "qgsvtpktiles.h"
1919
#include "qgsvectortileloader.h"
2020
#include "qgsapplication.h"
21+
#include "qgslogger.h"
2122

2223
#include <QIcon>
2324

@@ -31,7 +32,32 @@ QString QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION = QObject::tr(
3132
QgsVtpkVectorTileDataProvider::QgsVtpkVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
3233
: QgsVectorTileDataProvider( uri, providerOptions, flags )
3334
{
35+
QgsDataSourceUri dsUri;
36+
dsUri.setEncodedUri( dataSourceUri() );
37+
const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
38+
39+
QgsVtpkTiles reader( sourcePath );
40+
if ( !reader.open() )
41+
{
42+
QgsDebugMsg( QStringLiteral( "failed to open VTPK file: " ) + sourcePath );
43+
mIsValid = false;
44+
return;
45+
}
3446

47+
const QVariantMap metadata = reader.metadata();
48+
const QString format = metadata.value( QStringLiteral( "tileInfo" ) ).toMap().value( QStringLiteral( "format" ) ).toString();
49+
if ( format != QLatin1String( "pbf" ) )
50+
{
51+
QgsDebugMsg( QStringLiteral( "Cannot open VTPK for vector tiles. Format = " ) + format );
52+
mIsValid = false;
53+
return;
54+
}
55+
56+
mMatrixSet = reader.matrixSet();
57+
mCrs = mMatrixSet.crs();
58+
mExtent = reader.extent( transformContext() );
59+
60+
mIsValid = true;
3561
}
3662

3763
QString QgsVtpkVectorTileDataProvider::name() const
@@ -70,14 +96,28 @@ bool QgsVtpkVectorTileDataProvider::isValid() const
7096
{
7197
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
7298

73-
return true;
99+
return mIsValid;
74100
}
75101

76102
QgsCoordinateReferenceSystem QgsVtpkVectorTileDataProvider::crs() const
77103
{
78104
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
79105

80-
return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
106+
return mCrs;
107+
}
108+
109+
QgsRectangle QgsVtpkVectorTileDataProvider::extent() const
110+
{
111+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
112+
113+
return mExtent;
114+
}
115+
116+
const QgsVectorTileMatrixSet &QgsVtpkVectorTileDataProvider::tileMatrixSet() const
117+
{
118+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
119+
120+
return mMatrixSet;
81121
}
82122

83123
QByteArray QgsVtpkVectorTileDataProvider::readTile( const QgsTileMatrix &, const QgsTileXYZ &id, QgsFeedback *feedback ) const

‎src/core/vectortile/qgsvtpkvectortiledataprovider.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgis_sip.h"
2121
#include "qgsvectortiledataprovider.h"
2222
#include "qgsprovidermetadata.h"
23+
#include "qgsvectortilematrixset.h"
2324

2425
#define SIP_NO_FILE
2526

@@ -42,6 +43,8 @@ class CORE_EXPORT QgsVtpkVectorTileDataProvider : public QgsVectorTileDataProvid
4243
QString sourcePath() const override;
4344
bool isValid() const override;
4445
QgsCoordinateReferenceSystem crs() const override;
46+
QgsRectangle extent() const override;
47+
const QgsVectorTileMatrixSet &tileMatrixSet() const override;
4548
QByteArray readTile( const QgsTileMatrix &tileMatrix, const QgsTileXYZ &id, QgsFeedback *feedback = nullptr ) const override;
4649
QList<QgsVectorTileRawData> readTiles( const QgsTileMatrix &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback = nullptr ) const override;
4750

@@ -52,6 +55,10 @@ class CORE_EXPORT QgsVtpkVectorTileDataProvider : public QgsVectorTileDataProvid
5255

5356
//! Returns raw tile data for a single tile loaded from VTPK file
5457
static QByteArray loadFromVtpk( QgsVtpkTiles &vtpkTileReader, const QgsTileXYZ &id, QgsFeedback *feedback = nullptr );
58+
bool mIsValid = false;
59+
QgsCoordinateReferenceSystem mCrs;
60+
QgsRectangle mExtent;
61+
QgsVectorTileMatrixSet mMatrixSet;
5562

5663
};
5764

‎src/core/vectortile/qgsxyzvectortiledataprovider.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,29 @@ QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QString &uri,
3838
QgsDataSourceUri dsUri;
3939
dsUri.setEncodedUri( uri );
4040

41+
const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
42+
if ( !QgsVectorTileUtils::checkXYZUrlTemplate( sourcePath ) )
43+
{
44+
QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + sourcePath );
45+
mIsValid = false;
46+
return;
47+
}
48+
4149
mAuthCfg = dsUri.authConfigId();
4250
mHeaders = dsUri.httpHeaders();
51+
52+
int zMin = 0;
53+
if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
54+
zMin = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
55+
56+
int zMax = 14;
57+
if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
58+
zMax = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
59+
60+
mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
61+
mExtent = QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 );
62+
63+
mIsValid = true;
4364
}
4465

4566
QString QgsXyzVectorTileDataProvider::name() const
@@ -78,7 +99,14 @@ bool QgsXyzVectorTileDataProvider::isValid() const
7899
{
79100
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
80101

81-
return true;
102+
return mIsValid;
103+
}
104+
105+
QgsRectangle QgsXyzVectorTileDataProvider::extent() const
106+
{
107+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
108+
109+
return mExtent;
82110
}
83111

84112
QgsCoordinateReferenceSystem QgsXyzVectorTileDataProvider::crs() const
@@ -88,6 +116,13 @@ QgsCoordinateReferenceSystem QgsXyzVectorTileDataProvider::crs() const
88116
return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
89117
}
90118

119+
const QgsVectorTileMatrixSet &QgsXyzVectorTileDataProvider::tileMatrixSet() const
120+
{
121+
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
122+
123+
return mMatrixSet;
124+
}
125+
91126
bool QgsXyzVectorTileDataProvider::supportsAsync() const
92127
{
93128
return true;

‎src/core/vectortile/qgsxyzvectortiledataprovider.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgis_sip.h"
2121
#include "qgsvectortiledataprovider.h"
2222
#include "qgsprovidermetadata.h"
23+
#include "qgsvectortilematrixset.h"
2324

2425
#define SIP_NO_FILE
2526

@@ -38,7 +39,9 @@ class CORE_EXPORT QgsXyzVectorTileDataProvider : public QgsVectorTileDataProvide
3839
QgsVectorTileDataProvider *clone() const override;
3940
QString sourcePath() const override;
4041
bool isValid() const override;
42+
QgsRectangle extent() const override;
4143
QgsCoordinateReferenceSystem crs() const override;
44+
const QgsVectorTileMatrixSet &tileMatrixSet() const override;
4245
bool supportsAsync() const override;
4346
QByteArray readTile( const QgsTileMatrix &tileMatrix, const QgsTileXYZ &id, QgsFeedback *feedback = nullptr ) const override;
4447
QList<QgsVectorTileRawData> readTiles( const QgsTileMatrix &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback = nullptr ) const override;
@@ -54,6 +57,10 @@ class CORE_EXPORT QgsXyzVectorTileDataProvider : public QgsVectorTileDataProvide
5457

5558
private:
5659

60+
bool mIsValid = false;
61+
QgsRectangle mExtent;
62+
QgsVectorTileMatrixSet mMatrixSet;
63+
5764
//! Returns raw tile data for a single tile, doing a HTTP request. Block the caller until tile data are downloaded.
5865
static QByteArray loadFromNetwork( const QgsTileXYZ &id,
5966
const QgsTileMatrix &tileMatrix,

0 commit comments

Comments
 (0)
Please sign in to comment.