QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
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
138
140{
142
143 return mDataProvider.get();
144}
145
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 ( elemLabeling.hasAttribute( QStringLiteral( "labelsEnabled" ) ) )
251 mLabelsEnabled = elemLabeling.attribute( QStringLiteral( "labelsEnabled" ) ).toInt();
252 else
253 mLabelsEnabled = true;
254
255 if ( labeling )
256 {
257 labeling->readXml( elemLabeling, context );
259 }
260 }
261 }
262
263 if ( categories.testFlag( Symbology ) )
264 {
265 // get and set the blend mode if it exists
266 const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
267 if ( !blendModeNode.isNull() )
268 {
269 const QDomElement e = blendModeNode.toElement();
270 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
271 }
272 }
273
274 // get and set the layer transparency
275 if ( categories.testFlag( Rendering ) )
276 {
277 const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
278 if ( !layerOpacityNode.isNull() )
279 {
280 const QDomElement e = layerOpacityNode.toElement();
281 setOpacity( e.text().toDouble() );
282 }
283 }
284
285 return true;
286}
287
288bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
289{
291
292 Q_UNUSED( errorMessage )
293 QDomElement elem = node.toElement();
294
295 writeCommonStyle( elem, doc, context, categories );
296
297 if ( mRenderer )
298 {
299 QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
300 elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
301 if ( categories.testFlag( Symbology ) )
302 {
303 mRenderer->writeXml( elemRenderer, context );
304 }
305 elem.appendChild( elemRenderer );
306 }
307
308 if ( mLabeling && categories.testFlag( Labeling ) )
309 {
310 QDomElement elemLabeling = doc.createElement( QStringLiteral( "labeling" ) );
311 elemLabeling.setAttribute( QStringLiteral( "type" ), mLabeling->type() );
312 elemLabeling.setAttribute( QStringLiteral( "labelsEnabled" ), mLabelsEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
313 mLabeling->writeXml( elemLabeling, context );
314 elem.appendChild( elemLabeling );
315 }
316
317 if ( categories.testFlag( Symbology ) )
318 {
319 // add the blend mode field
320 QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
321 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
322 blendModeElem.appendChild( blendModeText );
323 node.appendChild( blendModeElem );
324 }
325
326 // add the layer opacity
327 if ( categories.testFlag( Rendering ) )
328 {
329 QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
330 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
331 layerOpacityElem.appendChild( layerOpacityText );
332 node.appendChild( layerOpacityElem );
333 }
334
335 return true;
336}
337
339{
341
342 if ( mDataProvider )
343 mDataProvider->setTransformContext( transformContext );
344
345 mTransformContext = transformContext;
347}
348
349QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
350{
352
353 QString error;
354 QStringList warnings;
355 resultFlag = loadDefaultStyle( error, warnings );
356 return error;
357}
358
359Qgis::MapLayerProperties QgsVectorTileLayer::properties() const
360{
362
363 Qgis::MapLayerProperties res;
364 if ( mSourceType == QLatin1String( "xyz" ) )
365 {
366 // always consider xyz vector tiles as basemap layers
368 }
369 else
370 {
371 // TODO when should we consider mbtiles layers as basemap layers? potentially if their extent is "large"?
372 }
373
374 return res;
375}
376
377bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
378{
380
381 return loadDefaultStyleAndSubLayersPrivate( error, warnings, nullptr );
382}
383
384bool QgsVectorTileLayer::loadDefaultStyleAndSubLayers( QString &error, QStringList &warnings, QList<QgsMapLayer *> &subLayers )
385{
387
388 return loadDefaultStyleAndSubLayersPrivate( error, warnings, &subLayers );
389}
390
391bool QgsVectorTileLayer::loadDefaultStyleAndSubLayersPrivate( QString &error, QStringList &warnings, QList<QgsMapLayer *> *subLayers )
392{
394 QgsVectorTileDataProvider *vtProvider = qgis::down_cast< QgsVectorTileDataProvider *> ( mDataProvider.get() );
395 if ( !vtProvider )
396 return false;
397
398 QgsDataSourceUri dsUri;
399 dsUri.setEncodedUri( mDataSource );
400
401 QVariantMap styleDefinition;
403 QString styleUrl;
404 if ( !dsUri.param( QStringLiteral( "styleUrl" ) ).isEmpty() )
405 {
406 styleUrl = dsUri.param( QStringLiteral( "styleUrl" ) );
407 }
408 else
409 {
410 styleUrl = vtProvider->styleUrl();
411 }
412
413 styleDefinition = vtProvider->styleDefinition();
414 const QVariantMap spriteDefinition = vtProvider->spriteDefinition();
415 if ( !spriteDefinition.isEmpty() )
416 {
417 const QImage spriteImage = vtProvider->spriteImage();
418 context.setSprites( spriteImage, spriteDefinition );
419 }
420
421 if ( !styleDefinition.isEmpty() || !styleUrl.isEmpty() )
422 {
423 if ( styleDefinition.isEmpty() )
424 {
425 QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
426
427 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );
428
429 QgsBlockingNetworkRequest networkRequest;
430 switch ( networkRequest.get( request ) )
431 {
433 break;
434
438 error = QObject::tr( "Error retrieving default style" );
439 return false;
440 }
441
442 const QgsNetworkReplyContent content = networkRequest.reply();
443 styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
444 }
445
446 QgsVectorTileUtils::loadSprites( styleDefinition, context, styleUrl );
447 }
448
449 if ( !styleDefinition.isEmpty() )
450 {
451 // convert automatically from pixel sizes to millimeters, because pixel sizes
452 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
454 //assume source uses 96 dpi
455 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
456
458 if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
459 {
460 warnings = converter.warnings();
461 error = converter.errorMessage();
462 return false;
463 }
464
465 setRenderer( converter.renderer() );
466 setLabeling( converter.labeling() );
467 warnings = converter.warnings();
468
469 if ( subLayers )
470 {
471 *subLayers = converter.createSubLayers();
472 }
473
474 return true;
475 }
476 else
477 {
478 bool resultFlag = false;
479 error = QgsMapLayer::loadDefaultStyle( resultFlag );
480 return resultFlag;
481 }
482}
483
485{
487
488 resultFlag = false;
489 if ( !mDataProvider || !mDataProvider->isValid() )
490 return QString();
491
492 if ( qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerCapabilities() & Qgis::VectorTileProviderCapability::ReadLayerMetadata )
493 {
494 setMetadata( mDataProvider->layerMetadata() );
495 }
496 else
497 {
499 }
500 resultFlag = true;
501 return QString();
502}
503
504QString QgsVectorTileLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
505{
507
509}
510
511QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
512{
514
515 return QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, source, context );
516}
517
519{
521
522 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
523
524 QString info = QStringLiteral( "<html><head></head>\n<body>\n" );
525
526 info += generalHtmlMetadata();
527
528 info += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
529 QStringLiteral( "<table class=\"list-view\">\n" );
530
531 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source type" ) % QStringLiteral( "</td><td>" ) % sourceType() % QStringLiteral( "</td></tr>\n" );
532
533 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Zoom levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % QStringLiteral( "</td></tr>\n" );
534
535 if ( mDataProvider )
536 info += qobject_cast< const QgsVectorTileDataProvider * >( mDataProvider.get() )->htmlMetadata();
537
538 info += QLatin1String( "</table>\n<br>" );
539
540 // CRS
541 info += crsHtmlMetadata();
542
543 // Identification section
544 info += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
545 htmlFormatter.identificationSectionHtml() %
546 QStringLiteral( "<br>\n" ) %
547
548 // extent section
549 QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
550 htmlFormatter.extentSectionHtml( ) %
551 QStringLiteral( "<br>\n" ) %
552
553 // Start the Access section
554 QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
555 htmlFormatter.accessSectionHtml( ) %
556 QStringLiteral( "<br>\n" ) %
557
558
559 // Start the contacts section
560 QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
561 htmlFormatter.contactsSectionHtml( ) %
562 QStringLiteral( "<br><br>\n" ) %
563
564 // Start the links section
565 QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
566 htmlFormatter.linksSectionHtml( ) %
567 QStringLiteral( "<br>\n" ) %
568
569 // Start the history section
570 QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
571 htmlFormatter.historySectionHtml( ) %
572 QStringLiteral( "<br>\n" ) %
573
574 QStringLiteral( "\n</body>\n</html>\n" );
575
576 return info;
577}
578
580{
581 if ( QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() ) )
582 return vtProvider->sourcePath();
583
584 return QString();
585}
586
588{
590
591 QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() );
592 if ( !vtProvider )
593 return QgsVectorTileRawData();
594
595 return vtProvider->readTile( mMatrixSet, tileID );
596}
597
605
607{
609
610 return mRenderer.get();
611}
612
620
622{
624
625 return mLabeling.get();
626}
627
629{
630 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
632
633 return mLabelsEnabled && static_cast< bool >( mLabeling );
634}
635
637{
639
640 mLabelsEnabled = enabled;
641}
642
644{
646
647 QList< QgsFeature > res;
648 res.reserve( mSelectedFeatures.size() );
649 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
650 res.append( it.value() );
651
652 return res;
653}
654
656{
658
659 return mSelectedFeatures.size();
660}
661
662void QgsVectorTileLayer::selectByGeometry( const QgsGeometry &geometry, const QgsSelectionContext &context, Qgis::SelectBehavior behavior, Qgis::SelectGeometryRelationship relationship, Qgis::SelectionFlags flags, QgsRenderContext *renderContext )
663{
665
666 if ( !isInScaleRange( context.scale() ) )
667 {
668 QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
669 return;
670 }
671
672 QSet< QgsFeatureId > prevSelection;
673 prevSelection.reserve( mSelectedFeatures.size() );
674 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
675 prevSelection.insert( it.key() );
676
678 {
679 switch ( behavior )
680 {
682 mSelectedFeatures.clear();
683 break;
684
688 break;
689 }
690 }
691
692 QgsGeometry selectionGeom = geometry;
693 bool isPointOrRectangle;
694 QgsPointXY point;
695 bool isSinglePoint = selectionGeom.type() == Qgis::GeometryType::Point;
696 if ( isSinglePoint )
697 {
698 isPointOrRectangle = true;
699 point = selectionGeom.asPoint();
701 }
702 else
703 {
704 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly instersection tests later
705 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
706 }
707
708 auto addDerivedFields = []( QgsFeature & feature, const int tileZoom, const QString & layer )
709 {
710 QgsFields fields = feature.fields();
711 fields.append( QgsField( QStringLiteral( "tile_zoom" ), QVariant::Int ) );
712 fields.append( QgsField( QStringLiteral( "tile_layer" ), QVariant::String ) );
713 QgsAttributes attributes = feature.attributes();
714 attributes << tileZoom << layer;
715 feature.setFields( fields );
716 feature.setAttributes( attributes );
717 };
718
719 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
720 QList< QgsFeature > singleSelectCandidates;
721
722 QgsRectangle r;
723 if ( isSinglePoint )
724 {
725 r = QgsRectangle( point.x(), point.y(), point.x(), point.y() );
726 }
727 else
728 {
729 r = selectionGeom.boundingBox();
730
731 if ( !isPointOrRectangle || relationship == Qgis::SelectGeometryRelationship::Within )
732 {
733 // use prepared geometry for faster intersection test
734 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
735 }
736 }
737
738 switch ( behavior )
739 {
742 {
743 // when adding to or setting a selection, we retrieve the tile data for the current scale
744 const int tileZoom = tileMatrixSet().scaleToZoomLevel( context.scale() );
745 const QgsTileMatrix tileMatrix = tileMatrixSet().tileMatrix( tileZoom );
746 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
747 const QVector< QgsTileXYZ> tiles = tileMatrixSet().tilesInRange( tileRange, tileZoom );
748
749 for ( const QgsTileXYZ &tileID : tiles )
750 {
751 const QgsVectorTileRawData data = getRawTile( tileID );
752 if ( data.data.isEmpty() )
753 continue; // failed to get data
754
756 if ( !decoder.decode( data ) )
757 continue; // failed to decode
758
759 QMap<QString, QgsFields> perLayerFields;
760 const QStringList layerNames = decoder.layers();
761 for ( const QString &layerName : layerNames )
762 {
763 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
764 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
765 }
766
767 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
768 const QStringList featuresLayerNames = features.keys();
769 for ( const QString &layerName : featuresLayerNames )
770 {
771 const QgsFields fFields = perLayerFields[layerName];
772 const QVector<QgsFeature> &layerFeatures = features[layerName];
773 for ( const QgsFeature &f : layerFeatures )
774 {
775 if ( renderContext && mRenderer && !mRenderer->willRenderFeature( f, tileID.zoomLevel(), layerName, *renderContext ) )
776 continue;
777
778 if ( f.geometry().intersects( r ) )
779 {
780 bool selectFeature = true;
781 if ( selectionGeomPrepared )
782 {
783 switch ( relationship )
784 {
786 selectFeature = selectionGeomPrepared->intersects( f.geometry().constGet() );
787 break;
789 selectFeature = selectionGeomPrepared->contains( f.geometry().constGet() );
790 break;
791 }
792 }
793
794 if ( selectFeature )
795 {
796 QgsFeature derivedFeature = f;
797 addDerivedFields( derivedFeature, tileID.zoomLevel(), layerName );
799 singleSelectCandidates << derivedFeature;
800 else
801 mSelectedFeatures.insert( derivedFeature.id(), derivedFeature );
802 }
803 }
804 }
805 }
806 }
807 break;
808 }
809
812 {
813 // when removing from the selection, we instead just iterate over the current selection and test against the geometry
814 // we do this as we don't want the selection removal operation to depend at all on the tile zoom
815 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); )
816 {
817 bool matchesGeometry = false;
818 if ( selectionGeomPrepared )
819 {
820 switch ( relationship )
821 {
823 matchesGeometry = selectionGeomPrepared->intersects( it->geometry().constGet() );
824 break;
826 matchesGeometry = selectionGeomPrepared->contains( it->geometry().constGet() );
827 break;
828 }
829 }
830 else
831 {
832 switch ( relationship )
833 {
835 matchesGeometry = it->geometry().intersects( r );
836 break;
838 matchesGeometry = r.contains( it->geometry().boundingBox() );
839 break;
840 }
841 }
842
844 {
845 singleSelectCandidates << it.value();
846 it++;
847 }
848 else if ( ( matchesGeometry && behavior == Qgis::SelectBehavior::IntersectSelection )
849 || ( !matchesGeometry && behavior == Qgis::SelectBehavior::RemoveFromSelection ) )
850 {
851 it++;
852 }
853 else
854 {
855 it = mSelectedFeatures.erase( it );
856 }
857 }
858 break;
859 }
860 }
861
862 if ( ( flags & Qgis::SelectionFlag::SingleFeatureSelection ) && !singleSelectCandidates.empty() )
863 {
864 QgsFeature bestCandidate;
865
867 {
868 // 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
869
870 // find smallest feature in the current selection
871 double smallestArea = std::numeric_limits< double >::max();
872 double smallestLength = std::numeric_limits< double >::max();
873 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
874 {
875 if ( !mSelectedFeatures.contains( candidate.id() ) )
876 continue;
877
878 switch ( candidate.geometry().type() )
879 {
881 bestCandidate = candidate;
882 break;
884 {
885 const double length = candidate.geometry().length();
886 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
887 {
888 bestCandidate = candidate;
889 smallestLength = length;
890 }
891 break;
892 }
894 {
895 const double area = candidate.geometry().area();
896 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
897 {
898 bestCandidate = candidate;
899 smallestArea = area;
900 }
901 break;
902 }
905 break;
906 }
907 }
908 }
909
910 if ( !bestCandidate.isValid() )
911 {
912 // find smallest feature (ie. pick the "hardest" one to click on)
913 double smallestArea = std::numeric_limits< double >::max();
914 double smallestLength = std::numeric_limits< double >::max();
915 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
916 {
917 switch ( candidate.geometry().type() )
918 {
920 bestCandidate = candidate;
921 break;
923 {
924 const double length = candidate.geometry().length();
925 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
926 {
927 bestCandidate = candidate;
928 smallestLength = length;
929 }
930 break;
931 }
933 {
934 const double area = candidate.geometry().area();
935 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
936 {
937 bestCandidate = candidate;
938 smallestArea = area;
939 }
940 break;
941 }
944 break;
945 }
946 }
947 }
948
950 {
951 if ( prevSelection.contains( bestCandidate.id() ) )
952 mSelectedFeatures.remove( bestCandidate.id() );
953 else
954 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
955 }
956 else
957 {
958 switch ( behavior )
959 {
962 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
963 break;
964
966 {
967 if ( mSelectedFeatures.contains( bestCandidate.id() ) )
968 {
969 mSelectedFeatures.clear();
970 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
971 }
972 else
973 {
974 mSelectedFeatures.clear();
975 }
976 break;
977 }
978
980 {
981 mSelectedFeatures.remove( bestCandidate.id() );
982 break;
983 }
984 }
985 }
986 }
987
988 QSet< QgsFeatureId > newSelection;
989 newSelection.reserve( mSelectedFeatures.size() );
990 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
991 newSelection.insert( it.key() );
992
993 // signal
994 if ( prevSelection != newSelection )
995 emit selectionChanged();
996}
997
999{
1001
1002 if ( mSelectedFeatures.empty() )
1003 return;
1004
1005 mSelectedFeatures.clear();
1006 emit selectionChanged();
1007}
1008
1009
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
@ 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:3413
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Millimeters
Millimeters.
@ 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:1277
@ 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:1263
@ 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.
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
QgsFeatureId id
Definition qgsfeature.h:64
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
QgsGeometry geometry
Definition qgsfeature.h:67
bool isValid() const
Returns the validity of this feature.
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.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double length() const
Returns the planar, 2-dimensional length of geometry.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
Qgis::GeometryType type
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:74
QString name
Definition qgsmaplayer.h:77
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.
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:79
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)
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.
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:83
@ Symbology
Symbology.
@ Rendering
Rendering: scale visibility, simplify method, opacity.
@ Labeling
Labeling.
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 Qgis::BlendMode corresponding to a QPainter::CompositionMode.
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a Qgis::BlendMode.
A class to represent a 2D point.
Definition qgspointxy.h:59
double y
Definition qgspointxy.h:63
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.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
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:391
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system associated with the tiles.
Definition qgstiles.cpp:218
QVector< QgsTileXYZ > tilesInRange(QgsTileRange range, int zoomLevel) const
Returns a list of tiles in the given tile range.
Definition qgstiles.cpp:428
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:342
int scaleToZoomLevel(double scale, bool clamp=true) const
Finds the best fitting (integer) zoom level given a map scale denominator.
Definition qgstiles.cpp:283
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.
void setLabelsEnabled(bool enabled)
Sets whether labels should be enabled for the layer.
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).
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
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_NON_FATAL
#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.