QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsvectortilelayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectortilelayer.cpp
3 --------------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsvectortilelayer.h"
17
18#include "qgslogger.h"
20#include "qgsmbtiles.h"
21#include "qgsvtpktiles.h"
25#include "qgsvectortileloader.h"
26#include "qgsvectortileutils.h"
28
29#include "qgsdatasourceuri.h"
33#include "qgsjsonutils.h"
34#include "qgspainting.h"
35#include "qgsmaplayerfactory.h"
36#include "qgsarcgisrestutils.h"
37#include "qgsselectioncontext.h"
38#include "qgsgeometryengine.h"
40#include "qgsrasterlayer.h"
41
42#include <QUrl>
43#include <QUrlQuery>
44
45QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName, const LayerOptions &options )
47 , mTransformContext( options.transformContext )
48{
50
51 mDataSource = uri;
52
53 setValid( loadDataSource() );
54
55 // set a default renderer
59
60 connect( this, &QgsVectorTileLayer::selectionChanged, this, [ = ] { triggerRepaint(); } );
61}
62
63void QgsVectorTileLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &, const QgsDataProvider::ProviderOptions &, QgsDataProvider::ReadFlags )
64{
65 mDataSource = dataSource;
66 mLayerName = baseName;
67 mDataProvider.reset();
68
69 setValid( loadDataSource() );
70}
71
72bool QgsVectorTileLayer::loadDataSource()
73{
74 QgsDataSourceUri dsUri;
76
77 setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
78
79 mSourceType = dsUri.param( QStringLiteral( "type" ) );
80 mSourcePath = dsUri.param( QStringLiteral( "url" ) );
81 if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
82 {
83 if ( !setupArcgisVectorTileServiceConnection( mSourcePath, dsUri ) )
84 return false;
85 }
86 else if ( mSourceType == QLatin1String( "xyz" ) )
87 {
88 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
89 {
90 QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
91 return false;
92 }
93
94 // online tiles
95 int zMin = 0;
96 if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
97 zMin = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
98
99 int zMax = 14;
100 if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
101 zMax = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
102
103 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
104 setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );
105 }
106 else if ( mSourceType == QLatin1String( "mbtiles" ) )
107 {
108 QgsMbTiles reader( mSourcePath );
109 if ( !reader.open() )
110 {
111 QgsDebugMsg( QStringLiteral( "failed to open MBTiles file: " ) + mSourcePath );
112 return false;
113 }
114
115 const QString format = reader.metadataValue( QStringLiteral( "format" ) );
116 if ( format != QLatin1String( "pbf" ) )
117 {
118 QgsDebugMsg( QStringLiteral( "Cannot open MBTiles for vector tiles. Format = " ) + format );
119 return false;
120 }
121
122 QgsDebugMsgLevel( QStringLiteral( "name: " ) + reader.metadataValue( QStringLiteral( "name" ) ), 2 );
123 bool minZoomOk, maxZoomOk;
124 const int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk );
125 const int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk );
126 if ( minZoomOk )
127 mMatrixSet.dropMatricesOutsideZoomRange( minZoom, 99 );
128 if ( maxZoomOk )
129 mMatrixSet.dropMatricesOutsideZoomRange( 0, maxZoom );
130 QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mMatrixSet.minimumZoom() ).arg( mMatrixSet.maximumZoom() ), 2 );
131
132 QgsRectangle r = reader.extent();
133 QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ),
134 QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), transformContext() );
135 ct.setBallparkTransformsAreAppropriate( true );
136 r = ct.transformBoundingBox( r );
137 setExtent( r );
138 }
139 else if ( mSourceType == QLatin1String( "vtpk" ) )
140 {
141 QgsVtpkTiles reader( mSourcePath );
142 if ( !reader.open() )
143 {
144 QgsDebugMsg( QStringLiteral( "failed to open VTPK file: " ) + mSourcePath );
145 return false;
146 }
147
148 const QVariantMap metadata = reader.metadata();
149 const QString format = metadata.value( QStringLiteral( "tileInfo" ) ).toMap().value( QStringLiteral( "format" ) ).toString();
150 if ( format != QLatin1String( "pbf" ) )
151 {
152 QgsDebugMsg( QStringLiteral( "Cannot open VTPK for vector tiles. Format = " ) + format );
153 return false;
154 }
155
156 mMatrixSet = reader.matrixSet();
157 setCrs( mMatrixSet.crs() );
158 setExtent( reader.extent( transformContext() ) );
159 }
160 else
161 {
162 QgsDebugMsg( QStringLiteral( "Unknown source type: " ) + mSourceType );
163 return false;
164 }
165
166 const QgsDataProvider::ProviderOptions providerOptions { mTransformContext };
167 const QgsDataProvider::ReadFlags flags;
168 mDataProvider.reset( new QgsVectorTileDataProvider( providerOptions, flags ) );
169 mProviderKey = mDataProvider->name();
170
171 return true;
172}
173
174bool QgsVectorTileLayer::setupArcgisVectorTileServiceConnection( const QString &uri, const QgsDataSourceUri &dataSourceUri )
175{
176 QString tileServiceUri = uri;
177 QUrl url( tileServiceUri );
178 // some services don't default to json format, while others do... so let's explicitly request it!
179 // (refs https://github.com/qgis/QGIS/issues/4231)
180 QUrlQuery query;
181 query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
182 url.setQuery( query );
183
184 QNetworkRequest request = QNetworkRequest( url );
185
186 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
187
188 QgsBlockingNetworkRequest networkRequest;
189 switch ( networkRequest.get( request ) )
190 {
192 break;
193
197 return false;
198 }
199
200 const QgsNetworkReplyContent content = networkRequest.reply();
201 const QByteArray raw = content.content();
202
203 // Parse data
204 QJsonParseError err;
205 const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
206 if ( doc.isNull() )
207 {
208 return false;
209 }
210
211 mArcgisLayerConfiguration = doc.object().toVariantMap();
212 if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
213 {
214 return false;
215 }
216
217 if ( !mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).isValid() )
218 {
219 // maybe url is pointing to a resources/styles/root.json type url, that's ok too!
220 const QString sourceUri = mArcgisLayerConfiguration.value( QStringLiteral( "sources" ) ).toMap().value( QStringLiteral( "esri" ) ).toMap().value( QStringLiteral( "url" ) ).toString();
221 if ( !sourceUri.isEmpty() )
222 {
223 QUrl url( sourceUri );
224 // some services don't default to json format, while others do... so let's explicitly request it!
225 // (refs https://github.com/qgis/QGIS/issues/4231)
226 QUrlQuery query;
227 query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
228 url.setQuery( query );
229
230 QNetworkRequest request = QNetworkRequest( url );
231
232 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
233
234 QgsBlockingNetworkRequest networkRequest;
235 switch ( networkRequest.get( request ) )
236 {
238 break;
239
243 return false;
244 }
245
246 const QgsNetworkReplyContent content = networkRequest.reply();
247 const QByteArray raw = content.content();
248
249 // Parse data
250 QJsonParseError err;
251 const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
252 if ( doc.isNull() )
253 {
254 return false;
255 }
256
257 tileServiceUri = sourceUri;
258
259 // the resources/styles/root.json configuration is actually our style definition
260 mArcgisStyleConfiguration = mArcgisLayerConfiguration;
261 mArcgisLayerConfiguration = doc.object().toVariantMap();
262 if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
263 {
264 return false;
265 }
266 }
267 }
268
269 mSourcePath = tileServiceUri + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).toList().value( 0 ).toString();
270 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
271 {
272 QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
273 return false;
274 }
275
276 mArcgisLayerConfiguration.insert( QStringLiteral( "serviceUri" ), tileServiceUri );
277
278
279 mMatrixSet.fromEsriJson( mArcgisLayerConfiguration );
280 setCrs( mMatrixSet.crs() );
281
282 // if hardcoded zoom limits aren't specified, take them from the server
283 if ( dataSourceUri.hasParam( QStringLiteral( "zmin" ) ) )
284 mMatrixSet.dropMatricesOutsideZoomRange( dataSourceUri.param( QStringLiteral( "zmin" ) ).toInt(), 99 );
285
286 if ( dataSourceUri.hasParam( QStringLiteral( "zmax" ) ) )
287 mMatrixSet.dropMatricesOutsideZoomRange( 0, dataSourceUri.param( QStringLiteral( "zmax" ) ).toInt() );
288
289 const QVariantMap fullExtent = mArcgisLayerConfiguration.value( QStringLiteral( "fullExtent" ) ).toMap();
290 if ( !fullExtent.isEmpty() )
291 {
292 const QgsRectangle fullExtentRect(
293 fullExtent.value( QStringLiteral( "xmin" ) ).toDouble(),
294 fullExtent.value( QStringLiteral( "ymin" ) ).toDouble(),
295 fullExtent.value( QStringLiteral( "xmax" ) ).toDouble(),
296 fullExtent.value( QStringLiteral( "ymax" ) ).toDouble()
297 );
298
299 const QgsCoordinateReferenceSystem fullExtentCrs = QgsArcGisRestUtils::convertSpatialReference( fullExtent.value( QStringLiteral( "spatialReference" ) ).toMap() );
300 const QgsCoordinateTransform extentTransform( fullExtentCrs, crs(), transformContext() );
301 try
302 {
303 setExtent( extentTransform.transformBoundingBox( fullExtentRect ) );
304 }
305 catch ( QgsCsException & )
306 {
307 QgsDebugMsg( QStringLiteral( "Could not transform layer fullExtent to layer CRS" ) );
308 }
309 }
310 else
311 {
312 // if no fullExtent specified in JSON, default to web mercator specs full extent
313 const QgsCoordinateTransform extentTransform( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), crs(), transformContext() );
314 try
315 {
316 setExtent( extentTransform.transformBoundingBox( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) ) );
317 }
318 catch ( QgsCsException & )
319 {
320 QgsDebugMsg( QStringLiteral( "Could not transform layer extent to layer CRS" ) );
321 }
322 }
323
324 return true;
325}
326
328
329
331{
332 const QgsVectorTileLayer::LayerOptions options( mTransformContext );
333 QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name(), options );
334 layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
335 return layer;
336}
337
339{
340 return mDataProvider.get();
341}
342
344{
345 return mDataProvider.get();
346}
347
349{
350 return new QgsVectorTileLayerRenderer( this, rendererContext );
351}
352
353bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
354{
355 setValid( loadDataSource() );
356
357 const QDomElement matrixSetElement = layerNode.firstChildElement( QStringLiteral( "matrixSet" ) );
358 if ( !matrixSetElement.isNull() )
359 {
360 mMatrixSet.readXml( matrixSetElement, context );
361 setCrs( mMatrixSet.crs() );
362 }
363
364 QString errorMsg;
365 if ( !readSymbology( layerNode, errorMsg, context ) )
366 return false;
367
368 readStyleManager( layerNode );
369 return true;
370}
371
372bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
373{
374 QDomElement mapLayerNode = layerNode.toElement();
375 mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::VectorTileLayer ) );
376
377 mapLayerNode.appendChild( mMatrixSet.writeXml( doc, context ) );
378
379 // add provider node
380 if ( mDataProvider )
381 {
382 QDomElement provider = doc.createElement( QStringLiteral( "provider" ) );
383 const QDomText providerText = doc.createTextNode( providerType() );
384 provider.appendChild( providerText );
385 mapLayerNode.appendChild( provider );
386 }
387
388 writeStyleManager( layerNode, doc );
389
390 QString errorMsg;
391 return writeSymbology( layerNode, doc, errorMsg, context );
392}
393
394bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
395{
396 const QDomElement elem = node.toElement();
397
398 readCommonStyle( elem, context, categories );
399
400 const QDomElement elemRenderer = elem.firstChildElement( QStringLiteral( "renderer" ) );
401 if ( elemRenderer.isNull() )
402 {
403 errorMessage = tr( "Missing <renderer> tag" );
404 return false;
405 }
406 const QString rendererType = elemRenderer.attribute( QStringLiteral( "type" ) );
407
408 if ( categories.testFlag( Symbology ) )
409 {
410 QgsVectorTileRenderer *r = nullptr;
411 if ( rendererType == QLatin1String( "basic" ) )
413 else
414 {
415 errorMessage = tr( "Unknown renderer type: " ) + rendererType;
416 return false;
417 }
418
419 r->readXml( elemRenderer, context );
420 setRenderer( r );
421 }
422
423 if ( categories.testFlag( Labeling ) )
424 {
425 setLabeling( nullptr );
426 const QDomElement elemLabeling = elem.firstChildElement( QStringLiteral( "labeling" ) );
427 if ( !elemLabeling.isNull() )
428 {
429 const QString labelingType = elemLabeling.attribute( QStringLiteral( "type" ) );
431 if ( labelingType == QLatin1String( "basic" ) )
433 else
434 {
435 errorMessage = tr( "Unknown labeling type: " ) + rendererType;
436 }
437
438 if ( labeling )
439 {
440 labeling->readXml( elemLabeling, context );
442 }
443 }
444 }
445
446 if ( categories.testFlag( Symbology ) )
447 {
448 // get and set the blend mode if it exists
449 const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
450 if ( !blendModeNode.isNull() )
451 {
452 const QDomElement e = blendModeNode.toElement();
453 setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
454 }
455 }
456
457 // get and set the layer transparency
458 if ( categories.testFlag( Rendering ) )
459 {
460 const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
461 if ( !layerOpacityNode.isNull() )
462 {
463 const QDomElement e = layerOpacityNode.toElement();
464 setOpacity( e.text().toDouble() );
465 }
466 }
467
468 return true;
469}
470
471bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
472{
473 Q_UNUSED( errorMessage )
474 QDomElement elem = node.toElement();
475
476 writeCommonStyle( elem, doc, context, categories );
477
478 if ( mRenderer )
479 {
480 QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
481 elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
482 if ( categories.testFlag( Symbology ) )
483 {
484 mRenderer->writeXml( elemRenderer, context );
485 }
486 elem.appendChild( elemRenderer );
487 }
488
489 if ( mLabeling && categories.testFlag( Labeling ) )
490 {
491 QDomElement elemLabeling = doc.createElement( QStringLiteral( "labeling" ) );
492 elemLabeling.setAttribute( QStringLiteral( "type" ), mLabeling->type() );
493 mLabeling->writeXml( elemLabeling, context );
494 elem.appendChild( elemLabeling );
495 }
496
497 if ( categories.testFlag( Symbology ) )
498 {
499 // add the blend mode field
500 QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
501 const QDomText blendModeText = doc.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
502 blendModeElem.appendChild( blendModeText );
503 node.appendChild( blendModeElem );
504 }
505
506 // add the layer opacity
507 if ( categories.testFlag( Rendering ) )
508 {
509 QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
510 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
511 layerOpacityElem.appendChild( layerOpacityText );
512 node.appendChild( layerOpacityElem );
513 }
514
515 return true;
516}
517
519{
520 if ( mDataProvider )
521 mDataProvider->setTransformContext( transformContext );
522
523 mTransformContext = transformContext;
525}
526
527QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
528{
529 QString error;
530 QStringList warnings;
531 resultFlag = loadDefaultStyle( error, warnings );
532 return error;
533}
534
535Qgis::MapLayerProperties QgsVectorTileLayer::properties() const
536{
537 Qgis::MapLayerProperties res;
538 if ( mSourceType == QLatin1String( "xyz" ) )
539 {
540 // always consider xyz vector tiles as basemap layers
542 }
543 else
544 {
545 // TODO when should we consider mbtiles layers as basemap layers? potentially if their extent is "large"?
546 }
547
548 return res;
549}
550
551bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
552{
553 return loadDefaultStyleAndSubLayersPrivate( error, warnings, nullptr );
554}
555
556bool QgsVectorTileLayer::loadDefaultStyleAndSubLayers( QString &error, QStringList &warnings, QList<QgsMapLayer *> &subLayers )
557{
558 return loadDefaultStyleAndSubLayersPrivate( error, warnings, &subLayers );
559}
560
561bool QgsVectorTileLayer::loadDefaultStyleAndSubLayersPrivate( QString &error, QStringList &warnings, QList<QgsMapLayer *> *subLayers )
562{
563 QgsDataSourceUri dsUri;
564 dsUri.setEncodedUri( mDataSource );
565
566 QVariantMap styleDefinition;
568 QString styleUrl;
569 if ( !dsUri.param( QStringLiteral( "styleUrl" ) ).isEmpty() )
570 {
571 styleUrl = dsUri.param( QStringLiteral( "styleUrl" ) );
572 }
573 else if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
574 {
575 // for ArcMap VectorTileServices we default to the defaultStyles URL from the layer configuration
576 styleUrl = mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString()
577 + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "defaultStyles" ) ).toString();
578 }
579
580 if ( mSourceType == QLatin1String( "vtpk" ) )
581 {
582 QgsVtpkTiles reader( mSourcePath );
583 if ( !reader.open() )
584 {
585 QgsDebugMsg( QStringLiteral( "failed to open VTPK file: " ) + mSourcePath );
586 return false;
587 }
588
589 styleDefinition = reader.styleDefinition();
590
591 const QVariantMap spriteDefinition = reader.spriteDefinition();
592 if ( !spriteDefinition.isEmpty() )
593 {
594 const QImage spriteImage = reader.spriteImage();
595 context.setSprites( spriteImage, spriteDefinition );
596 }
597 }
598 else if ( !mArcgisStyleConfiguration.isEmpty() || !styleUrl.isEmpty() )
599 {
600 if ( !mArcgisStyleConfiguration.isEmpty() )
601 {
602 styleDefinition = mArcgisStyleConfiguration;
603 }
604 else
605 {
606 QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
607
608 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );
609
610 QgsBlockingNetworkRequest networkRequest;
611 switch ( networkRequest.get( request ) )
612 {
614 break;
615
619 error = QObject::tr( "Error retrieving default style" );
620 return false;
621 }
622
623 const QgsNetworkReplyContent content = networkRequest.reply();
624 styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
625 }
626
627 if ( styleDefinition.contains( QStringLiteral( "sprite" ) ) )
628 {
629 // retrieve sprite definition
630 QString spriteUriBase;
631 if ( styleDefinition.value( QStringLiteral( "sprite" ) ).toString().startsWith( QLatin1String( "http" ) ) )
632 {
633 spriteUriBase = styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
634 }
635 else
636 {
637 spriteUriBase = styleUrl + '/' + styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
638 }
639
640 for ( int resolution = 2; resolution > 0; resolution-- )
641 {
642 QUrl spriteUrl = QUrl( spriteUriBase );
643 spriteUrl.setPath( spriteUrl.path() + QStringLiteral( "%1.json" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) );
644 QNetworkRequest request = QNetworkRequest( spriteUrl );
645 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
646 QgsBlockingNetworkRequest networkRequest;
647 switch ( networkRequest.get( request ) )
648 {
650 {
651 const QgsNetworkReplyContent content = networkRequest.reply();
652 const QVariantMap spriteDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
653
654 // retrieve sprite images
655 QUrl spriteUrl = QUrl( spriteUriBase );
656 spriteUrl.setPath( spriteUrl.path() + QStringLiteral( "%1.png" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) );
657 QNetworkRequest request = QNetworkRequest( spriteUrl );
658 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
659 QgsBlockingNetworkRequest networkRequest;
660 switch ( networkRequest.get( request ) )
661 {
663 {
664 const QgsNetworkReplyContent imageContent = networkRequest.reply();
665 const QImage spriteImage( QImage::fromData( imageContent.content() ) );
666 context.setSprites( spriteImage, spriteDefinition );
667 break;
668 }
669
673 break;
674 }
675
676 break;
677 }
678
682 break;
683 }
684
685 if ( !context.spriteDefinitions().isEmpty() )
686 break;
687 }
688 }
689 }
690
691 if ( !styleDefinition.isEmpty() )
692 {
693 // convert automatically from pixel sizes to millimeters, because pixel sizes
694 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
696 //assume source uses 96 dpi
697 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
698
700 if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
701 {
702 warnings = converter.warnings();
703 error = converter.errorMessage();
704 return false;
705 }
706
707 setRenderer( converter.renderer() );
708 setLabeling( converter.labeling() );
709 warnings = converter.warnings();
710
711 if ( subLayers )
712 {
713 *subLayers = converter.createSubLayers();
714 }
715
716 return true;
717 }
718 else
719 {
720 bool resultFlag = false;
721 error = QgsMapLayer::loadDefaultStyle( resultFlag );
722 return resultFlag;
723 }
724}
725
727{
728 QgsDataSourceUri dsUri;
729 dsUri.setEncodedUri( mDataSource );
730 if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
731 {
732 // populate default metadata
734 metadata.setIdentifier( mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString() );
735 const QString parentIdentifier = mArcgisLayerConfiguration.value( QStringLiteral( "serviceItemId" ) ).toString();
736 if ( !parentIdentifier.isEmpty() )
737 {
738 metadata.setParentIdentifier( parentIdentifier );
739 }
740 metadata.setType( QStringLiteral( "dataset" ) );
741 metadata.setTitle( mArcgisLayerConfiguration.value( QStringLiteral( "name" ) ).toString() );
742 const QString copyright = mArcgisLayerConfiguration.value( QStringLiteral( "copyrightText" ) ).toString();
743 if ( !copyright.isEmpty() )
744 metadata.setRights( QStringList() << copyright );
745 metadata.addLink( QgsAbstractMetadataBase::Link( tr( "Source" ), QStringLiteral( "WWW:LINK" ), mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString() ) );
746
748
749 resultFlag = true;
750 return QString();
751 }
752 else if ( mSourceType == QLatin1String( "vtpk" ) )
753 {
754 QgsVtpkTiles reader( mSourcePath );
755 if ( !reader.open() )
756 {
757 QgsDebugMsg( QStringLiteral( "failed to open VTPK file: " ) + mSourcePath );
758 resultFlag = false;
759 }
760 else
761 {
762 setMetadata( reader.layerMetadata() );
763 resultFlag = true;
764 }
765 return QString();
766 }
767 else
768 {
770 resultFlag = true;
771 return QString();
772 }
773}
774
775QString QgsVectorTileLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
776{
777 QgsDataSourceUri dsUri;
778 dsUri.setEncodedUri( source );
779
780 const QString sourceType = dsUri.param( QStringLiteral( "type" ) );
781 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
782 if ( sourceType == QLatin1String( "xyz" ) )
783 {
784 const QUrl sourceUrl( sourcePath );
785 if ( sourceUrl.isLocalFile() )
786 {
787 // relative path will become "file:./x.txt"
788 const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
789 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
790 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
791 return dsUri.encodedUri();
792 }
793 }
794 else if ( sourceType == QLatin1String( "mbtiles" ) )
795 {
797 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
798 dsUri.setParam( QStringLiteral( "url" ), sourcePath );
799 return dsUri.encodedUri();
800 }
801
802 return source;
803}
804
805QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
806{
807 Q_UNUSED( provider )
808
809 QgsDataSourceUri dsUri;
810 dsUri.setEncodedUri( source );
811
812 const QString sourceType = dsUri.param( QStringLiteral( "type" ) );
813 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
814 if ( sourceType == QLatin1String( "xyz" ) )
815 {
816 const QUrl sourceUrl( sourcePath );
817 if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
818 {
819 const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
820 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
821 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
822 return dsUri.encodedUri();
823 }
824 }
825 else if ( sourceType == QLatin1String( "mbtiles" ) )
826 {
828 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
829 dsUri.setParam( QStringLiteral( "url" ), sourcePath );
830 return dsUri.encodedUri();
831 }
832
833 return source;
834}
835
837{
838 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
839
840 QString info = QStringLiteral( "<html><head></head>\n<body>\n" );
841
842 info += generalHtmlMetadata();
843
844 info += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
845 QStringLiteral( "<table class=\"list-view\">\n" );
846
847 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source type" ) % QStringLiteral( "</td><td>" ) % sourceType() % QStringLiteral( "</td></tr>\n" );
848
849 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Zoom levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % QStringLiteral( "</td></tr>\n" );
850
851 info += QLatin1String( "</table>\n<br>" );
852
853 // CRS
854 info += crsHtmlMetadata();
855
856 // Identification section
857 info += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
858 htmlFormatter.identificationSectionHtml() %
859 QStringLiteral( "<br>\n" ) %
860
861 // extent section
862 QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
863 htmlFormatter.extentSectionHtml( ) %
864 QStringLiteral( "<br>\n" ) %
865
866 // Start the Access section
867 QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
868 htmlFormatter.accessSectionHtml( ) %
869 QStringLiteral( "<br>\n" ) %
870
871
872 // Start the contacts section
873 QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
874 htmlFormatter.contactsSectionHtml( ) %
875 QStringLiteral( "<br><br>\n" ) %
876
877 // Start the links section
878 QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
879 htmlFormatter.linksSectionHtml( ) %
880 QStringLiteral( "<br>\n" ) %
881
882 // Start the history section
883 QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
884 htmlFormatter.historySectionHtml( ) %
885 QStringLiteral( "<br>\n" ) %
886
887 QStringLiteral( "\n</body>\n</html>\n" );
888
889 return info;
890}
891
893{
894 const QgsTileMatrix tileMatrix = mMatrixSet.tileMatrix( tileID.zoomLevel() );
895 const QgsTileRange tileRange( tileID.column(), tileID.column(), tileID.row(), tileID.row() );
896
897 QgsDataSourceUri dsUri;
898 dsUri.setEncodedUri( mDataSource );
899 const QString authcfg = dsUri.authConfigId();
900
901 QList<QgsVectorTileRawData> rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, tileMatrix, QPointF(), tileRange, authcfg, dsUri.httpHeaders() );
902 if ( rawTiles.isEmpty() )
903 return QByteArray();
904 return rawTiles.first().data;
905}
906
908{
909 mRenderer.reset( r );
911}
912
914{
915 return mRenderer.get();
916}
917
919{
920 mLabeling.reset( labeling );
922}
923
925{
926 return mLabeling.get();
927}
928
930{
931 QList< QgsFeature > res;
932 res.reserve( mSelectedFeatures.size() );
933 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
934 res.append( it.value() );
935
936 return res;
937}
938
940{
941 return mSelectedFeatures.size();
942}
943
944void QgsVectorTileLayer::selectByGeometry( const QgsGeometry &geometry, const QgsSelectionContext &context, Qgis::SelectBehavior behavior, Qgis::SelectGeometryRelationship relationship, Qgis::SelectionFlags flags, QgsRenderContext *renderContext )
945{
946 if ( !isInScaleRange( context.scale() ) )
947 {
948 QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
949 return;
950 }
951
952 QSet< QgsFeatureId > prevSelection;
953 prevSelection.reserve( mSelectedFeatures.size() );
954 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
955 prevSelection.insert( it.key() );
956
958 {
959 switch ( behavior )
960 {
962 mSelectedFeatures.clear();
963 break;
964
968 break;
969 }
970 }
971
972 QgsGeometry selectionGeom = geometry;
973 bool isPointOrRectangle;
974 QgsPointXY point;
975 bool isSinglePoint = selectionGeom.type() == QgsWkbTypes::PointGeometry;
976 if ( isSinglePoint )
977 {
978 isPointOrRectangle = true;
979 point = selectionGeom.asPoint();
981 }
982 else
983 {
984 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly instersection tests later
985 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
986 }
987
988 auto addDerivedFields = []( QgsFeature & feature, const int tileZoom, const QString & layer )
989 {
990 QgsFields fields = feature.fields();
991 fields.append( QgsField( QStringLiteral( "tile_zoom" ), QVariant::Int ) );
992 fields.append( QgsField( QStringLiteral( "tile_layer" ), QVariant::String ) );
993 QgsAttributes attributes = feature.attributes();
994 attributes << tileZoom << layer;
995 feature.setFields( fields );
996 feature.setAttributes( attributes );
997 };
998
999 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
1000 QList< QgsFeature > singleSelectCandidates;
1001
1002 QgsRectangle r;
1003 if ( isSinglePoint )
1004 {
1005 r = QgsRectangle( point.x(), point.y(), point.x(), point.y() );
1006 }
1007 else
1008 {
1009 r = selectionGeom.boundingBox();
1010
1011 if ( !isPointOrRectangle || relationship == Qgis::SelectGeometryRelationship::Within )
1012 {
1013 // use prepared geometry for faster intersection test
1014 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
1015 }
1016 }
1017
1018 switch ( behavior )
1019 {
1022 {
1023 // when adding to or setting a selection, we retrieve the tile data for the current scale
1024 const int tileZoom = tileMatrixSet().scaleToZoomLevel( context.scale() );
1025 const QgsTileMatrix tileMatrix = tileMatrixSet().tileMatrix( tileZoom );
1026 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
1027
1028 for ( int row = tileRange.startRow(); row <= tileRange.endRow(); ++row )
1029 {
1030 for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); ++col )
1031 {
1032 QgsTileXYZ tileID( col, row, tileZoom );
1033 QByteArray data = getRawTile( tileID );
1034 if ( data.isEmpty() )
1035 continue; // failed to get data
1036
1038 if ( !decoder.decode( tileID, data ) )
1039 continue; // failed to decode
1040
1041 QMap<QString, QgsFields> perLayerFields;
1042 const QStringList layerNames = decoder.layers();
1043 for ( const QString &layerName : layerNames )
1044 {
1045 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
1046 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
1047 }
1048
1049 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
1050 const QStringList featuresLayerNames = features.keys();
1051 for ( const QString &layerName : featuresLayerNames )
1052 {
1053 const QgsFields fFields = perLayerFields[layerName];
1054 const QVector<QgsFeature> &layerFeatures = features[layerName];
1055 for ( const QgsFeature &f : layerFeatures )
1056 {
1057 if ( renderContext && mRenderer && !mRenderer->willRenderFeature( f, tileZoom, layerName, *renderContext ) )
1058 continue;
1059
1060 if ( f.geometry().intersects( r ) )
1061 {
1062 bool selectFeature = true;
1063 if ( selectionGeomPrepared )
1064 {
1065 switch ( relationship )
1066 {
1068 selectFeature = selectionGeomPrepared->intersects( f.geometry().constGet() );
1069 break;
1071 selectFeature = selectionGeomPrepared->contains( f.geometry().constGet() );
1072 break;
1073 }
1074 }
1075
1076 if ( selectFeature )
1077 {
1078 QgsFeature derivedFeature = f;
1079 addDerivedFields( derivedFeature, tileZoom, layerName );
1081 singleSelectCandidates << derivedFeature;
1082 else
1083 mSelectedFeatures.insert( derivedFeature.id(), derivedFeature );
1084 }
1085 }
1086 }
1087 }
1088 }
1089 }
1090 break;
1091 }
1092
1095 {
1096 // when removing from the selection, we instead just iterate over the current selection and test against the geometry
1097 // we do this as we don't want the selection removal operation to depend at all on the tile zoom
1098 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); )
1099 {
1100 bool matchesGeometry = false;
1101 if ( selectionGeomPrepared )
1102 {
1103 switch ( relationship )
1104 {
1106 matchesGeometry = selectionGeomPrepared->intersects( it->geometry().constGet() );
1107 break;
1109 matchesGeometry = selectionGeomPrepared->contains( it->geometry().constGet() );
1110 break;
1111 }
1112 }
1113 else
1114 {
1115 switch ( relationship )
1116 {
1118 matchesGeometry = it->geometry().intersects( r );
1119 break;
1121 matchesGeometry = r.contains( it->geometry().boundingBox() );
1122 break;
1123 }
1124 }
1125
1127 {
1128 singleSelectCandidates << it.value();
1129 it++;
1130 }
1131 else if ( ( matchesGeometry && behavior == Qgis::SelectBehavior::IntersectSelection )
1132 || ( !matchesGeometry && behavior == Qgis::SelectBehavior::RemoveFromSelection ) )
1133 {
1134 it++;
1135 }
1136 else
1137 {
1138 it = mSelectedFeatures.erase( it );
1139 }
1140 }
1141 break;
1142 }
1143 }
1144
1145 if ( ( flags & Qgis::SelectionFlag::SingleFeatureSelection ) && !singleSelectCandidates.empty() )
1146 {
1147 QgsFeature bestCandidate;
1148
1150 {
1151 // when toggling a selection, we first check to see if we can remove a feature from the current selection -- that takes precedence over adding new features to the selection
1152
1153 // find smallest feature in the current selection
1154 double smallestArea = std::numeric_limits< double >::max();
1155 double smallestLength = std::numeric_limits< double >::max();
1156 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
1157 {
1158 if ( !mSelectedFeatures.contains( candidate.id() ) )
1159 continue;
1160
1161 switch ( candidate.geometry().type() )
1162 {
1164 bestCandidate = candidate;
1165 break;
1167 {
1168 const double length = candidate.geometry().length();
1169 if ( length < smallestLength && bestCandidate.geometry().type() != QgsWkbTypes::PointGeometry )
1170 {
1171 bestCandidate = candidate;
1172 smallestLength = length;
1173 }
1174 break;
1175 }
1177 {
1178 const double area = candidate.geometry().area();
1179 if ( area < smallestArea && bestCandidate.geometry().type() != QgsWkbTypes::PointGeometry && bestCandidate.geometry().type() != QgsWkbTypes::LineGeometry )
1180 {
1181 bestCandidate = candidate;
1182 smallestArea = area;
1183 }
1184 break;
1185 }
1188 break;
1189 }
1190 }
1191 }
1192
1193 if ( !bestCandidate.isValid() )
1194 {
1195 // find smallest feature (ie. pick the "hardest" one to click on)
1196 double smallestArea = std::numeric_limits< double >::max();
1197 double smallestLength = std::numeric_limits< double >::max();
1198 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
1199 {
1200 switch ( candidate.geometry().type() )
1201 {
1203 bestCandidate = candidate;
1204 break;
1206 {
1207 const double length = candidate.geometry().length();
1208 if ( length < smallestLength && bestCandidate.geometry().type() != QgsWkbTypes::PointGeometry )
1209 {
1210 bestCandidate = candidate;
1211 smallestLength = length;
1212 }
1213 break;
1214 }
1216 {
1217 const double area = candidate.geometry().area();
1218 if ( area < smallestArea && bestCandidate.geometry().type() != QgsWkbTypes::PointGeometry && bestCandidate.geometry().type() != QgsWkbTypes::LineGeometry )
1219 {
1220 bestCandidate = candidate;
1221 smallestArea = area;
1222 }
1223 break;
1224 }
1227 break;
1228 }
1229 }
1230 }
1231
1233 {
1234 if ( prevSelection.contains( bestCandidate.id() ) )
1235 mSelectedFeatures.remove( bestCandidate.id() );
1236 else
1237 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
1238 }
1239 else
1240 {
1241 switch ( behavior )
1242 {
1245 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
1246 break;
1247
1249 {
1250 if ( mSelectedFeatures.contains( bestCandidate.id() ) )
1251 {
1252 mSelectedFeatures.clear();
1253 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
1254 }
1255 else
1256 {
1257 mSelectedFeatures.clear();
1258 }
1259 break;
1260 }
1261
1263 {
1264 mSelectedFeatures.remove( bestCandidate.id() );
1265 break;
1266 }
1267 }
1268 }
1269 }
1270
1271 QSet< QgsFeatureId > newSelection;
1272 newSelection.reserve( mSelectedFeatures.size() );
1273 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
1274 newSelection.insert( it.key() );
1275
1276 // signal
1277 if ( prevSelection != newSelection )
1278 emit selectionChanged();
1279}
1280
1282{
1283 if ( mSelectedFeatures.empty() )
1284 return;
1285
1286 mSelectedFeatures.clear();
1287 emit selectionChanged();
1288}
1289
1290
1291//
1292// QgsVectorTileDataProvider
1293//
1295QgsVectorTileDataProvider::QgsVectorTileDataProvider(
1296 const ProviderOptions &options,
1297 QgsDataProvider::ReadFlags flags )
1298 : QgsDataProvider( QString(), options, flags )
1299{}
1300
1302{
1303 return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
1304}
1305
1306QString QgsVectorTileDataProvider::name() const
1307{
1308 return QStringLiteral( "vectortile" );
1309}
1310
1311QString QgsVectorTileDataProvider::description() const
1312{
1313 return QString();
1314}
1315
1316QgsRectangle QgsVectorTileDataProvider::extent() const
1317{
1318 return QgsRectangle();
1319}
1320
1321bool QgsVectorTileDataProvider::isValid() const
1322{
1323 return true;
1324}
1325
1326bool QgsVectorTileDataProvider::renderInPreview( const PreviewContext &context )
1327{
1328 // Vector tiles by design are very CPU light to render, so we are much more permissive here compared
1329 // with other layer types. (Generally if a vector tile layer has taken more than a few milliseconds to render it's
1330 // a result of network requests, and the tile manager class handles these gracefully for us)
1331 return context.lastRenderingTimeMs <= 1000;
1332}
1333
@ IsBasemapLayer
Layer is considered a 'basemap' layer, and certain properties of the layer should be ignored when cal...
@ ToggleSelection
Enables a "toggle" selection mode, where previously selected matching features will be deselected and...
@ SingleFeatureSelection
Select only a single feature, picking the "best" match for the selection geometry.
SelectGeometryRelationship
Geometry relationship test to apply for selecting features.
Definition: qgis.h:812
@ Within
Select where features are within the reference geometry.
@ Intersect
Select where features intersect the reference geometry.
SelectBehavior
Specifies how a selection should be applied.
Definition: qgis.h:798
@ SetSelection
Set selection, removing any existing selection.
@ AddToSelection
Add selection to current selection.
@ IntersectSelection
Modify current selection to include only select features which match.
@ RemoveFromSelection
Remove from current selection.
void setType(const QString &type)
Sets the type (nature) of the resource.
void setParentIdentifier(const QString &parentIdentifier)
Sets a reference, URI, URL or some other mechanism to identify the parent resource that this resource...
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results.
void setIdentifier(const QString &identifier)
Sets the reference, URI, URL or some other mechanism to identify the resource.
void addLink(const QgsAbstractMetadataBase::Link &link)
Adds an individual link to the existing links.
static QgsCoordinateReferenceSystem convertSpatialReference(const QVariantMap &spatialReferenceMap)
Converts a spatial reference JSON definition to a QgsCoordinateReferenceSystem value.
A vector of attributes.
Definition: qgsattributes.h:59
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
@ NetworkError
A network error occurred.
@ ServerExceptionError
An exception was raised by the server.
@ NoError
No error was encountered.
@ TimeoutError
Timeout was reached before a reply was received.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Abstract base class for spatial data provider implementations.
Class for storing the component parts of a RDBMS data source URI (e.g.
QByteArray encodedUri() const
Returns the complete encoded URI as a byte array.
bool hasParam(const QString &key) const
Returns true if a parameter with the specified key exists.
int removeParam(const QString &key)
Removes a generic parameter by key.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
QgsHttpHeaders httpHeaders() const
Returns http headers.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on the URI.
QString authConfigId() const
Returns any associated authentication configuration ID stored in the URI.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsFields fields
Definition: qgsfeature.h:66
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:160
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:198
QgsGeometry geometry
Definition: qgsfeature.h:67
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:219
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:167
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
Class for metadata formatter.
QString linksSectionHtml() const
Formats the "Links" section according to a metadata object.
QString extentSectionHtml(const bool showSpatialExtent=true) const
Formats the "Extents" section according to a metadata object (extent and temporal).
QString contactsSectionHtml() const
Formats the "Contacts" section according to a metadata object.
QString identificationSectionHtml() const
Formats the "Identification" section according to a metadata object.
QString historySectionHtml() const
Formats the "History" section according to a metadata object.
QString accessSectionHtml() const
Formats the "Access" section according to a metadata object.
A structured metadata store for a map layer.
void setRights(const QStringList &rights)
Sets a list of rights (attribution or copyright strings) associated with the resource.
Context for a MapBox GL style conversion operation.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
void setSprites(const QImage &image, const QVariantMap &definitions)
Sets the sprite image and definitions JSON to use during conversion.
void setTargetUnit(QgsUnitTypes::RenderUnit targetUnit)
Sets the target unit type.
QVariantMap spriteDefinitions() const
Returns the sprite definitions to use during conversion.
Handles conversion of MapBox GL styles to QGIS vector tile renderers and labeling settings.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
QgsVectorTileLabeling * labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
QList< QgsMapLayer * > createSubLayers() const
Returns a list of new map layers corresponding to sublayers of the style, e.g.
@ Success
Conversion was successful.
QString errorMessage() const
Returns a descriptive error message if an error was encountered during the style conversion,...
QStringList warnings() const
Returns a list of user-friendly warnings generated during the conversion, e.g.
static QString typeToString(QgsMapLayerType type)
Converts a map layer type to a string value.
Base class for utility classes that encapsulate information necessary for rendering of map layers.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
void readStyleManager(const QDomNode &layerNode)
Read style manager's configuration (if any). To be called by subclasses.
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
void writeStyleManager(QDomNode &layerNode, QDomDocument &doc) const
Write style manager's configuration (if exists). To be called by subclasses.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
virtual QString loadDefaultStyle(bool &resultFlag)
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
QString mLayerName
Name of the layer - used for display.
Definition: qgsmaplayer.h:1945
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
QString crsHtmlMetadata() const
Returns a HTML fragment containing the layer's CRS metadata, for use in the htmlMetadata() method.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:78
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
virtual void setOpacity(double opacity)
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
virtual void setMetadata(const QgsLayerMetadata &metadata)
Sets the layer's metadata store.
QString mProviderKey
Data provider key (name of the data provider)
Definition: qgsmaplayer.h:1983
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
virtual QgsError error() const
Gets current status error.
virtual QStringList subLayers() const
Returns the sublayers of this layer.
virtual void setExtent(const QgsRectangle &rect)
Sets the extent.
bool isValid
Definition: qgsmaplayer.h:81
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:1942
virtual QString loadDefaultMetadata(bool &resultFlag)
Retrieve the default metadata for this layer if one exists (either as a .qmd file on disk or as a rec...
void setValid(bool valid)
Sets whether layer is valid or not.
void readCommonStyle(const QDomElement &layerElement, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)
Read style data common to all layer types.
double opacity
Definition: qgsmaplayer.h:82
@ Symbology
Symbology.
Definition: qgsmaplayer.h:161
@ Rendering
Rendering: scale visibility, simplify method, opacity.
Definition: qgsmaplayer.h:170
@ Labeling
Labeling.
Definition: qgsmaplayer.h:163
QString generalHtmlMetadata() const
Returns an HTML fragment containing general metadata information, for use in the htmlMetadata() metho...
void writeCommonStyle(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const
Write style data common to all layer types.
void invalidateWgs84Extent()
Invalidates the WGS84 extent.
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
Utility class for reading and writing MBTiles files (which are SQLite3 databases).
Definition: qgsmbtiles.h:39
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
BlendMode
Blending modes enum defining the available composition modes that can be used when rendering a layer.
Definition: qgspainting.h:37
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
The class is used as a container of context for various read/write operations on other objects.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool contains(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:363
Contains information about the context of a rendering operation.
Encapsulates the context of a layer selection operation.
double scale() const
Returns the map scale at which the selection should occur.
virtual QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Writes the set to an XML element.
Definition: qgstiles.cpp:363
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system associated with the tiles.
Definition: qgstiles.cpp:206
int minimumZoom() const
Returns the minimum zoom level for tiles present in the set.
Definition: qgstiles.cpp:169
int scaleToZoomLevel(double scale) const
Finds the best fitting (integer) zoom level given a map scale denominator.
Definition: qgstiles.cpp:256
int maximumZoom() const
Returns the maximum zoom level for tiles present in the set.
Definition: qgstiles.cpp:180
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition: qgstiles.cpp:149
virtual bool readXml(const QDomElement &element, QgsReadWriteContext &context)
Reads the set from an XML element.
Definition: qgstiles.cpp:314
void dropMatricesOutsideZoomRange(int minimumZoom, int maximumZoom)
Deletes any existing matrices which fall outside the zoom range specified by minimumZoom to maximumZo...
Definition: qgstiles.cpp:191
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition: qgstiles.h:108
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition: qgstiles.cpp:96
Range of tiles in a tile matrix to be rendered.
Definition: qgstiles.h:71
int endColumn() const
Returns index of the last column in the range.
Definition: qgstiles.h:83
int endRow() const
Returns index of the last row in the range.
Definition: qgstiles.h:87
int startRow() const
Returns index of the first row in the range.
Definition: qgstiles.h:85
int startColumn() const
Returns index of the first column in the range.
Definition: qgstiles.h:81
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
int zoomLevel() const
Returns tile's zoom level (Z)
Definition: qgstiles.h:51
int column() const
Returns tile's column index (X)
Definition: qgstiles.h:47
int row() const
Returns tile's row index (Y)
Definition: qgstiles.h:49
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
Basic labeling configuration for vector tile layers.
The default vector tile renderer implementation.
static QList< QgsVectorTileBasicRendererStyle > simpleStyleWithRandomColors()
Returns a list of styles to render all layers, using random colors.
Base class for labeling configuration classes for vector tile layers.
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)=0
Reads labeling properties from given XML element.
This class provides map rendering functionality for vector tile layers.
Implements a map layer that is dedicated to rendering of vector tiles.
QgsVectorTileLayer(const QString &path=QString(), const QString &baseName=QString(), const QgsVectorTileLayer::LayerOptions &options=QgsVectorTileLayer::LayerOptions())
Constructs a new vector tile layer.
QByteArray getRawTile(QgsTileXYZ tileID)
Fetches raw tile data for the give tile coordinates.
bool readXml(const QDomNode &layerNode, QgsReadWriteContext &context) override
Called by readLayerXML(), used by children to read state specific to them from project files.
bool writeSymbology(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const override
Write the style for the layer into the document provided.
void setRenderer(QgsVectorTileRenderer *r)
Sets renderer for the map layer.
bool loadDefaultStyleAndSubLayers(QString &error, QStringList &warnings, QList< QgsMapLayer * > &subLayers)
Loads the default style for the layer, and returns true if the style was successfully loaded.
QString decodedSource(const QString &source, const QString &provider, const QgsReadWriteContext &context) const FINAL
Called by readLayerXML(), used by derived classes to decode provider's specific data source from proj...
QString sourceType() const
Returns type of the data source.
bool writeXml(QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context) const override
Called by writeLayerXML(), used by children to write state specific to them to project files.
void selectByGeometry(const QgsGeometry &geometry, const QgsSelectionContext &context, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection, Qgis::SelectGeometryRelationship relationship=Qgis::SelectGeometryRelationship::Intersect, Qgis::SelectionFlags flags=Qgis::SelectionFlags(), QgsRenderContext *renderContext=nullptr)
Selects features found within the search geometry (in layer's coordinates).
void setLabeling(QgsVectorTileLabeling *labeling)
Sets labeling for the map layer.
QList< QgsFeature > selectedFeatures() const
Returns the list of features currently selected in the layer.
QgsVectorTileLabeling * labeling() const
Returns currently assigned labeling.
~QgsVectorTileLayer() override
bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) override
Read the symbology for the current layer from the DOM node supplied.
int sourceMinZoom() const
Returns minimum zoom level at which source has any valid tiles (negative = unconstrained)
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) override
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
QString loadDefaultStyle(bool &resultFlag) override
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
Qgis::MapLayerProperties properties() const override
Returns the map layer properties of this layer.
QString htmlMetadata() const override
Obtain a formatted HTML string containing assorted metadata for this layer.
QString encodedSource(const QString &source, const QgsReadWriteContext &context) const FINAL
Called by writeLayerXML(), used by derived classes to encode provider's specific data source to proje...
int sourceMaxZoom() const
Returns maximum zoom level at which source has any valid tiles (negative = unconstrained)
QgsVectorTileRenderer * renderer() const
Returns currently assigned renderer.
void removeSelection()
Clear selection.
QgsDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
QString loadDefaultMetadata(bool &resultFlag) override
Retrieve the default metadata for this layer if one exists (either as a .qmd file on disk or as a rec...
QString sourcePath() const
Returns URL/path of the data source (syntax different to each data source type)
void setTransformContext(const QgsCoordinateTransformContext &transformContext) override
Sets the coordinate transform context to transformContext.
QgsVectorTileMatrixSet & tileMatrixSet()
Returns the vector tile matrix set.
void selectionChanged()
Emitted whenever the selected features in the layer are changed.
QgsVectorTileLayer * clone() const override
Returns a new instance equivalent to this one except for the id which is still unique.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
static QList< QgsVectorTileRawData > blockingFetchTileRawData(const QString &sourceType, const QString &sourcePath, const QgsTileMatrix &tileMatrix, const QPointF &viewCenter, const QgsTileRange &range, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback=nullptr)
Returns raw tile data for the specified range of tiles. Blocks the caller until all tiles are fetched...
This class is responsible for decoding raw tile data written with Mapbox Vector Tiles encoding.
QStringList layerFieldNames(const QString &layerName) const
Returns a list of all field names in a tile. It can only be called after a successful decode()
QStringList layers() const
Returns a list of sub-layer names in a tile. It can only be called after a successful decode()
QgsVectorTileFeatures layerFeatures(const QMap< QString, QgsFields > &perLayerFields, const QgsCoordinateTransform &ct, const QSet< QString > *layerSubset=nullptr) const
Returns decoded features grouped by sub-layers.
bool decode(QgsTileXYZ tileID, const QByteArray &rawTileData)
Tries to decode raw tile data, returns true on success.
bool fromEsriJson(const QVariantMap &json)
Initializes the tile structure settings from an ESRI REST VectorTileService json map.
static QgsVectorTileMatrixSet fromWebMercator(int minimumZoom=0, int maximumZoom=14)
Returns a vector tile structure corresponding to the standard web mercator/GoogleCRS84Quad setup.
Abstract base class for all vector tile renderer implementations.
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)=0
Reads renderer's properties from given XML element.
static bool checkXYZUrlTemplate(const QString &url)
Checks whether the URL template string is correct (contains {x}, {y} / {-y}, {z} placeholders)
static QgsFields makeQgisFields(const QSet< QString > &flds)
Returns QgsFields instance based on the set of field names.
Utility class for reading and writing ESRI VTPK files.
Definition: qgsvtpktiles.h:38
QgsLayerMetadata layerMetadata() const
Reads layer metadata from the VTPK file.
bool open()
Tries to open the file, returns true on success.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
#define QgsSetRequestInitiatorClass(request, _class)
QMap< QString, QVector< QgsFeature > > QgsVectorTileFeatures
Features of a vector tile, grouped by sub-layer names (key of the map)
const QgsCoordinateReferenceSystem & crs
Setting options for creating vector data providers.
Setting options for loading vector tile layers.