QGIS API Documentation 3.29.0-Master (da8bb1db43)
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#include "qgsthreadingutils.h"
42
43#include <QUrl>
44#include <QUrlQuery>
45
46QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName, const LayerOptions &options )
48 , mTransformContext( options.transformContext )
49{
51
52 mDataSource = uri;
53
54 setValid( loadDataSource() );
55
56 // set a default renderer
60
61 connect( this, &QgsVectorTileLayer::selectionChanged, this, [ = ] { triggerRepaint(); } );
62}
63
64void QgsVectorTileLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &, const QgsDataProvider::ProviderOptions &, QgsDataProvider::ReadFlags )
65{
67
68 mDataSource = dataSource;
69 mLayerName = baseName;
70 mDataProvider.reset();
71
72 setValid( loadDataSource() );
73}
74
75bool QgsVectorTileLayer::loadDataSource()
76{
78
79 QgsDataSourceUri dsUri;
81
82 setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
83
84 mSourceType = dsUri.param( QStringLiteral( "type" ) );
85 mSourcePath = dsUri.param( QStringLiteral( "url" ) );
86 if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
87 {
88 if ( !setupArcgisVectorTileServiceConnection( mSourcePath, dsUri ) )
89 return false;
90 }
91 else if ( mSourceType == QLatin1String( "xyz" ) )
92 {
93 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
94 {
95 QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
96 return false;
97 }
98
99 // online tiles
100 int zMin = 0;
101 if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
102 zMin = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
103
104 int zMax = 14;
105 if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
106 zMax = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
107
108 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
109 setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );
110 }
111 else if ( mSourceType == QLatin1String( "mbtiles" ) )
112 {
113 QgsMbTiles reader( mSourcePath );
114 if ( !reader.open() )
115 {
116 QgsDebugMsg( QStringLiteral( "failed to open MBTiles file: " ) + mSourcePath );
117 return false;
118 }
119
120 const QString format = reader.metadataValue( QStringLiteral( "format" ) );
121 if ( format != QLatin1String( "pbf" ) )
122 {
123 QgsDebugMsg( QStringLiteral( "Cannot open MBTiles for vector tiles. Format = " ) + format );
124 return false;
125 }
126
127 QgsDebugMsgLevel( QStringLiteral( "name: " ) + reader.metadataValue( QStringLiteral( "name" ) ), 2 );
128 bool minZoomOk, maxZoomOk;
129 const int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk );
130 const int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk );
131 if ( minZoomOk )
132 mMatrixSet.dropMatricesOutsideZoomRange( minZoom, 99 );
133 if ( maxZoomOk )
134 mMatrixSet.dropMatricesOutsideZoomRange( 0, maxZoom );
135 QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mMatrixSet.minimumZoom() ).arg( mMatrixSet.maximumZoom() ), 2 );
136
137 QgsRectangle r = reader.extent();
138 QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ),
139 QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), transformContext() );
140 ct.setBallparkTransformsAreAppropriate( true );
141 r = ct.transformBoundingBox( r );
142 setExtent( r );
143 }
144 else if ( mSourceType == QLatin1String( "vtpk" ) )
145 {
146 QgsVtpkTiles reader( mSourcePath );
147 if ( !reader.open() )
148 {
149 QgsDebugMsg( QStringLiteral( "failed to open VTPK file: " ) + mSourcePath );
150 return false;
151 }
152
153 const QVariantMap metadata = reader.metadata();
154 const QString format = metadata.value( QStringLiteral( "tileInfo" ) ).toMap().value( QStringLiteral( "format" ) ).toString();
155 if ( format != QLatin1String( "pbf" ) )
156 {
157 QgsDebugMsg( QStringLiteral( "Cannot open VTPK for vector tiles. Format = " ) + format );
158 return false;
159 }
160
161 mMatrixSet = reader.matrixSet();
162 setCrs( mMatrixSet.crs() );
163 setExtent( reader.extent( transformContext() ) );
164 }
165 else
166 {
167 QgsDebugMsg( QStringLiteral( "Unknown source type: " ) + mSourceType );
168 return false;
169 }
170
171 const QgsDataProvider::ProviderOptions providerOptions { mTransformContext };
172 const QgsDataProvider::ReadFlags flags;
173 mDataProvider.reset( new QgsVectorTileDataProvider( providerOptions, flags ) );
174 mProviderKey = mDataProvider->name();
175
176 return true;
177}
178
179bool QgsVectorTileLayer::setupArcgisVectorTileServiceConnection( const QString &uri, const QgsDataSourceUri &dataSourceUri )
180{
182
183 QString tileServiceUri = uri;
184 QUrl url( tileServiceUri );
185 // some services don't default to json format, while others do... so let's explicitly request it!
186 // (refs https://github.com/qgis/QGIS/issues/4231)
187 QUrlQuery query;
188 query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
189 url.setQuery( query );
190
191 QNetworkRequest request = QNetworkRequest( url );
192
193 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
194
195 QgsBlockingNetworkRequest networkRequest;
196 switch ( networkRequest.get( request ) )
197 {
199 break;
200
204 return false;
205 }
206
207 const QgsNetworkReplyContent content = networkRequest.reply();
208 const QByteArray raw = content.content();
209
210 // Parse data
211 QJsonParseError err;
212 const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
213 if ( doc.isNull() )
214 {
215 return false;
216 }
217
218 mArcgisLayerConfiguration = doc.object().toVariantMap();
219 if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
220 {
221 return false;
222 }
223
224 if ( !mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).isValid() )
225 {
226 // maybe url is pointing to a resources/styles/root.json type url, that's ok too!
227 const QString sourceUri = mArcgisLayerConfiguration.value( QStringLiteral( "sources" ) ).toMap().value( QStringLiteral( "esri" ) ).toMap().value( QStringLiteral( "url" ) ).toString();
228 if ( !sourceUri.isEmpty() )
229 {
230 QUrl url( sourceUri );
231 // some services don't default to json format, while others do... so let's explicitly request it!
232 // (refs https://github.com/qgis/QGIS/issues/4231)
233 QUrlQuery query;
234 query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
235 url.setQuery( query );
236
237 QNetworkRequest request = QNetworkRequest( url );
238
239 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
240
241 QgsBlockingNetworkRequest networkRequest;
242 switch ( networkRequest.get( request ) )
243 {
245 break;
246
250 return false;
251 }
252
253 const QgsNetworkReplyContent content = networkRequest.reply();
254 const QByteArray raw = content.content();
255
256 // Parse data
257 QJsonParseError err;
258 const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
259 if ( doc.isNull() )
260 {
261 return false;
262 }
263
264 tileServiceUri = sourceUri;
265
266 // the resources/styles/root.json configuration is actually our style definition
267 mArcgisStyleConfiguration = mArcgisLayerConfiguration;
268 mArcgisLayerConfiguration = doc.object().toVariantMap();
269 if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
270 {
271 return false;
272 }
273 }
274 }
275
276 mSourcePath = tileServiceUri + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).toList().value( 0 ).toString();
277 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
278 {
279 QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
280 return false;
281 }
282
283 mArcgisLayerConfiguration.insert( QStringLiteral( "serviceUri" ), tileServiceUri );
284
285
286 mMatrixSet.fromEsriJson( mArcgisLayerConfiguration );
287 setCrs( mMatrixSet.crs() );
288
289 // if hardcoded zoom limits aren't specified, take them from the server
290 if ( dataSourceUri.hasParam( QStringLiteral( "zmin" ) ) )
291 mMatrixSet.dropMatricesOutsideZoomRange( dataSourceUri.param( QStringLiteral( "zmin" ) ).toInt(), 99 );
292
293 if ( dataSourceUri.hasParam( QStringLiteral( "zmax" ) ) )
294 mMatrixSet.dropMatricesOutsideZoomRange( 0, dataSourceUri.param( QStringLiteral( "zmax" ) ).toInt() );
295
296 const QVariantMap fullExtent = mArcgisLayerConfiguration.value( QStringLiteral( "fullExtent" ) ).toMap();
297 if ( !fullExtent.isEmpty() )
298 {
299 const QgsRectangle fullExtentRect(
300 fullExtent.value( QStringLiteral( "xmin" ) ).toDouble(),
301 fullExtent.value( QStringLiteral( "ymin" ) ).toDouble(),
302 fullExtent.value( QStringLiteral( "xmax" ) ).toDouble(),
303 fullExtent.value( QStringLiteral( "ymax" ) ).toDouble()
304 );
305
306 const QgsCoordinateReferenceSystem fullExtentCrs = QgsArcGisRestUtils::convertSpatialReference( fullExtent.value( QStringLiteral( "spatialReference" ) ).toMap() );
307 const QgsCoordinateTransform extentTransform( fullExtentCrs, crs(), transformContext() );
308 try
309 {
310 setExtent( extentTransform.transformBoundingBox( fullExtentRect ) );
311 }
312 catch ( QgsCsException & )
313 {
314 QgsDebugMsg( QStringLiteral( "Could not transform layer fullExtent to layer CRS" ) );
315 }
316 }
317 else
318 {
319 // if no fullExtent specified in JSON, default to web mercator specs full extent
320 const QgsCoordinateTransform extentTransform( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), crs(), transformContext() );
321 try
322 {
323 setExtent( extentTransform.transformBoundingBox( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) ) );
324 }
325 catch ( QgsCsException & )
326 {
327 QgsDebugMsg( QStringLiteral( "Could not transform layer extent to layer CRS" ) );
328 }
329 }
330
331 return true;
332}
333
335
336
338{
340
341 const QgsVectorTileLayer::LayerOptions options( mTransformContext );
342 QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name(), options );
343 layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
344 return layer;
345}
346
348{
350
351 return mDataProvider.get();
352}
353
355{
357
358 return mDataProvider.get();
359}
360
362{
364
365 return new QgsVectorTileLayerRenderer( this, rendererContext );
366}
367
368bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
369{
371
372 setValid( loadDataSource() );
373
374 const QDomElement matrixSetElement = layerNode.firstChildElement( QStringLiteral( "matrixSet" ) );
375 if ( !matrixSetElement.isNull() )
376 {
377 mMatrixSet.readXml( matrixSetElement, context );
378 setCrs( mMatrixSet.crs() );
379 }
380
381 QString errorMsg;
382 if ( !readSymbology( layerNode, errorMsg, context ) )
383 return false;
384
385 readStyleManager( layerNode );
386 return true;
387}
388
389bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
390{
392
393 QDomElement mapLayerNode = layerNode.toElement();
394 mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::VectorTileLayer ) );
395
396 mapLayerNode.appendChild( mMatrixSet.writeXml( doc, context ) );
397
398 // add provider node
399 if ( mDataProvider )
400 {
401 QDomElement provider = doc.createElement( QStringLiteral( "provider" ) );
402 const QDomText providerText = doc.createTextNode( providerType() );
403 provider.appendChild( providerText );
404 mapLayerNode.appendChild( provider );
405 }
406
407 writeStyleManager( layerNode, doc );
408
409 QString errorMsg;
410 return writeSymbology( layerNode, doc, errorMsg, context );
411}
412
413bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
414{
416
417 const QDomElement elem = node.toElement();
418
419 readCommonStyle( elem, context, categories );
420
421 const QDomElement elemRenderer = elem.firstChildElement( QStringLiteral( "renderer" ) );
422 if ( elemRenderer.isNull() )
423 {
424 errorMessage = tr( "Missing <renderer> tag" );
425 return false;
426 }
427 const QString rendererType = elemRenderer.attribute( QStringLiteral( "type" ) );
428
429 if ( categories.testFlag( Symbology ) )
430 {
431 QgsVectorTileRenderer *r = nullptr;
432 if ( rendererType == QLatin1String( "basic" ) )
434 else
435 {
436 errorMessage = tr( "Unknown renderer type: " ) + rendererType;
437 return false;
438 }
439
440 r->readXml( elemRenderer, context );
441 setRenderer( r );
442 }
443
444 if ( categories.testFlag( Labeling ) )
445 {
446 setLabeling( nullptr );
447 const QDomElement elemLabeling = elem.firstChildElement( QStringLiteral( "labeling" ) );
448 if ( !elemLabeling.isNull() )
449 {
450 const QString labelingType = elemLabeling.attribute( QStringLiteral( "type" ) );
452 if ( labelingType == QLatin1String( "basic" ) )
454 else
455 {
456 errorMessage = tr( "Unknown labeling type: " ) + rendererType;
457 }
458
459 if ( labeling )
460 {
461 labeling->readXml( elemLabeling, context );
463 }
464 }
465 }
466
467 if ( categories.testFlag( Symbology ) )
468 {
469 // get and set the blend mode if it exists
470 const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
471 if ( !blendModeNode.isNull() )
472 {
473 const QDomElement e = blendModeNode.toElement();
474 setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
475 }
476 }
477
478 // get and set the layer transparency
479 if ( categories.testFlag( Rendering ) )
480 {
481 const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
482 if ( !layerOpacityNode.isNull() )
483 {
484 const QDomElement e = layerOpacityNode.toElement();
485 setOpacity( e.text().toDouble() );
486 }
487 }
488
489 return true;
490}
491
492bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
493{
495
496 Q_UNUSED( errorMessage )
497 QDomElement elem = node.toElement();
498
499 writeCommonStyle( elem, doc, context, categories );
500
501 if ( mRenderer )
502 {
503 QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
504 elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
505 if ( categories.testFlag( Symbology ) )
506 {
507 mRenderer->writeXml( elemRenderer, context );
508 }
509 elem.appendChild( elemRenderer );
510 }
511
512 if ( mLabeling && categories.testFlag( Labeling ) )
513 {
514 QDomElement elemLabeling = doc.createElement( QStringLiteral( "labeling" ) );
515 elemLabeling.setAttribute( QStringLiteral( "type" ), mLabeling->type() );
516 mLabeling->writeXml( elemLabeling, context );
517 elem.appendChild( elemLabeling );
518 }
519
520 if ( categories.testFlag( Symbology ) )
521 {
522 // add the blend mode field
523 QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
524 const QDomText blendModeText = doc.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
525 blendModeElem.appendChild( blendModeText );
526 node.appendChild( blendModeElem );
527 }
528
529 // add the layer opacity
530 if ( categories.testFlag( Rendering ) )
531 {
532 QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
533 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
534 layerOpacityElem.appendChild( layerOpacityText );
535 node.appendChild( layerOpacityElem );
536 }
537
538 return true;
539}
540
542{
544
545 if ( mDataProvider )
546 mDataProvider->setTransformContext( transformContext );
547
548 mTransformContext = transformContext;
550}
551
552QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
553{
555
556 QString error;
557 QStringList warnings;
558 resultFlag = loadDefaultStyle( error, warnings );
559 return error;
560}
561
562Qgis::MapLayerProperties QgsVectorTileLayer::properties() const
563{
565
566 Qgis::MapLayerProperties res;
567 if ( mSourceType == QLatin1String( "xyz" ) )
568 {
569 // always consider xyz vector tiles as basemap layers
571 }
572 else
573 {
574 // TODO when should we consider mbtiles layers as basemap layers? potentially if their extent is "large"?
575 }
576
577 return res;
578}
579
580bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
581{
583
584 return loadDefaultStyleAndSubLayersPrivate( error, warnings, nullptr );
585}
586
587bool QgsVectorTileLayer::loadDefaultStyleAndSubLayers( QString &error, QStringList &warnings, QList<QgsMapLayer *> &subLayers )
588{
590
591 return loadDefaultStyleAndSubLayersPrivate( error, warnings, &subLayers );
592}
593
594bool QgsVectorTileLayer::loadDefaultStyleAndSubLayersPrivate( QString &error, QStringList &warnings, QList<QgsMapLayer *> *subLayers )
595{
597
598 QgsDataSourceUri dsUri;
599 dsUri.setEncodedUri( mDataSource );
600
601 QVariantMap styleDefinition;
603 QString styleUrl;
604 if ( !dsUri.param( QStringLiteral( "styleUrl" ) ).isEmpty() )
605 {
606 styleUrl = dsUri.param( QStringLiteral( "styleUrl" ) );
607 }
608 else if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
609 {
610 // for ArcMap VectorTileServices we default to the defaultStyles URL from the layer configuration
611 styleUrl = mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString()
612 + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "defaultStyles" ) ).toString();
613 }
614
615 if ( mSourceType == QLatin1String( "vtpk" ) )
616 {
617 QgsVtpkTiles reader( mSourcePath );
618 if ( !reader.open() )
619 {
620 QgsDebugMsg( QStringLiteral( "failed to open VTPK file: " ) + mSourcePath );
621 return false;
622 }
623
624 styleDefinition = reader.styleDefinition();
625
626 const QVariantMap spriteDefinition = reader.spriteDefinition();
627 if ( !spriteDefinition.isEmpty() )
628 {
629 const QImage spriteImage = reader.spriteImage();
630 context.setSprites( spriteImage, spriteDefinition );
631 }
632 }
633 else if ( !mArcgisStyleConfiguration.isEmpty() || !styleUrl.isEmpty() )
634 {
635 if ( !mArcgisStyleConfiguration.isEmpty() )
636 {
637 styleDefinition = mArcgisStyleConfiguration;
638 }
639 else
640 {
641 QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
642
643 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );
644
645 QgsBlockingNetworkRequest networkRequest;
646 switch ( networkRequest.get( request ) )
647 {
649 break;
650
654 error = QObject::tr( "Error retrieving default style" );
655 return false;
656 }
657
658 const QgsNetworkReplyContent content = networkRequest.reply();
659 styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
660 }
661
662 if ( styleDefinition.contains( QStringLiteral( "sprite" ) ) )
663 {
664 // retrieve sprite definition
665 QString spriteUriBase;
666 if ( styleDefinition.value( QStringLiteral( "sprite" ) ).toString().startsWith( QLatin1String( "http" ) ) )
667 {
668 spriteUriBase = styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
669 }
670 else
671 {
672 spriteUriBase = styleUrl + '/' + styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
673 }
674
675 for ( int resolution = 2; resolution > 0; resolution-- )
676 {
677 QUrl spriteUrl = QUrl( spriteUriBase );
678 spriteUrl.setPath( spriteUrl.path() + QStringLiteral( "%1.json" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) );
679 QNetworkRequest request = QNetworkRequest( spriteUrl );
680 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
681 QgsBlockingNetworkRequest networkRequest;
682 switch ( networkRequest.get( request ) )
683 {
685 {
686 const QgsNetworkReplyContent content = networkRequest.reply();
687 const QVariantMap spriteDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
688
689 // retrieve sprite images
690 QUrl spriteUrl = QUrl( spriteUriBase );
691 spriteUrl.setPath( spriteUrl.path() + QStringLiteral( "%1.png" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) );
692 QNetworkRequest request = QNetworkRequest( spriteUrl );
693 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
694 QgsBlockingNetworkRequest networkRequest;
695 switch ( networkRequest.get( request ) )
696 {
698 {
699 const QgsNetworkReplyContent imageContent = networkRequest.reply();
700 const QImage spriteImage( QImage::fromData( imageContent.content() ) );
701 context.setSprites( spriteImage, spriteDefinition );
702 break;
703 }
704
708 break;
709 }
710
711 break;
712 }
713
717 break;
718 }
719
720 if ( !context.spriteDefinitions().isEmpty() )
721 break;
722 }
723 }
724 }
725
726 if ( !styleDefinition.isEmpty() )
727 {
728 // convert automatically from pixel sizes to millimeters, because pixel sizes
729 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
731 //assume source uses 96 dpi
732 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
733
735 if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
736 {
737 warnings = converter.warnings();
738 error = converter.errorMessage();
739 return false;
740 }
741
742 setRenderer( converter.renderer() );
743 setLabeling( converter.labeling() );
744 warnings = converter.warnings();
745
746 if ( subLayers )
747 {
748 *subLayers = converter.createSubLayers();
749 }
750
751 return true;
752 }
753 else
754 {
755 bool resultFlag = false;
756 error = QgsMapLayer::loadDefaultStyle( resultFlag );
757 return resultFlag;
758 }
759}
760
762{
764
765 QgsDataSourceUri dsUri;
766 dsUri.setEncodedUri( mDataSource );
767 if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
768 {
769 // populate default metadata
771 metadata.setIdentifier( mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString() );
772 const QString parentIdentifier = mArcgisLayerConfiguration.value( QStringLiteral( "serviceItemId" ) ).toString();
773 if ( !parentIdentifier.isEmpty() )
774 {
775 metadata.setParentIdentifier( parentIdentifier );
776 }
777 metadata.setType( QStringLiteral( "dataset" ) );
778 metadata.setTitle( mArcgisLayerConfiguration.value( QStringLiteral( "name" ) ).toString() );
779 const QString copyright = mArcgisLayerConfiguration.value( QStringLiteral( "copyrightText" ) ).toString();
780 if ( !copyright.isEmpty() )
781 metadata.setRights( QStringList() << copyright );
782 metadata.addLink( QgsAbstractMetadataBase::Link( tr( "Source" ), QStringLiteral( "WWW:LINK" ), mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString() ) );
783
785
786 resultFlag = true;
787 return QString();
788 }
789 else if ( mSourceType == QLatin1String( "vtpk" ) )
790 {
791 QgsVtpkTiles reader( mSourcePath );
792 if ( !reader.open() )
793 {
794 QgsDebugMsg( QStringLiteral( "failed to open VTPK file: " ) + mSourcePath );
795 resultFlag = false;
796 }
797 else
798 {
799 setMetadata( reader.layerMetadata() );
800 resultFlag = true;
801 }
802 return QString();
803 }
804 else
805 {
807 resultFlag = true;
808 return QString();
809 }
810}
811
812QString QgsVectorTileLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
813{
815
816 QgsDataSourceUri dsUri;
817 dsUri.setEncodedUri( source );
818
819 const QString sourceType = dsUri.param( QStringLiteral( "type" ) );
820 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
821 if ( sourceType == QLatin1String( "xyz" ) )
822 {
823 const QUrl sourceUrl( sourcePath );
824 if ( sourceUrl.isLocalFile() )
825 {
826 // relative path will become "file:./x.txt"
827 const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
828 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
829 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
830 return dsUri.encodedUri();
831 }
832 }
833 else if ( sourceType == QLatin1String( "mbtiles" ) )
834 {
836 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
837 dsUri.setParam( QStringLiteral( "url" ), sourcePath );
838 return dsUri.encodedUri();
839 }
840
841 return source;
842}
843
844QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
845{
847
848 Q_UNUSED( provider )
849
850 QgsDataSourceUri dsUri;
851 dsUri.setEncodedUri( source );
852
853 const QString sourceType = dsUri.param( QStringLiteral( "type" ) );
854 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
855 if ( sourceType == QLatin1String( "xyz" ) )
856 {
857 const QUrl sourceUrl( sourcePath );
858 if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
859 {
860 const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
861 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
862 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
863 return dsUri.encodedUri();
864 }
865 }
866 else if ( sourceType == QLatin1String( "mbtiles" ) )
867 {
869 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
870 dsUri.setParam( QStringLiteral( "url" ), sourcePath );
871 return dsUri.encodedUri();
872 }
873
874 return source;
875}
876
878{
880
881 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
882
883 QString info = QStringLiteral( "<html><head></head>\n<body>\n" );
884
885 info += generalHtmlMetadata();
886
887 info += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
888 QStringLiteral( "<table class=\"list-view\">\n" );
889
890 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source type" ) % QStringLiteral( "</td><td>" ) % sourceType() % QStringLiteral( "</td></tr>\n" );
891
892 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Zoom levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % QStringLiteral( "</td></tr>\n" );
893
894 info += QLatin1String( "</table>\n<br>" );
895
896 // CRS
897 info += crsHtmlMetadata();
898
899 // Identification section
900 info += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
901 htmlFormatter.identificationSectionHtml() %
902 QStringLiteral( "<br>\n" ) %
903
904 // extent section
905 QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
906 htmlFormatter.extentSectionHtml( ) %
907 QStringLiteral( "<br>\n" ) %
908
909 // Start the Access section
910 QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
911 htmlFormatter.accessSectionHtml( ) %
912 QStringLiteral( "<br>\n" ) %
913
914
915 // Start the contacts section
916 QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
917 htmlFormatter.contactsSectionHtml( ) %
918 QStringLiteral( "<br><br>\n" ) %
919
920 // Start the links section
921 QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
922 htmlFormatter.linksSectionHtml( ) %
923 QStringLiteral( "<br>\n" ) %
924
925 // Start the history section
926 QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
927 htmlFormatter.historySectionHtml( ) %
928 QStringLiteral( "<br>\n" ) %
929
930 QStringLiteral( "\n</body>\n</html>\n" );
931
932 return info;
933}
934
936{
938
939 const QgsTileMatrix tileMatrix = mMatrixSet.tileMatrix( tileID.zoomLevel() );
940 const QgsTileRange tileRange( tileID.column(), tileID.column(), tileID.row(), tileID.row() );
941
942 QgsDataSourceUri dsUri;
943 dsUri.setEncodedUri( mDataSource );
944 const QString authcfg = dsUri.authConfigId();
945
946 QList<QgsVectorTileRawData> rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, tileMatrix, QPointF(), tileRange, authcfg, dsUri.httpHeaders() );
947 if ( rawTiles.isEmpty() )
948 return QByteArray();
949 return rawTiles.first().data;
950}
951
953{
955
956 mRenderer.reset( r );
958}
959
961{
963
964 return mRenderer.get();
965}
966
968{
970
971 mLabeling.reset( labeling );
973}
974
976{
978
979 return mLabeling.get();
980}
981
983{
985
986 QList< QgsFeature > res;
987 res.reserve( mSelectedFeatures.size() );
988 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
989 res.append( it.value() );
990
991 return res;
992}
993
995{
997
998 return mSelectedFeatures.size();
999}
1000
1001void QgsVectorTileLayer::selectByGeometry( const QgsGeometry &geometry, const QgsSelectionContext &context, Qgis::SelectBehavior behavior, Qgis::SelectGeometryRelationship relationship, Qgis::SelectionFlags flags, QgsRenderContext *renderContext )
1002{
1004
1005 if ( !isInScaleRange( context.scale() ) )
1006 {
1007 QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
1008 return;
1009 }
1010
1011 QSet< QgsFeatureId > prevSelection;
1012 prevSelection.reserve( mSelectedFeatures.size() );
1013 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
1014 prevSelection.insert( it.key() );
1015
1017 {
1018 switch ( behavior )
1019 {
1021 mSelectedFeatures.clear();
1022 break;
1023
1027 break;
1028 }
1029 }
1030
1031 QgsGeometry selectionGeom = geometry;
1032 bool isPointOrRectangle;
1033 QgsPointXY point;
1034 bool isSinglePoint = selectionGeom.type() == QgsWkbTypes::PointGeometry;
1035 if ( isSinglePoint )
1036 {
1037 isPointOrRectangle = true;
1038 point = selectionGeom.asPoint();
1040 }
1041 else
1042 {
1043 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly instersection tests later
1044 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
1045 }
1046
1047 auto addDerivedFields = []( QgsFeature & feature, const int tileZoom, const QString & layer )
1048 {
1049 QgsFields fields = feature.fields();
1050 fields.append( QgsField( QStringLiteral( "tile_zoom" ), QVariant::Int ) );
1051 fields.append( QgsField( QStringLiteral( "tile_layer" ), QVariant::String ) );
1052 QgsAttributes attributes = feature.attributes();
1053 attributes << tileZoom << layer;
1054 feature.setFields( fields );
1055 feature.setAttributes( attributes );
1056 };
1057
1058 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
1059 QList< QgsFeature > singleSelectCandidates;
1060
1061 QgsRectangle r;
1062 if ( isSinglePoint )
1063 {
1064 r = QgsRectangle( point.x(), point.y(), point.x(), point.y() );
1065 }
1066 else
1067 {
1068 r = selectionGeom.boundingBox();
1069
1070 if ( !isPointOrRectangle || relationship == Qgis::SelectGeometryRelationship::Within )
1071 {
1072 // use prepared geometry for faster intersection test
1073 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
1074 }
1075 }
1076
1077 switch ( behavior )
1078 {
1081 {
1082 // when adding to or setting a selection, we retrieve the tile data for the current scale
1083 const int tileZoom = tileMatrixSet().scaleToZoomLevel( context.scale() );
1084 const QgsTileMatrix tileMatrix = tileMatrixSet().tileMatrix( tileZoom );
1085 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
1086
1087 for ( int row = tileRange.startRow(); row <= tileRange.endRow(); ++row )
1088 {
1089 for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); ++col )
1090 {
1091 QgsTileXYZ tileID( col, row, tileZoom );
1092 QByteArray data = getRawTile( tileID );
1093 if ( data.isEmpty() )
1094 continue; // failed to get data
1095
1097 if ( !decoder.decode( tileID, data ) )
1098 continue; // failed to decode
1099
1100 QMap<QString, QgsFields> perLayerFields;
1101 const QStringList layerNames = decoder.layers();
1102 for ( const QString &layerName : layerNames )
1103 {
1104 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
1105 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
1106 }
1107
1108 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
1109 const QStringList featuresLayerNames = features.keys();
1110 for ( const QString &layerName : featuresLayerNames )
1111 {
1112 const QgsFields fFields = perLayerFields[layerName];
1113 const QVector<QgsFeature> &layerFeatures = features[layerName];
1114 for ( const QgsFeature &f : layerFeatures )
1115 {
1116 if ( renderContext && mRenderer && !mRenderer->willRenderFeature( f, tileZoom, layerName, *renderContext ) )
1117 continue;
1118
1119 if ( f.geometry().intersects( r ) )
1120 {
1121 bool selectFeature = true;
1122 if ( selectionGeomPrepared )
1123 {
1124 switch ( relationship )
1125 {
1127 selectFeature = selectionGeomPrepared->intersects( f.geometry().constGet() );
1128 break;
1130 selectFeature = selectionGeomPrepared->contains( f.geometry().constGet() );
1131 break;
1132 }
1133 }
1134
1135 if ( selectFeature )
1136 {
1137 QgsFeature derivedFeature = f;
1138 addDerivedFields( derivedFeature, tileZoom, layerName );
1140 singleSelectCandidates << derivedFeature;
1141 else
1142 mSelectedFeatures.insert( derivedFeature.id(), derivedFeature );
1143 }
1144 }
1145 }
1146 }
1147 }
1148 }
1149 break;
1150 }
1151
1154 {
1155 // when removing from the selection, we instead just iterate over the current selection and test against the geometry
1156 // we do this as we don't want the selection removal operation to depend at all on the tile zoom
1157 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); )
1158 {
1159 bool matchesGeometry = false;
1160 if ( selectionGeomPrepared )
1161 {
1162 switch ( relationship )
1163 {
1165 matchesGeometry = selectionGeomPrepared->intersects( it->geometry().constGet() );
1166 break;
1168 matchesGeometry = selectionGeomPrepared->contains( it->geometry().constGet() );
1169 break;
1170 }
1171 }
1172 else
1173 {
1174 switch ( relationship )
1175 {
1177 matchesGeometry = it->geometry().intersects( r );
1178 break;
1180 matchesGeometry = r.contains( it->geometry().boundingBox() );
1181 break;
1182 }
1183 }
1184
1186 {
1187 singleSelectCandidates << it.value();
1188 it++;
1189 }
1190 else if ( ( matchesGeometry && behavior == Qgis::SelectBehavior::IntersectSelection )
1191 || ( !matchesGeometry && behavior == Qgis::SelectBehavior::RemoveFromSelection ) )
1192 {
1193 it++;
1194 }
1195 else
1196 {
1197 it = mSelectedFeatures.erase( it );
1198 }
1199 }
1200 break;
1201 }
1202 }
1203
1204 if ( ( flags & Qgis::SelectionFlag::SingleFeatureSelection ) && !singleSelectCandidates.empty() )
1205 {
1206 QgsFeature bestCandidate;
1207
1209 {
1210 // 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
1211
1212 // find smallest feature in the current selection
1213 double smallestArea = std::numeric_limits< double >::max();
1214 double smallestLength = std::numeric_limits< double >::max();
1215 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
1216 {
1217 if ( !mSelectedFeatures.contains( candidate.id() ) )
1218 continue;
1219
1220 switch ( candidate.geometry().type() )
1221 {
1223 bestCandidate = candidate;
1224 break;
1226 {
1227 const double length = candidate.geometry().length();
1228 if ( length < smallestLength && bestCandidate.geometry().type() != QgsWkbTypes::PointGeometry )
1229 {
1230 bestCandidate = candidate;
1231 smallestLength = length;
1232 }
1233 break;
1234 }
1236 {
1237 const double area = candidate.geometry().area();
1238 if ( area < smallestArea && bestCandidate.geometry().type() != QgsWkbTypes::PointGeometry && bestCandidate.geometry().type() != QgsWkbTypes::LineGeometry )
1239 {
1240 bestCandidate = candidate;
1241 smallestArea = area;
1242 }
1243 break;
1244 }
1247 break;
1248 }
1249 }
1250 }
1251
1252 if ( !bestCandidate.isValid() )
1253 {
1254 // find smallest feature (ie. pick the "hardest" one to click on)
1255 double smallestArea = std::numeric_limits< double >::max();
1256 double smallestLength = std::numeric_limits< double >::max();
1257 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
1258 {
1259 switch ( candidate.geometry().type() )
1260 {
1262 bestCandidate = candidate;
1263 break;
1265 {
1266 const double length = candidate.geometry().length();
1267 if ( length < smallestLength && bestCandidate.geometry().type() != QgsWkbTypes::PointGeometry )
1268 {
1269 bestCandidate = candidate;
1270 smallestLength = length;
1271 }
1272 break;
1273 }
1275 {
1276 const double area = candidate.geometry().area();
1277 if ( area < smallestArea && bestCandidate.geometry().type() != QgsWkbTypes::PointGeometry && bestCandidate.geometry().type() != QgsWkbTypes::LineGeometry )
1278 {
1279 bestCandidate = candidate;
1280 smallestArea = area;
1281 }
1282 break;
1283 }
1286 break;
1287 }
1288 }
1289 }
1290
1292 {
1293 if ( prevSelection.contains( bestCandidate.id() ) )
1294 mSelectedFeatures.remove( bestCandidate.id() );
1295 else
1296 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
1297 }
1298 else
1299 {
1300 switch ( behavior )
1301 {
1304 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
1305 break;
1306
1308 {
1309 if ( mSelectedFeatures.contains( bestCandidate.id() ) )
1310 {
1311 mSelectedFeatures.clear();
1312 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
1313 }
1314 else
1315 {
1316 mSelectedFeatures.clear();
1317 }
1318 break;
1319 }
1320
1322 {
1323 mSelectedFeatures.remove( bestCandidate.id() );
1324 break;
1325 }
1326 }
1327 }
1328 }
1329
1330 QSet< QgsFeatureId > newSelection;
1331 newSelection.reserve( mSelectedFeatures.size() );
1332 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
1333 newSelection.insert( it.key() );
1334
1335 // signal
1336 if ( prevSelection != newSelection )
1337 emit selectionChanged();
1338}
1339
1341{
1343
1344 if ( mSelectedFeatures.empty() )
1345 return;
1346
1347 mSelectedFeatures.clear();
1348 emit selectionChanged();
1349}
1350
1351
1352//
1353// QgsVectorTileDataProvider
1354//
1356QgsVectorTileDataProvider::QgsVectorTileDataProvider(
1357 const ProviderOptions &options,
1358 QgsDataProvider::ReadFlags flags )
1359 : QgsDataProvider( QString(), options, flags )
1360{}
1361
1363{
1365
1366 return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
1367}
1368
1369QString QgsVectorTileDataProvider::name() const
1370{
1372
1373 return QStringLiteral( "vectortile" );
1374}
1375
1376QString QgsVectorTileDataProvider::description() const
1377{
1379
1380 return QString();
1381}
1382
1383QgsRectangle QgsVectorTileDataProvider::extent() const
1384{
1386
1387 return QgsRectangle();
1388}
1389
1390bool QgsVectorTileDataProvider::isValid() const
1391{
1393
1394 return true;
1395}
1396
1397bool QgsVectorTileDataProvider::renderInPreview( const PreviewContext &context )
1398{
1400
1401 // Vector tiles by design are very CPU light to render, so we are much more permissive here compared
1402 // with other layer types. (Generally if a vector tile layer has taken more than a few milliseconds to render it's
1403 // a result of network requests, and the tile manager class handles these gracefully for us)
1404 return context.lastRenderingTimeMs <= 1000;
1405}
1406
@ 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:885
@ 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:871
@ 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.
bool hasParam(const QString &key) const
Returns true if a parameter with the specified key exists.
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.
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.
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:1994
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:2032
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:1991
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:162
@ Rendering
Rendering: scale visibility, simplify method, opacity.
Definition: qgsmaplayer.h:171
@ Labeling
Labeling.
Definition: qgsmaplayer.h:164
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)
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
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.