QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
19#include "qgsdatasourceuri.h"
20#include "qgsgeometryengine.h"
21#include "qgsjsonutils.h"
23#include "qgslogger.h"
25#include "qgsmaplayerfactory.h"
26#include "qgspainting.h"
27#include "qgsproviderregistry.h"
28#include "qgsselectioncontext.h"
30#include "qgsthreadingutils.h"
36#include "qgsvectortileloader.h"
38#include "qgsvectortileutils.h"
39
40#include <QUrl>
41#include <QUrlQuery>
42
43#include "moc_qgsvectortilelayer.cpp"
44
45QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName, const LayerOptions &options )
46 : QgsMapLayer( Qgis::LayerType::VectorTile, baseName )
47 , mTransformContext( options.transformContext )
48{
49 mDataSource = uri;
50
51 if ( !uri.isEmpty() )
52 setValid( loadDataSource() );
53
54 // set a default renderer
58
59 connect( this, &QgsVectorTileLayer::selectionChanged, this, [this] { triggerRepaint(); } );
60}
61
62void QgsVectorTileLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &, const QgsDataProvider::ProviderOptions &, Qgis::DataProviderReadFlags )
63{
65
66 mDataSource = dataSource;
67 mLayerName = baseName;
68 mDataProvider.reset();
69
70 setValid( loadDataSource() );
71}
72
73bool QgsVectorTileLayer::loadDataSource()
74{
76
77 QgsDataSourceUri dsUri;
78 dsUri.setEncodedUri( mDataSource );
79
80 setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
81
82 const QgsDataProvider::ProviderOptions providerOptions { mTransformContext };
84
85 mSourceType = dsUri.param( QStringLiteral( "type" ) );
86 QString providerKey;
87 if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
88 {
89 providerKey = QStringLiteral( "arcgisvectortileservice" );
90 }
91 else if ( mSourceType == QLatin1String( "xyz" ) )
92 {
93 providerKey = QStringLiteral( "xyzvectortiles" );
94 }
95 else if ( mSourceType == QLatin1String( "mbtiles" ) )
96 {
97 providerKey = QStringLiteral( "mbtilesvectortiles" );
98 }
99 else if ( mSourceType == QLatin1String( "vtpk" ) )
100 {
101 providerKey = QStringLiteral( "vtpkvectortiles" );
102 }
103 else
104 {
105 QgsDebugError( QStringLiteral( "Unknown source type: " ) + mSourceType );
106 return false;
107 }
108
109 mDataProvider.reset( qobject_cast<QgsVectorTileDataProvider *>( QgsProviderRegistry::instance()->createProvider( providerKey, mDataSource, providerOptions, flags ) ) );
110 mProviderKey = mDataProvider ? mDataProvider->name() : providerKey;
111
112 if ( mDataProvider )
113 {
114 mMatrixSet = qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->tileMatrixSet();
115 setCrs( mDataProvider->crs() );
116 setExtent( mDataProvider->extent() );
117 }
118
119 return mDataProvider && mDataProvider->isValid();
120}
121
123
125{
127
128 const QgsVectorTileLayer::LayerOptions options( mTransformContext );
129 QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name(), options );
130 layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
131 layer->setLabeling( labeling() ? labeling()->clone() : nullptr );
132 return layer;
133}
134
141
143{
145
146 return mDataProvider.get();
147}
148
155
156bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
157{
159
160 setValid( loadDataSource() );
161
162 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
163 {
164 const QDomElement matrixSetElement = layerNode.firstChildElement( QStringLiteral( "matrixSet" ) );
165 if ( !matrixSetElement.isNull() )
166 {
167 mMatrixSet.readXml( matrixSetElement, context );
168 }
169 }
170 setCrs( mMatrixSet.crs() );
171
172 QString errorMsg;
173 if ( !readSymbology( layerNode, errorMsg, context ) )
174 return false;
175
176 readStyleManager( layerNode );
177 return true;
178}
179
180bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
181{
183
184 QDomElement mapLayerNode = layerNode.toElement();
185 mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( Qgis::LayerType::VectorTile ) );
186
187 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
188 {
189 mapLayerNode.appendChild( mMatrixSet.writeXml( doc, context ) );
190 }
191
192 // add provider node
193 if ( mDataProvider )
194 {
195 QDomElement provider = doc.createElement( QStringLiteral( "provider" ) );
196 const QDomText providerText = doc.createTextNode( providerType() );
197 provider.appendChild( providerText );
198 mapLayerNode.appendChild( provider );
199 }
200
201 writeStyleManager( layerNode, doc );
202
203 QString errorMsg;
204 return writeSymbology( layerNode, doc, errorMsg, context );
205}
206
207bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
208{
210
211 const QDomElement elem = node.toElement();
212
213 readCommonStyle( elem, context, categories );
214
215 const QDomElement elemRenderer = elem.firstChildElement( QStringLiteral( "renderer" ) );
216 if ( elemRenderer.isNull() )
217 {
218 errorMessage = tr( "Missing <renderer> tag" );
219 return false;
220 }
221 const QString rendererType = elemRenderer.attribute( QStringLiteral( "type" ) );
222
223 if ( categories.testFlag( Symbology ) )
224 {
225 QgsVectorTileRenderer *r = nullptr;
226 if ( rendererType == QLatin1String( "basic" ) )
228 else
229 {
230 errorMessage = tr( "Unknown renderer type: " ) + rendererType;
231 return false;
232 }
233
234 r->readXml( elemRenderer, context );
235 setRenderer( r );
236 }
237
238 if ( categories.testFlag( Labeling ) )
239 {
240 setLabeling( nullptr );
241 const QDomElement elemLabeling = elem.firstChildElement( QStringLiteral( "labeling" ) );
242 if ( !elemLabeling.isNull() )
243 {
244 const QString labelingType = elemLabeling.attribute( QStringLiteral( "type" ) );
246 if ( labelingType == QLatin1String( "basic" ) )
248 else
249 {
250 errorMessage = tr( "Unknown labeling type: " ) + rendererType;
251 }
252
253 if ( elemLabeling.hasAttribute( QStringLiteral( "labelsEnabled" ) ) )
254 mLabelsEnabled = elemLabeling.attribute( QStringLiteral( "labelsEnabled" ) ).toInt();
255 else
256 mLabelsEnabled = true;
257
258 if ( labeling )
259 {
260 labeling->readXml( elemLabeling, context );
262 }
263 }
264 }
265
266 if ( categories.testFlag( Symbology ) )
267 {
268 // get and set the blend mode if it exists
269 const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
270 if ( !blendModeNode.isNull() )
271 {
272 const QDomElement e = blendModeNode.toElement();
273 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
274 }
275 }
276
277 // get and set the layer transparency
278 if ( categories.testFlag( Rendering ) )
279 {
280 const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
281 if ( !layerOpacityNode.isNull() )
282 {
283 const QDomElement e = layerOpacityNode.toElement();
284 setOpacity( e.text().toDouble() );
285 }
286 }
287
288 return true;
289}
290
291bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
292{
294
295 Q_UNUSED( errorMessage )
296 QDomElement elem = node.toElement();
297
298 writeCommonStyle( elem, doc, context, categories );
299
300 if ( mRenderer )
301 {
302 QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
303 elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
304 if ( categories.testFlag( Symbology ) )
305 {
306 mRenderer->writeXml( elemRenderer, context );
307 }
308 elem.appendChild( elemRenderer );
309 }
310
311 if ( mLabeling && categories.testFlag( Labeling ) )
312 {
313 QDomElement elemLabeling = doc.createElement( QStringLiteral( "labeling" ) );
314 elemLabeling.setAttribute( QStringLiteral( "type" ), mLabeling->type() );
315 elemLabeling.setAttribute( QStringLiteral( "labelsEnabled" ), mLabelsEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
316 mLabeling->writeXml( elemLabeling, context );
317 elem.appendChild( elemLabeling );
318 }
319
320 if ( categories.testFlag( Symbology ) )
321 {
322 // add the blend mode field
323 QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
324 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
325 blendModeElem.appendChild( blendModeText );
326 node.appendChild( blendModeElem );
327 }
328
329 // add the layer opacity
330 if ( categories.testFlag( Rendering ) )
331 {
332 QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
333 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
334 layerOpacityElem.appendChild( layerOpacityText );
335 node.appendChild( layerOpacityElem );
336 }
337
338 return true;
339}
340
342{
344
345 if ( mDataProvider )
346 mDataProvider->setTransformContext( transformContext );
347
348 mTransformContext = transformContext;
350}
351
352QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
353{
355
356 QString error;
357 QStringList warnings;
358 resultFlag = loadDefaultStyle( error, warnings );
359 return error;
360}
361
363{
365
367 if ( mSourceType == QLatin1String( "xyz" ) )
368 {
369 // always consider xyz vector tiles as basemap layers
371 }
372 else
373 {
374 // TODO when should we consider mbtiles layers as basemap layers? potentially if their extent is "large"?
375 }
376
377 return res;
378}
379
380bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
381{
383
384 return loadDefaultStyleAndSubLayersPrivate( error, warnings, nullptr );
385}
386
387bool QgsVectorTileLayer::loadDefaultStyleAndSubLayers( QString &error, QStringList &warnings, QList<QgsMapLayer *> &subLayers )
388{
390
391 return loadDefaultStyleAndSubLayersPrivate( error, warnings, &subLayers );
392}
393
394bool QgsVectorTileLayer::loadDefaultStyleAndSubLayersPrivate( QString &error, QStringList &warnings, QList<QgsMapLayer *> *subLayers )
395{
397 QgsVectorTileDataProvider *vtProvider = qgis::down_cast< QgsVectorTileDataProvider *> ( mDataProvider.get() );
398 if ( !vtProvider )
399 return false;
400
401 QgsDataSourceUri dsUri;
402 dsUri.setEncodedUri( mDataSource );
403
404 QVariantMap styleDefinition;
406 QString styleUrl;
407 if ( !dsUri.param( QStringLiteral( "styleUrl" ) ).isEmpty() )
408 {
409 styleUrl = dsUri.param( QStringLiteral( "styleUrl" ) );
410 }
411 else
412 {
413 styleUrl = vtProvider->styleUrl();
414 }
415
416 styleDefinition = vtProvider->styleDefinition();
417 const QVariantMap spriteDefinition = vtProvider->spriteDefinition();
418 if ( !spriteDefinition.isEmpty() )
419 {
420 const QImage spriteImage = vtProvider->spriteImage();
421 context.setSprites( spriteImage, spriteDefinition );
422 }
423
424 if ( !styleDefinition.isEmpty() || !styleUrl.isEmpty() )
425 {
426 if ( styleDefinition.isEmpty() )
427 {
428 QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
429
430 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );
431
432 QgsBlockingNetworkRequest networkRequest;
433 switch ( networkRequest.get( request ) )
434 {
436 break;
437
441 error = QObject::tr( "Error retrieving default style" );
442 return false;
443 }
444
445 const QgsNetworkReplyContent content = networkRequest.reply();
446 styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
447 }
448
449 QgsVectorTileUtils::loadSprites( styleDefinition, context, styleUrl );
450 }
451
452 if ( !styleDefinition.isEmpty() )
453 {
454 // convert automatically from pixel sizes to millimeters, because pixel sizes
455 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
457 //assume source uses 96 dpi
458 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
459
460 QgsMapBoxGlStyleConverter converter;
461 if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
462 {
463 warnings = converter.warnings();
464 error = converter.errorMessage();
465 return false;
466 }
467
468 setRenderer( converter.renderer() );
469 setLabeling( converter.labeling() );
470 warnings = converter.warnings();
471
472 if ( subLayers )
473 {
474 *subLayers = converter.createSubLayers();
475 }
476
477 return true;
478 }
479 else
480 {
481 bool resultFlag = false;
482 error = QgsMapLayer::loadDefaultStyle( resultFlag );
483 return resultFlag;
484 }
485}
486
488{
490
491 resultFlag = false;
492 if ( !mDataProvider || !mDataProvider->isValid() )
493 return QString();
494
495 if ( qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerCapabilities() & Qgis::VectorTileProviderCapability::ReadLayerMetadata )
496 {
497 setMetadata( mDataProvider->layerMetadata() );
498 }
499 else
500 {
502 }
503 resultFlag = true;
504 return QString();
505}
506
513
514QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
515{
517
518 return QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, source, context );
519}
520
522{
524
525 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
526
527 QString info = QStringLiteral( "<html><head></head>\n<body>\n" );
528
529 info += generalHtmlMetadata();
530
531 info += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
532 QStringLiteral( "<table class=\"list-view\">\n" );
533
534 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source type" ) % QStringLiteral( "</td><td>" ) % sourceType() % QStringLiteral( "</td></tr>\n" );
535
536 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Zoom levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % QStringLiteral( "</td></tr>\n" );
537
538 if ( mDataProvider )
539 info += qobject_cast< const QgsVectorTileDataProvider * >( mDataProvider.get() )->htmlMetadata();
540
541 info += QLatin1String( "</table>\n<br>" );
542
543 // CRS
544 info += crsHtmlMetadata();
545
546 // Identification section
547 info += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
548 htmlFormatter.identificationSectionHtml() %
549 QStringLiteral( "<br>\n" ) %
550
551 // extent section
552 QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
553 htmlFormatter.extentSectionHtml( ) %
554 QStringLiteral( "<br>\n" ) %
555
556 // Start the Access section
557 QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
558 htmlFormatter.accessSectionHtml( ) %
559 QStringLiteral( "<br>\n" ) %
560
561
562 // Start the contacts section
563 QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
564 htmlFormatter.contactsSectionHtml( ) %
565 QStringLiteral( "<br><br>\n" ) %
566
567 // Start the links section
568 QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
569 htmlFormatter.linksSectionHtml( ) %
570 QStringLiteral( "<br>\n" ) %
571
572 // Start the history section
573 QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
574 htmlFormatter.historySectionHtml( ) %
575 QStringLiteral( "<br>\n" ) %
576
578
579 QStringLiteral( "\n</body>\n</html>\n" );
580
581 return info;
582}
583
585{
586 if ( QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() ) )
587 return vtProvider->sourcePath();
588
589 return QString();
590}
591
593{
595
596 QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() );
597 if ( !vtProvider )
598 return QgsVectorTileRawData();
599
600 return vtProvider->readTile( mMatrixSet, tileID );
601}
602
610
612{
614
615 return mRenderer.get();
616}
617
625
627{
629
630 return mLabeling.get();
631}
632
634{
635 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
637
638 return mLabelsEnabled && static_cast< bool >( mLabeling );
639}
640
642{
644
645 mLabelsEnabled = enabled;
646}
647
649{
651
652 QList< QgsFeature > res;
653 res.reserve( mSelectedFeatures.size() );
654 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
655 res.append( it.value() );
656
657 return res;
658}
659
661{
663
664 return mSelectedFeatures.size();
665}
666
668{
670
671 if ( !isInScaleRange( context.scale() ) )
672 {
673 QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
674 return;
675 }
676
677 QSet< QgsFeatureId > prevSelection;
678 prevSelection.reserve( mSelectedFeatures.size() );
679 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
680 prevSelection.insert( it.key() );
681
683 {
684 switch ( behavior )
685 {
687 mSelectedFeatures.clear();
688 break;
689
693 break;
694 }
695 }
696
697 QgsGeometry selectionGeom = geometry;
698 bool isPointOrRectangle;
699 QgsPointXY point;
700 bool isSinglePoint = selectionGeom.type() == Qgis::GeometryType::Point;
701 if ( isSinglePoint )
702 {
703 isPointOrRectangle = true;
704 point = selectionGeom.asPoint();
706 }
707 else
708 {
709 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly instersection tests later
710 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
711 }
712
713 auto addDerivedFields = []( QgsFeature & feature, const int tileZoom, const QString & layer )
714 {
715 QgsFields fields = feature.fields();
716 fields.append( QgsField( QStringLiteral( "tile_zoom" ), QMetaType::Type::Int ) );
717 fields.append( QgsField( QStringLiteral( "tile_layer" ), QMetaType::Type::QString ) );
718 QgsAttributes attributes = feature.attributes();
719 attributes << tileZoom << layer;
720 feature.setFields( fields );
721 feature.setAttributes( attributes );
722 };
723
724 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
725 QList< QgsFeature > singleSelectCandidates;
726
727 QgsRectangle r;
728 if ( isSinglePoint )
729 {
730 r = QgsRectangle( point.x(), point.y(), point.x(), point.y() );
731 }
732 else
733 {
734 r = selectionGeom.boundingBox();
735
736 if ( !isPointOrRectangle || relationship == Qgis::SelectGeometryRelationship::Within )
737 {
738 // use prepared geometry for faster intersection test
739 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
740 }
741 }
742
743 switch ( behavior )
744 {
747 {
748 // when adding to or setting a selection, we retrieve the tile data for the current scale
749 const int tileZoom = tileMatrixSet().scaleToZoomLevel( context.scale() );
750 const QgsTileMatrix tileMatrix = tileMatrixSet().tileMatrix( tileZoom );
751 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
752 const QVector< QgsTileXYZ> tiles = tileMatrixSet().tilesInRange( tileRange, tileZoom );
753
754 for ( const QgsTileXYZ &tileID : tiles )
755 {
756 const QgsVectorTileRawData data = getRawTile( tileID );
757 if ( data.data.isEmpty() )
758 continue; // failed to get data
759
761 if ( !decoder.decode( data ) )
762 continue; // failed to decode
763
764 QMap<QString, QgsFields> perLayerFields;
765 const QStringList layerNames = decoder.layers();
766 for ( const QString &layerName : layerNames )
767 {
768 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
769 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
770 }
771
772 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
773 const QStringList featuresLayerNames = features.keys();
774 for ( const QString &layerName : featuresLayerNames )
775 {
776 const QgsFields fFields = perLayerFields[layerName];
777 const QVector<QgsFeature> &layerFeatures = features[layerName];
778 for ( const QgsFeature &f : layerFeatures )
779 {
780 if ( renderContext && mRenderer && !mRenderer->willRenderFeature( f, tileID.zoomLevel(), layerName, *renderContext ) )
781 continue;
782
783 if ( f.geometry().intersects( r ) )
784 {
785 bool selectFeature = true;
786 if ( selectionGeomPrepared )
787 {
788 switch ( relationship )
789 {
791 selectFeature = selectionGeomPrepared->intersects( f.geometry().constGet() );
792 break;
794 selectFeature = selectionGeomPrepared->contains( f.geometry().constGet() );
795 break;
796 }
797 }
798
799 if ( selectFeature )
800 {
801 QgsFeature derivedFeature = f;
802 addDerivedFields( derivedFeature, tileID.zoomLevel(), layerName );
804 singleSelectCandidates << derivedFeature;
805 else
806 mSelectedFeatures.insert( derivedFeature.id(), derivedFeature );
807 }
808 }
809 }
810 }
811 }
812 break;
813 }
814
817 {
818 // when removing from the selection, we instead just iterate over the current selection and test against the geometry
819 // we do this as we don't want the selection removal operation to depend at all on the tile zoom
820 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); )
821 {
822 bool matchesGeometry = false;
823 if ( selectionGeomPrepared )
824 {
825 switch ( relationship )
826 {
828 matchesGeometry = selectionGeomPrepared->intersects( it->geometry().constGet() );
829 break;
831 matchesGeometry = selectionGeomPrepared->contains( it->geometry().constGet() );
832 break;
833 }
834 }
835 else
836 {
837 switch ( relationship )
838 {
840 matchesGeometry = it->geometry().intersects( r );
841 break;
843 matchesGeometry = r.contains( it->geometry().boundingBox() );
844 break;
845 }
846 }
847
849 {
850 singleSelectCandidates << it.value();
851 it++;
852 }
853 else if ( ( matchesGeometry && behavior == Qgis::SelectBehavior::IntersectSelection )
854 || ( !matchesGeometry && behavior == Qgis::SelectBehavior::RemoveFromSelection ) )
855 {
856 it++;
857 }
858 else
859 {
860 it = mSelectedFeatures.erase( it );
861 }
862 }
863 break;
864 }
865 }
866
867 if ( ( flags & Qgis::SelectionFlag::SingleFeatureSelection ) && !singleSelectCandidates.empty() )
868 {
869 QgsFeature bestCandidate;
870
872 {
873 // 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
874
875 // find smallest feature in the current selection
876 double smallestArea = std::numeric_limits< double >::max();
877 double smallestLength = std::numeric_limits< double >::max();
878 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
879 {
880 if ( !mSelectedFeatures.contains( candidate.id() ) )
881 continue;
882
883 switch ( candidate.geometry().type() )
884 {
886 bestCandidate = candidate;
887 break;
889 {
890 const double length = candidate.geometry().length();
891 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
892 {
893 bestCandidate = candidate;
894 smallestLength = length;
895 }
896 break;
897 }
899 {
900 const double area = candidate.geometry().area();
901 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
902 {
903 bestCandidate = candidate;
904 smallestArea = area;
905 }
906 break;
907 }
910 break;
911 }
912 }
913 }
914
915 if ( !bestCandidate.isValid() )
916 {
917 // find smallest feature (ie. pick the "hardest" one to click on)
918 double smallestArea = std::numeric_limits< double >::max();
919 double smallestLength = std::numeric_limits< double >::max();
920 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
921 {
922 switch ( candidate.geometry().type() )
923 {
925 bestCandidate = candidate;
926 break;
928 {
929 const double length = candidate.geometry().length();
930 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
931 {
932 bestCandidate = candidate;
933 smallestLength = length;
934 }
935 break;
936 }
938 {
939 const double area = candidate.geometry().area();
940 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
941 {
942 bestCandidate = candidate;
943 smallestArea = area;
944 }
945 break;
946 }
949 break;
950 }
951 }
952 }
953
955 {
956 if ( prevSelection.contains( bestCandidate.id() ) )
957 mSelectedFeatures.remove( bestCandidate.id() );
958 else
959 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
960 }
961 else
962 {
963 switch ( behavior )
964 {
967 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
968 break;
969
971 {
972 if ( mSelectedFeatures.contains( bestCandidate.id() ) )
973 {
974 mSelectedFeatures.clear();
975 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
976 }
977 else
978 {
979 mSelectedFeatures.clear();
980 }
981 break;
982 }
983
985 {
986 mSelectedFeatures.remove( bestCandidate.id() );
987 break;
988 }
989 }
990 }
991 }
992
993 QSet< QgsFeatureId > newSelection;
994 newSelection.reserve( mSelectedFeatures.size() );
995 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
996 newSelection.insert( it.key() );
997
998 // signal
999 if ( prevSelection != newSelection )
1000 emit selectionChanged();
1001}
1002
1004{
1006
1007 if ( mSelectedFeatures.empty() )
1008 return;
1009
1010 mSelectedFeatures.clear();
1011 emit selectionChanged();
1012}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:56
@ IsBasemapLayer
Layer is considered a 'basemap' layer, and certain properties of the layer should be ignored when cal...
Definition qgis.h:2281
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata().
Definition qgis.h:5589
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
Definition qgis.h:5571
QFlags< SelectionFlag > SelectionFlags
Flags which control feature selection behavior.
Definition qgis.h:1807
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:4930
@ Point
Points.
Definition qgis.h:359
@ Line
Lines.
Definition qgis.h:360
@ Polygon
Polygons.
Definition qgis.h:361
@ Unknown
Unknown types.
Definition qgis.h:362
@ Null
No geometry.
Definition qgis.h:363
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:486
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:195
@ Millimeters
Millimeters.
Definition qgis.h:5184
@ ToggleSelection
Enables a "toggle" selection mode, where previously selected matching features will be deselected and...
Definition qgis.h:1799
@ SingleFeatureSelection
Select only a single feature, picking the "best" match for the selection geometry.
Definition qgis.h:1798
QFlags< MapLayerProperty > MapLayerProperties
Map layer properties.
Definition qgis.h:2285
SelectGeometryRelationship
Geometry relationship test to apply for selecting features.
Definition qgis.h:1785
@ Within
Select where features are within the reference geometry.
Definition qgis.h:1787
@ Intersect
Select where features intersect the reference geometry.
Definition qgis.h:1786
SelectBehavior
Specifies how a selection should be applied.
Definition qgis.h:1771
@ SetSelection
Set selection, removing any existing selection.
Definition qgis.h:1772
@ AddToSelection
Add selection to current selection.
Definition qgis.h:1773
@ IntersectSelection
Modify current selection to include only select features which match.
Definition qgis.h:1774
@ RemoveFromSelection
Remove from current selection.
Definition qgis.h:1775
A vector of attributes.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
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...
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Abstract base class for spatial data provider implementations.
Stores the component parts of a 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:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
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:69
bool isValid() const
Returns the validity of this feature.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:54
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:73
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
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
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
Formats layer metadata into HTML.
Context for a MapBox GL style conversion operation.
void setTargetUnit(Qgis::RenderUnit targetUnit)
Sets the target unit type.
void setSprites(const QImage &image, const QVariantMap &definitions, const QString &category=QString())
Sets the sprite image and definitions JSON for a given category to use during conversion.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
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.
QString name
Definition qgsmaplayer.h:84
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.
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:86
QgsMapLayer(Qgis::LayerType type=Qgis::LayerType::Vector, const QString &name=QString(), const QString &source=QString())
Constructor for QgsMapLayer.
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.
QFlags< StyleCategory > StyleCategories
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.
QgsMapLayer::LayerFlags flags
Definition qgsmaplayer.h:96
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:92
@ Symbology
Symbology.
@ Rendering
Rendering: scale visibility, simplify method, opacity.
@ Labeling
Labeling.
QString customPropertyHtmlMetadata() const
Returns an HTML fragment containing custom property information, for use in the htmlMetadata() method...
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.
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.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
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.
A container for the context for various read/write operations on 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.
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
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:158
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition qgstiles.cpp:97
A range of tiles in a tile matrix.
Definition qgstiles.h:114
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:39
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.
Provides map rendering functionality for vector tile layers.
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 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).
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...
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...
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...
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.
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.
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 from one or more sources that need to be decoded.
QMap< QString, QByteArray > data
Raw tile data by source ID.
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:61
#define QgsDebugError(str)
Definition qgslogger.h:57
#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.