QGIS API Documentation 3.31.0-Master (9f23a2c1dc)
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"
23#include "qgsvectortileloader.h"
24#include "qgsvectortileutils.h"
26#include "qgsdatasourceuri.h"
30#include "qgsjsonutils.h"
31#include "qgspainting.h"
32#include "qgsmaplayerfactory.h"
33#include "qgsselectioncontext.h"
34#include "qgsgeometryengine.h"
36#include "qgsthreadingutils.h"
37#include "qgsproviderregistry.h"
39
40#include <QUrl>
41#include <QUrlQuery>
42
43QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName, const LayerOptions &options )
44 : QgsMapLayer( Qgis::LayerType::VectorTile, baseName )
45 , mTransformContext( options.transformContext )
46{
47 mDataSource = uri;
48
49 if ( !uri.isEmpty() )
50 setValid( loadDataSource() );
51
52 // set a default renderer
56
57 connect( this, &QgsVectorTileLayer::selectionChanged, this, [ = ] { triggerRepaint(); } );
58}
59
60void QgsVectorTileLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &, const QgsDataProvider::ProviderOptions &, QgsDataProvider::ReadFlags )
61{
63
64 mDataSource = dataSource;
65 mLayerName = baseName;
66 mDataProvider.reset();
67
68 setValid( loadDataSource() );
69}
70
71bool QgsVectorTileLayer::loadDataSource()
72{
74
75 QgsDataSourceUri dsUri;
77
78 setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
79
80 const QgsDataProvider::ProviderOptions providerOptions { mTransformContext };
81 const QgsDataProvider::ReadFlags flags;
82
83 mSourceType = dsUri.param( QStringLiteral( "type" ) );
84 QString providerKey;
85 if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
86 {
87 providerKey = QStringLiteral( "arcgisvectortileservice" );
88 }
89 else if ( mSourceType == QLatin1String( "xyz" ) )
90 {
91 providerKey = QStringLiteral( "xyzvectortiles" );
92 }
93 else if ( mSourceType == QLatin1String( "mbtiles" ) )
94 {
95 providerKey = QStringLiteral( "mbtilesvectortiles" );
96 }
97 else if ( mSourceType == QLatin1String( "vtpk" ) )
98 {
99 providerKey = QStringLiteral( "vtpkvectortiles" );
100 }
101 else
102 {
103 QgsDebugError( QStringLiteral( "Unknown source type: " ) + mSourceType );
104 return false;
105 }
106
107 mDataProvider.reset( qobject_cast<QgsVectorTileDataProvider *>( QgsProviderRegistry::instance()->createProvider( providerKey, mDataSource, providerOptions, flags ) ) );
108 mProviderKey = mDataProvider->name();
109
110 if ( mDataProvider )
111 {
112 mMatrixSet = qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->tileMatrixSet();
113 setCrs( mDataProvider->crs() );
114 setExtent( mDataProvider->extent() );
115 }
116
117 return mDataProvider && mDataProvider->isValid();
118}
119
121
123{
125
126 const QgsVectorTileLayer::LayerOptions options( mTransformContext );
127 QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name(), options );
128 layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
129 return layer;
130}
131
133{
135
136 return mDataProvider.get();
137}
138
140{
142
143 return mDataProvider.get();
144}
145
147{
149
150 return new QgsVectorTileLayerRenderer( this, rendererContext );
151}
152
153bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
154{
156
157 setValid( loadDataSource() );
158
159 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
160 {
161 const QDomElement matrixSetElement = layerNode.firstChildElement( QStringLiteral( "matrixSet" ) );
162 if ( !matrixSetElement.isNull() )
163 {
164 mMatrixSet.readXml( matrixSetElement, context );
165 }
166 }
167 setCrs( mMatrixSet.crs() );
168
169 QString errorMsg;
170 if ( !readSymbology( layerNode, errorMsg, context ) )
171 return false;
172
173 readStyleManager( layerNode );
174 return true;
175}
176
177bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
178{
180
181 QDomElement mapLayerNode = layerNode.toElement();
182 mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( Qgis::LayerType::VectorTile ) );
183
184 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
185 {
186 mapLayerNode.appendChild( mMatrixSet.writeXml( doc, context ) );
187 }
188
189 // add provider node
190 if ( mDataProvider )
191 {
192 QDomElement provider = doc.createElement( QStringLiteral( "provider" ) );
193 const QDomText providerText = doc.createTextNode( providerType() );
194 provider.appendChild( providerText );
195 mapLayerNode.appendChild( provider );
196 }
197
198 writeStyleManager( layerNode, doc );
199
200 QString errorMsg;
201 return writeSymbology( layerNode, doc, errorMsg, context );
202}
203
204bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
205{
207
208 const QDomElement elem = node.toElement();
209
210 readCommonStyle( elem, context, categories );
211
212 const QDomElement elemRenderer = elem.firstChildElement( QStringLiteral( "renderer" ) );
213 if ( elemRenderer.isNull() )
214 {
215 errorMessage = tr( "Missing <renderer> tag" );
216 return false;
217 }
218 const QString rendererType = elemRenderer.attribute( QStringLiteral( "type" ) );
219
220 if ( categories.testFlag( Symbology ) )
221 {
222 QgsVectorTileRenderer *r = nullptr;
223 if ( rendererType == QLatin1String( "basic" ) )
225 else
226 {
227 errorMessage = tr( "Unknown renderer type: " ) + rendererType;
228 return false;
229 }
230
231 r->readXml( elemRenderer, context );
232 setRenderer( r );
233 }
234
235 if ( categories.testFlag( Labeling ) )
236 {
237 setLabeling( nullptr );
238 const QDomElement elemLabeling = elem.firstChildElement( QStringLiteral( "labeling" ) );
239 if ( !elemLabeling.isNull() )
240 {
241 const QString labelingType = elemLabeling.attribute( QStringLiteral( "type" ) );
243 if ( labelingType == QLatin1String( "basic" ) )
245 else
246 {
247 errorMessage = tr( "Unknown labeling type: " ) + rendererType;
248 }
249
250 if ( labeling )
251 {
252 labeling->readXml( elemLabeling, context );
254 }
255 }
256 }
257
258 if ( categories.testFlag( Symbology ) )
259 {
260 // get and set the blend mode if it exists
261 const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
262 if ( !blendModeNode.isNull() )
263 {
264 const QDomElement e = blendModeNode.toElement();
265 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
266 }
267 }
268
269 // get and set the layer transparency
270 if ( categories.testFlag( Rendering ) )
271 {
272 const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
273 if ( !layerOpacityNode.isNull() )
274 {
275 const QDomElement e = layerOpacityNode.toElement();
276 setOpacity( e.text().toDouble() );
277 }
278 }
279
280 return true;
281}
282
283bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
284{
286
287 Q_UNUSED( errorMessage )
288 QDomElement elem = node.toElement();
289
290 writeCommonStyle( elem, doc, context, categories );
291
292 if ( mRenderer )
293 {
294 QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
295 elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
296 if ( categories.testFlag( Symbology ) )
297 {
298 mRenderer->writeXml( elemRenderer, context );
299 }
300 elem.appendChild( elemRenderer );
301 }
302
303 if ( mLabeling && categories.testFlag( Labeling ) )
304 {
305 QDomElement elemLabeling = doc.createElement( QStringLiteral( "labeling" ) );
306 elemLabeling.setAttribute( QStringLiteral( "type" ), mLabeling->type() );
307 mLabeling->writeXml( elemLabeling, context );
308 elem.appendChild( elemLabeling );
309 }
310
311 if ( categories.testFlag( Symbology ) )
312 {
313 // add the blend mode field
314 QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
315 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
316 blendModeElem.appendChild( blendModeText );
317 node.appendChild( blendModeElem );
318 }
319
320 // add the layer opacity
321 if ( categories.testFlag( Rendering ) )
322 {
323 QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
324 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
325 layerOpacityElem.appendChild( layerOpacityText );
326 node.appendChild( layerOpacityElem );
327 }
328
329 return true;
330}
331
333{
335
336 if ( mDataProvider )
337 mDataProvider->setTransformContext( transformContext );
338
339 mTransformContext = transformContext;
341}
342
343QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
344{
346
347 QString error;
348 QStringList warnings;
349 resultFlag = loadDefaultStyle( error, warnings );
350 return error;
351}
352
353Qgis::MapLayerProperties QgsVectorTileLayer::properties() const
354{
356
357 Qgis::MapLayerProperties res;
358 if ( mSourceType == QLatin1String( "xyz" ) )
359 {
360 // always consider xyz vector tiles as basemap layers
362 }
363 else
364 {
365 // TODO when should we consider mbtiles layers as basemap layers? potentially if their extent is "large"?
366 }
367
368 return res;
369}
370
371bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
372{
374
375 return loadDefaultStyleAndSubLayersPrivate( error, warnings, nullptr );
376}
377
378bool QgsVectorTileLayer::loadDefaultStyleAndSubLayers( QString &error, QStringList &warnings, QList<QgsMapLayer *> &subLayers )
379{
381
382 return loadDefaultStyleAndSubLayersPrivate( error, warnings, &subLayers );
383}
384
385bool QgsVectorTileLayer::loadDefaultStyleAndSubLayersPrivate( QString &error, QStringList &warnings, QList<QgsMapLayer *> *subLayers )
386{
388 QgsVectorTileDataProvider *vtProvider = qgis::down_cast< QgsVectorTileDataProvider *> ( mDataProvider.get() );
389 if ( !vtProvider )
390 return false;
391
392 QgsDataSourceUri dsUri;
393 dsUri.setEncodedUri( mDataSource );
394
395 QVariantMap styleDefinition;
397 QString styleUrl;
398 if ( !dsUri.param( QStringLiteral( "styleUrl" ) ).isEmpty() )
399 {
400 styleUrl = dsUri.param( QStringLiteral( "styleUrl" ) );
401 }
402 else
403 {
404 styleUrl = vtProvider->styleUrl();
405 }
406
407 styleDefinition = vtProvider->styleDefinition();
408 const QVariantMap spriteDefinition = vtProvider->spriteDefinition();
409 if ( !spriteDefinition.isEmpty() )
410 {
411 const QImage spriteImage = vtProvider->spriteImage();
412 context.setSprites( spriteImage, spriteDefinition );
413 }
414
415 if ( !styleDefinition.isEmpty() || !styleUrl.isEmpty() )
416 {
417 if ( styleDefinition.isEmpty() )
418 {
419 QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
420
421 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );
422
423 QgsBlockingNetworkRequest networkRequest;
424 switch ( networkRequest.get( request ) )
425 {
427 break;
428
432 error = QObject::tr( "Error retrieving default style" );
433 return false;
434 }
435
436 const QgsNetworkReplyContent content = networkRequest.reply();
437 styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
438 }
439
440 QgsVectorTileUtils::loadSprites( styleDefinition, context, styleUrl );
441 }
442
443 if ( !styleDefinition.isEmpty() )
444 {
445 // convert automatically from pixel sizes to millimeters, because pixel sizes
446 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
447 context.setTargetUnit( Qgis::RenderUnit::Millimeters );
448 //assume source uses 96 dpi
449 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
450
452 if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
453 {
454 warnings = converter.warnings();
455 error = converter.errorMessage();
456 return false;
457 }
458
459 setRenderer( converter.renderer() );
460 setLabeling( converter.labeling() );
461 warnings = converter.warnings();
462
463 if ( subLayers )
464 {
465 *subLayers = converter.createSubLayers();
466 }
467
468 return true;
469 }
470 else
471 {
472 bool resultFlag = false;
473 error = QgsMapLayer::loadDefaultStyle( resultFlag );
474 return resultFlag;
475 }
476}
477
479{
481
482 resultFlag = false;
483 if ( !mDataProvider || !mDataProvider->isValid() )
484 return QString();
485
486 if ( qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerCapabilities() & Qgis::VectorTileProviderCapability::ReadLayerMetadata )
487 {
488 setMetadata( mDataProvider->layerMetadata() );
489 }
490 else
491 {
493 }
494 resultFlag = true;
495 return QString();
496}
497
498QString QgsVectorTileLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
499{
501
503}
504
505QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
506{
508
509 return QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, source, context );
510}
511
513{
515
516 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
517
518 QString info = QStringLiteral( "<html><head></head>\n<body>\n" );
519
520 info += generalHtmlMetadata();
521
522 info += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
523 QStringLiteral( "<table class=\"list-view\">\n" );
524
525 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source type" ) % QStringLiteral( "</td><td>" ) % sourceType() % QStringLiteral( "</td></tr>\n" );
526
527 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Zoom levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % QStringLiteral( "</td></tr>\n" );
528
529 if ( mDataProvider )
530 info += qobject_cast< const QgsVectorTileDataProvider * >( mDataProvider.get() )->htmlMetadata();
531
532 info += QLatin1String( "</table>\n<br>" );
533
534 // CRS
535 info += crsHtmlMetadata();
536
537 // Identification section
538 info += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
539 htmlFormatter.identificationSectionHtml() %
540 QStringLiteral( "<br>\n" ) %
541
542 // extent section
543 QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
544 htmlFormatter.extentSectionHtml( ) %
545 QStringLiteral( "<br>\n" ) %
546
547 // Start the Access section
548 QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
549 htmlFormatter.accessSectionHtml( ) %
550 QStringLiteral( "<br>\n" ) %
551
552
553 // Start the contacts section
554 QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
555 htmlFormatter.contactsSectionHtml( ) %
556 QStringLiteral( "<br><br>\n" ) %
557
558 // Start the links section
559 QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
560 htmlFormatter.linksSectionHtml( ) %
561 QStringLiteral( "<br>\n" ) %
562
563 // Start the history section
564 QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
565 htmlFormatter.historySectionHtml( ) %
566 QStringLiteral( "<br>\n" ) %
567
568 QStringLiteral( "\n</body>\n</html>\n" );
569
570 return info;
571}
572
574{
575 if ( QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() ) )
576 return vtProvider->sourcePath();
577
578 return QString();
579}
580
582{
584
585 QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() );
586 if ( !vtProvider )
587 return QgsVectorTileRawData();
588
589 return vtProvider->readTile( mMatrixSet, tileID );
590}
591
593{
595
596 mRenderer.reset( r );
598}
599
601{
603
604 return mRenderer.get();
605}
606
608{
610
611 mLabeling.reset( labeling );
613}
614
616{
618
619 return mLabeling.get();
620}
621
623{
625
626 QList< QgsFeature > res;
627 res.reserve( mSelectedFeatures.size() );
628 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
629 res.append( it.value() );
630
631 return res;
632}
633
635{
637
638 return mSelectedFeatures.size();
639}
640
641void QgsVectorTileLayer::selectByGeometry( const QgsGeometry &geometry, const QgsSelectionContext &context, Qgis::SelectBehavior behavior, Qgis::SelectGeometryRelationship relationship, Qgis::SelectionFlags flags, QgsRenderContext *renderContext )
642{
644
645 if ( !isInScaleRange( context.scale() ) )
646 {
647 QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
648 return;
649 }
650
651 QSet< QgsFeatureId > prevSelection;
652 prevSelection.reserve( mSelectedFeatures.size() );
653 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
654 prevSelection.insert( it.key() );
655
657 {
658 switch ( behavior )
659 {
661 mSelectedFeatures.clear();
662 break;
663
667 break;
668 }
669 }
670
671 QgsGeometry selectionGeom = geometry;
672 bool isPointOrRectangle;
673 QgsPointXY point;
674 bool isSinglePoint = selectionGeom.type() == Qgis::GeometryType::Point;
675 if ( isSinglePoint )
676 {
677 isPointOrRectangle = true;
678 point = selectionGeom.asPoint();
680 }
681 else
682 {
683 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly instersection tests later
684 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
685 }
686
687 auto addDerivedFields = []( QgsFeature & feature, const int tileZoom, const QString & layer )
688 {
689 QgsFields fields = feature.fields();
690 fields.append( QgsField( QStringLiteral( "tile_zoom" ), QVariant::Int ) );
691 fields.append( QgsField( QStringLiteral( "tile_layer" ), QVariant::String ) );
692 QgsAttributes attributes = feature.attributes();
693 attributes << tileZoom << layer;
694 feature.setFields( fields );
695 feature.setAttributes( attributes );
696 };
697
698 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
699 QList< QgsFeature > singleSelectCandidates;
700
701 QgsRectangle r;
702 if ( isSinglePoint )
703 {
704 r = QgsRectangle( point.x(), point.y(), point.x(), point.y() );
705 }
706 else
707 {
708 r = selectionGeom.boundingBox();
709
710 if ( !isPointOrRectangle || relationship == Qgis::SelectGeometryRelationship::Within )
711 {
712 // use prepared geometry for faster intersection test
713 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
714 }
715 }
716
717 switch ( behavior )
718 {
721 {
722 // when adding to or setting a selection, we retrieve the tile data for the current scale
723 const int tileZoom = tileMatrixSet().scaleToZoomLevel( context.scale() );
724 const QgsTileMatrix tileMatrix = tileMatrixSet().tileMatrix( tileZoom );
725 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
726 const QVector< QgsTileXYZ> tiles = tileMatrixSet().tilesInRange( tileRange, tileZoom );
727
728 for ( const QgsTileXYZ &tileID : tiles )
729 {
730 const QgsVectorTileRawData data = getRawTile( tileID );
731 if ( data.data.isEmpty() )
732 continue; // failed to get data
733
735 if ( !decoder.decode( data ) )
736 continue; // failed to decode
737
738 QMap<QString, QgsFields> perLayerFields;
739 const QStringList layerNames = decoder.layers();
740 for ( const QString &layerName : layerNames )
741 {
742 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
743 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
744 }
745
746 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
747 const QStringList featuresLayerNames = features.keys();
748 for ( const QString &layerName : featuresLayerNames )
749 {
750 const QgsFields fFields = perLayerFields[layerName];
751 const QVector<QgsFeature> &layerFeatures = features[layerName];
752 for ( const QgsFeature &f : layerFeatures )
753 {
754 if ( renderContext && mRenderer && !mRenderer->willRenderFeature( f, tileID.zoomLevel(), layerName, *renderContext ) )
755 continue;
756
757 if ( f.geometry().intersects( r ) )
758 {
759 bool selectFeature = true;
760 if ( selectionGeomPrepared )
761 {
762 switch ( relationship )
763 {
765 selectFeature = selectionGeomPrepared->intersects( f.geometry().constGet() );
766 break;
768 selectFeature = selectionGeomPrepared->contains( f.geometry().constGet() );
769 break;
770 }
771 }
772
773 if ( selectFeature )
774 {
775 QgsFeature derivedFeature = f;
776 addDerivedFields( derivedFeature, tileID.zoomLevel(), layerName );
778 singleSelectCandidates << derivedFeature;
779 else
780 mSelectedFeatures.insert( derivedFeature.id(), derivedFeature );
781 }
782 }
783 }
784 }
785 }
786 break;
787 }
788
791 {
792 // when removing from the selection, we instead just iterate over the current selection and test against the geometry
793 // we do this as we don't want the selection removal operation to depend at all on the tile zoom
794 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); )
795 {
796 bool matchesGeometry = false;
797 if ( selectionGeomPrepared )
798 {
799 switch ( relationship )
800 {
802 matchesGeometry = selectionGeomPrepared->intersects( it->geometry().constGet() );
803 break;
805 matchesGeometry = selectionGeomPrepared->contains( it->geometry().constGet() );
806 break;
807 }
808 }
809 else
810 {
811 switch ( relationship )
812 {
814 matchesGeometry = it->geometry().intersects( r );
815 break;
817 matchesGeometry = r.contains( it->geometry().boundingBox() );
818 break;
819 }
820 }
821
823 {
824 singleSelectCandidates << it.value();
825 it++;
826 }
827 else if ( ( matchesGeometry && behavior == Qgis::SelectBehavior::IntersectSelection )
828 || ( !matchesGeometry && behavior == Qgis::SelectBehavior::RemoveFromSelection ) )
829 {
830 it++;
831 }
832 else
833 {
834 it = mSelectedFeatures.erase( it );
835 }
836 }
837 break;
838 }
839 }
840
841 if ( ( flags & Qgis::SelectionFlag::SingleFeatureSelection ) && !singleSelectCandidates.empty() )
842 {
843 QgsFeature bestCandidate;
844
846 {
847 // 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
848
849 // find smallest feature in the current selection
850 double smallestArea = std::numeric_limits< double >::max();
851 double smallestLength = std::numeric_limits< double >::max();
852 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
853 {
854 if ( !mSelectedFeatures.contains( candidate.id() ) )
855 continue;
856
857 switch ( candidate.geometry().type() )
858 {
859 case Qgis::GeometryType::Point:
860 bestCandidate = candidate;
861 break;
862 case Qgis::GeometryType::Line:
863 {
864 const double length = candidate.geometry().length();
865 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
866 {
867 bestCandidate = candidate;
868 smallestLength = length;
869 }
870 break;
871 }
872 case Qgis::GeometryType::Polygon:
873 {
874 const double area = candidate.geometry().area();
875 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
876 {
877 bestCandidate = candidate;
878 smallestArea = area;
879 }
880 break;
881 }
882 case Qgis::GeometryType::Unknown:
883 case Qgis::GeometryType::Null:
884 break;
885 }
886 }
887 }
888
889 if ( !bestCandidate.isValid() )
890 {
891 // find smallest feature (ie. pick the "hardest" one to click on)
892 double smallestArea = std::numeric_limits< double >::max();
893 double smallestLength = std::numeric_limits< double >::max();
894 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
895 {
896 switch ( candidate.geometry().type() )
897 {
898 case Qgis::GeometryType::Point:
899 bestCandidate = candidate;
900 break;
901 case Qgis::GeometryType::Line:
902 {
903 const double length = candidate.geometry().length();
904 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
905 {
906 bestCandidate = candidate;
907 smallestLength = length;
908 }
909 break;
910 }
911 case Qgis::GeometryType::Polygon:
912 {
913 const double area = candidate.geometry().area();
914 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
915 {
916 bestCandidate = candidate;
917 smallestArea = area;
918 }
919 break;
920 }
921 case Qgis::GeometryType::Unknown:
922 case Qgis::GeometryType::Null:
923 break;
924 }
925 }
926 }
927
929 {
930 if ( prevSelection.contains( bestCandidate.id() ) )
931 mSelectedFeatures.remove( bestCandidate.id() );
932 else
933 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
934 }
935 else
936 {
937 switch ( behavior )
938 {
941 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
942 break;
943
945 {
946 if ( mSelectedFeatures.contains( bestCandidate.id() ) )
947 {
948 mSelectedFeatures.clear();
949 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
950 }
951 else
952 {
953 mSelectedFeatures.clear();
954 }
955 break;
956 }
957
959 {
960 mSelectedFeatures.remove( bestCandidate.id() );
961 break;
962 }
963 }
964 }
965 }
966
967 QSet< QgsFeatureId > newSelection;
968 newSelection.reserve( mSelectedFeatures.size() );
969 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
970 newSelection.insert( it.key() );
971
972 // signal
973 if ( prevSelection != newSelection )
974 emit selectionChanged();
975}
976
978{
980
981 if ( mSelectedFeatures.empty() )
982 return;
983
984 mSelectedFeatures.clear();
985 emit selectionChanged();
986}
987
988
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:55
@ IsBasemapLayer
Layer is considered a 'basemap' layer, and certain properties of the layer should be ignored when cal...
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata()
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition: qgis.h:3156
@ 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:1138
@ 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:1124
@ 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.
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.
Abstract base class for spatial data provider implementations.
Class for storing the component parts of a RDBMS data source URI (e.g.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
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:195
QgsGeometry geometry
Definition: qgsfeature.h:67
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:216
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
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.
double length() const
Returns the planar, 2-dimensional length of geometry.
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.
Qgis::GeometryType type
Definition: qgsgeometry.h:167
double area() const
Returns the planar, 2-dimensional area of the geometry.
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.
Context for a MapBox GL style conversion operation.
void setTargetUnit(Qgis::RenderUnit targetUnit)
Sets the target unit type.
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.
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(Qgis::LayerType 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.
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:2035
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:2073
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.
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:2032
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:163
@ Rendering
Rendering: scale visibility, simplify method, opacity.
Definition: qgsmaplayer.h:172
@ Labeling
Labeling.
Definition: qgsmaplayer.h:165
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.
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 Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
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
QString absoluteToRelativeUri(const QString &providerKey, const QString &uri, const QgsReadWriteContext &context) const
Converts absolute path(s) to relative path(s) in the given provider-specific URI.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QString relativeToAbsoluteUri(const QString &providerKey, const QString &uri, const QgsReadWriteContext &context) const
Converts relative path(s) to absolute path(s) in the given provider-specific URI.
The class is used as a container of context for various read/write operations on other objects.
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:376
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system associated with the tiles.
Definition: qgstiles.cpp:218
int scaleToZoomLevel(double scale) const
Finds the best fitting (integer) zoom level given a map scale denominator.
Definition: qgstiles.cpp:268
QVector< QgsTileXYZ > tilesInRange(QgsTileRange range, int zoomLevel) const
Returns a list of tiles in the given tile range.
Definition: qgstiles.cpp:413
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition: qgstiles.cpp:156
virtual bool readXml(const QDomElement &element, QgsReadWriteContext &context)
Reads the set from an XML element.
Definition: qgstiles.cpp:327
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition: qgstiles.h:134
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition: qgstiles.cpp:97
Range of tiles in a tile matrix to be rendered.
Definition: qgstiles.h:97
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
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 vector tile layer data providers.
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.
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)
QgsVectorTileRawData getRawTile(QgsTileXYZ tileID)
Fetches raw tile data for the give tile coordinates.
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.
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()
bool decode(const QgsVectorTileRawData &rawTileData)
Tries to decode raw tile data, returns true on success.
QgsVectorTileFeatures layerFeatures(const QMap< QString, QgsFields > &perLayerFields, const QgsCoordinateTransform &ct, const QSet< QString > *layerSubset=nullptr) const
Returns decoded features grouped by sub-layers.
Keeps track of raw tile data that need to be decoded.
QByteArray data
Raw tile data.
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 QgsFields makeQgisFields(const QSet< QString > &flds)
Returns QgsFields instance based on the set of field names.
static void loadSprites(const QVariantMap &styleDefinition, QgsMapBoxGlStyleConversionContext &context, const QString &styleUrl=QString())
Downloads the sprite image and sets it to the conversion context.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(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)
Setting options for creating vector data providers.
Setting options for loading vector tile layers.