QGIS API Documentation 3.99.0-Master (8e76e220402)
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 <QString>
41#include <QUrl>
42#include <QUrlQuery>
43
44#include "moc_qgsvectortilelayer.cpp"
45
46using namespace Qt::StringLiterals;
47
48QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName, const LayerOptions &options )
49 : QgsMapLayer( Qgis::LayerType::VectorTile, baseName )
50 , mTransformContext( options.transformContext )
51{
52 mDataSource = uri;
53
54 if ( !uri.isEmpty() )
55 setValid( loadDataSource() );
56
57 // set a default renderer
61
62 connect( this, &QgsVectorTileLayer::selectionChanged, this, [this] { triggerRepaint(); } );
63}
64
65void QgsVectorTileLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &, const QgsDataProvider::ProviderOptions &, Qgis::DataProviderReadFlags )
66{
68
69 mDataSource = dataSource;
70 mLayerName = baseName;
71 mDataProvider.reset();
72
73 setValid( loadDataSource() );
74}
75
76bool QgsVectorTileLayer::loadDataSource()
77{
79
80 QgsDataSourceUri dsUri;
81 dsUri.setEncodedUri( mDataSource );
82
83 setCrs( QgsCoordinateReferenceSystem( u"EPSG:3857"_s ) );
84
85 const QgsDataProvider::ProviderOptions providerOptions { mTransformContext };
87
88 mSourceType = dsUri.param( u"type"_s );
89 QString providerKey;
90 if ( mSourceType == "xyz"_L1 && dsUri.param( u"serviceType"_s ) == "arcgis"_L1 )
91 {
92 providerKey = u"arcgisvectortileservice"_s;
93 }
94 else if ( mSourceType == "xyz"_L1 )
95 {
96 providerKey = u"xyzvectortiles"_s;
97 }
98 else if ( mSourceType == "mbtiles"_L1 )
99 {
100 providerKey = u"mbtilesvectortiles"_s;
101 }
102 else if ( mSourceType == "vtpk"_L1 )
103 {
104 providerKey = u"vtpkvectortiles"_s;
105 }
106 else
107 {
108 QgsDebugError( u"Unknown source type: "_s + mSourceType );
109 return false;
110 }
111
112 mDataProvider.reset( qobject_cast<QgsVectorTileDataProvider *>( QgsProviderRegistry::instance()->createProvider( providerKey, mDataSource, providerOptions, flags ) ) );
113 mProviderKey = mDataProvider ? mDataProvider->name() : providerKey;
114
115 if ( mDataProvider )
116 {
117 mMatrixSet = qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->tileMatrixSet();
118 setCrs( mDataProvider->crs() );
119 setExtent( mDataProvider->extent() );
120 }
121
122 return mDataProvider && mDataProvider->isValid();
123}
124
126
128{
130
131 const QgsVectorTileLayer::LayerOptions options( mTransformContext );
132 QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name(), options );
133 layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
134 layer->setLabeling( labeling() ? labeling()->clone() : nullptr );
135 return layer;
136}
137
144
146{
148
149 return mDataProvider.get();
150}
151
158
159bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
160{
162
163 setValid( loadDataSource() );
164
165 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
166 {
167 const QDomElement matrixSetElement = layerNode.firstChildElement( u"matrixSet"_s );
168 if ( !matrixSetElement.isNull() )
169 {
170 mMatrixSet.readXml( matrixSetElement, context );
171 }
172 }
173 setCrs( mMatrixSet.crs() );
174
175 QString errorMsg;
176 if ( !readSymbology( layerNode, errorMsg, context ) )
177 return false;
178
179 readStyleManager( layerNode );
180 return true;
181}
182
183bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
184{
186
187 QDomElement mapLayerNode = layerNode.toElement();
188 mapLayerNode.setAttribute( u"type"_s, QgsMapLayerFactory::typeToString( Qgis::LayerType::VectorTile ) );
189
190 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
191 {
192 mapLayerNode.appendChild( mMatrixSet.writeXml( doc, context ) );
193 }
194
195 // add provider node
196 if ( mDataProvider )
197 {
198 QDomElement provider = doc.createElement( u"provider"_s );
199 const QDomText providerText = doc.createTextNode( providerType() );
200 provider.appendChild( providerText );
201 mapLayerNode.appendChild( provider );
202 }
203
204 writeStyleManager( layerNode, doc );
205
206 QString errorMsg;
207 return writeSymbology( layerNode, doc, errorMsg, context );
208}
209
210bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
211{
213
214 const QDomElement elem = node.toElement();
215
216 readCommonStyle( elem, context, categories );
217
218 const QDomElement elemRenderer = elem.firstChildElement( u"renderer"_s );
219 if ( elemRenderer.isNull() )
220 {
221 errorMessage = tr( "Missing <renderer> tag" );
222 return false;
223 }
224 const QString rendererType = elemRenderer.attribute( u"type"_s );
225
226 if ( categories.testFlag( Symbology ) )
227 {
228 QgsVectorTileRenderer *r = nullptr;
229 if ( rendererType == "basic"_L1 )
231 else
232 {
233 errorMessage = tr( "Unknown renderer type: " ) + rendererType;
234 return false;
235 }
236
237 r->readXml( elemRenderer, context );
238 setRenderer( r );
239 }
240
241 if ( categories.testFlag( Labeling ) )
242 {
243 setLabeling( nullptr );
244 const QDomElement elemLabeling = elem.firstChildElement( u"labeling"_s );
245 if ( !elemLabeling.isNull() )
246 {
247 const QString labelingType = elemLabeling.attribute( u"type"_s );
249 if ( labelingType == "basic"_L1 )
251 else
252 {
253 errorMessage = tr( "Unknown labeling type: " ) + rendererType;
254 }
255
256 if ( elemLabeling.hasAttribute( u"labelsEnabled"_s ) )
257 mLabelsEnabled = elemLabeling.attribute( u"labelsEnabled"_s ).toInt();
258 else
259 mLabelsEnabled = true;
260
261 if ( labeling )
262 {
263 labeling->readXml( elemLabeling, context );
265 }
266 }
267 }
268
269 if ( categories.testFlag( Symbology ) )
270 {
271 // get and set the blend mode if it exists
272 const QDomNode blendModeNode = node.namedItem( u"blendMode"_s );
273 if ( !blendModeNode.isNull() )
274 {
275 const QDomElement e = blendModeNode.toElement();
276 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
277 }
278 }
279
280 // get and set the layer transparency
281 if ( categories.testFlag( Rendering ) )
282 {
283 const QDomNode layerOpacityNode = node.namedItem( u"layerOpacity"_s );
284 if ( !layerOpacityNode.isNull() )
285 {
286 const QDomElement e = layerOpacityNode.toElement();
287 setOpacity( e.text().toDouble() );
288 }
289 }
290
291 return true;
292}
293
294bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
295{
297
298 Q_UNUSED( errorMessage )
299 QDomElement elem = node.toElement();
300
301 writeCommonStyle( elem, doc, context, categories );
302
303 if ( mRenderer )
304 {
305 QDomElement elemRenderer = doc.createElement( u"renderer"_s );
306 elemRenderer.setAttribute( u"type"_s, mRenderer->type() );
307 if ( categories.testFlag( Symbology ) )
308 {
309 mRenderer->writeXml( elemRenderer, context );
310 }
311 elem.appendChild( elemRenderer );
312 }
313
314 if ( mLabeling && categories.testFlag( Labeling ) )
315 {
316 QDomElement elemLabeling = doc.createElement( u"labeling"_s );
317 elemLabeling.setAttribute( u"type"_s, mLabeling->type() );
318 elemLabeling.setAttribute( u"labelsEnabled"_s, mLabelsEnabled ? u"1"_s : u"0"_s );
319 mLabeling->writeXml( elemLabeling, context );
320 elem.appendChild( elemLabeling );
321 }
322
323 if ( categories.testFlag( Symbology ) )
324 {
325 // add the blend mode field
326 QDomElement blendModeElem = doc.createElement( u"blendMode"_s );
327 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
328 blendModeElem.appendChild( blendModeText );
329 node.appendChild( blendModeElem );
330 }
331
332 // add the layer opacity
333 if ( categories.testFlag( Rendering ) )
334 {
335 QDomElement layerOpacityElem = doc.createElement( u"layerOpacity"_s );
336 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
337 layerOpacityElem.appendChild( layerOpacityText );
338 node.appendChild( layerOpacityElem );
339 }
340
341 return true;
342}
343
345{
347
348 if ( mDataProvider )
349 mDataProvider->setTransformContext( transformContext );
350
351 mTransformContext = transformContext;
353}
354
355QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
356{
358
359 QString error;
360 QStringList warnings;
361 resultFlag = loadDefaultStyle( error, warnings );
362 return error;
363}
364
366{
368
370 if ( mSourceType == "xyz"_L1 )
371 {
372 // always consider xyz vector tiles as basemap layers
374 }
375 else
376 {
377 // TODO when should we consider mbtiles layers as basemap layers? potentially if their extent is "large"?
378 }
379
380 return res;
381}
382
383bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
384{
386
387 return loadDefaultStyleAndSubLayersPrivate( error, warnings, nullptr );
388}
389
390bool QgsVectorTileLayer::loadDefaultStyleAndSubLayers( QString &error, QStringList &warnings, QList<QgsMapLayer *> &subLayers )
391{
393
394 return loadDefaultStyleAndSubLayersPrivate( error, warnings, &subLayers );
395}
396
397bool QgsVectorTileLayer::loadDefaultStyleAndSubLayersPrivate( QString &error, QStringList &warnings, QList<QgsMapLayer *> *subLayers )
398{
400 QgsVectorTileDataProvider *vtProvider = qgis::down_cast< QgsVectorTileDataProvider *> ( mDataProvider.get() );
401 if ( !vtProvider )
402 return false;
403
404 QgsDataSourceUri dsUri;
405 dsUri.setEncodedUri( mDataSource );
406
407 QVariantMap styleDefinition;
409 QString styleUrl;
410 if ( !dsUri.param( u"styleUrl"_s ).isEmpty() )
411 {
412 styleUrl = dsUri.param( u"styleUrl"_s );
413 }
414 else
415 {
416 styleUrl = vtProvider->styleUrl();
417 }
418
419 styleDefinition = vtProvider->styleDefinition();
420 const QVariantMap spriteDefinition = vtProvider->spriteDefinition();
421 if ( !spriteDefinition.isEmpty() )
422 {
423 const QImage spriteImage = vtProvider->spriteImage();
424 context.setSprites( spriteImage, spriteDefinition );
425 }
426
427 if ( !styleDefinition.isEmpty() || !styleUrl.isEmpty() )
428 {
429 if ( styleDefinition.isEmpty() )
430 {
431 QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
432
433 QgsSetRequestInitiatorClass( request, u"QgsVectorTileLayer"_s );
434
435 QgsBlockingNetworkRequest networkRequest;
436 switch ( networkRequest.get( request ) )
437 {
439 break;
440
444 error = QObject::tr( "Error retrieving default style" );
445 return false;
446 }
447
448 const QgsNetworkReplyContent content = networkRequest.reply();
449 styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
450 }
451
452 QgsVectorTileUtils::loadSprites( styleDefinition, context, styleUrl );
453 }
454
455 if ( !styleDefinition.isEmpty() )
456 {
457 // convert automatically from pixel sizes to millimeters, because pixel sizes
458 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
460 //assume source uses 96 dpi
461 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
462
463 QgsMapBoxGlStyleConverter converter;
464 if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
465 {
466 warnings = converter.warnings();
467 error = converter.errorMessage();
468 return false;
469 }
470
471 setRenderer( converter.renderer() );
472 setLabeling( converter.labeling() );
473 warnings = converter.warnings();
474
475 if ( subLayers )
476 {
477 *subLayers = converter.createSubLayers();
478 }
479
480 return true;
481 }
482 else
483 {
484 bool resultFlag = false;
485 error = QgsMapLayer::loadDefaultStyle( resultFlag );
486 return resultFlag;
487 }
488}
489
491{
493
494 resultFlag = false;
495 if ( !mDataProvider || !mDataProvider->isValid() )
496 return QString();
497
498 if ( qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerCapabilities() & Qgis::VectorTileProviderCapability::ReadLayerMetadata )
499 {
500 setMetadata( mDataProvider->layerMetadata() );
501 }
502 else
503 {
505 }
506 resultFlag = true;
507 return QString();
508}
509
516
517QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
518{
520
521 return QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, source, context );
522}
523
525{
527
528 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
529
530 QString info = u"<html><head></head>\n<body>\n"_s;
531
532 info += generalHtmlMetadata();
533
534 info += u"<h1>"_s + tr( "Information from provider" ) + u"</h1>\n<hr>\n"_s %
535 u"<table class=\"list-view\">\n"_s;
536
537 info += u"<tr><td class=\"highlight\">"_s % tr( "Source type" ) % u"</td><td>"_s % sourceType() % u"</td></tr>\n"_s;
538
539 info += u"<tr><td class=\"highlight\">"_s % tr( "Zoom levels" ) % u"</td><td>"_s % u"%1 - %2"_s.arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % u"</td></tr>\n"_s;
540
541 if ( mDataProvider )
542 info += qobject_cast< const QgsVectorTileDataProvider * >( mDataProvider.get() )->htmlMetadata();
543
544 info += "</table>\n<br>"_L1;
545
546 // CRS
547 info += crsHtmlMetadata();
548
549 // Identification section
550 info += u"<h1>"_s % tr( "Identification" ) % u"</h1>\n<hr>\n"_s %
551 htmlFormatter.identificationSectionHtml() %
552 u"<br>\n"_s %
553
554 // extent section
555 u"<h1>"_s % tr( "Extent" ) % u"</h1>\n<hr>\n"_s %
556 htmlFormatter.extentSectionHtml( ) %
557 u"<br>\n"_s %
558
559 // Start the Access section
560 u"<h1>"_s % tr( "Access" ) % u"</h1>\n<hr>\n"_s %
561 htmlFormatter.accessSectionHtml( ) %
562 u"<br>\n"_s %
563
564
565 // Start the contacts section
566 u"<h1>"_s % tr( "Contacts" ) % u"</h1>\n<hr>\n"_s %
567 htmlFormatter.contactsSectionHtml( ) %
568 u"<br><br>\n"_s %
569
570 // Start the links section
571 u"<h1>"_s % tr( "References" ) % u"</h1>\n<hr>\n"_s %
572 htmlFormatter.linksSectionHtml( ) %
573 u"<br>\n"_s %
574
575 // Start the history section
576 u"<h1>"_s % tr( "History" ) % u"</h1>\n<hr>\n"_s %
577 htmlFormatter.historySectionHtml( ) %
578 u"<br>\n"_s %
579
581
582 u"\n</body>\n</html>\n"_s;
583
584 return info;
585}
586
588{
589 if ( QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() ) )
590 return vtProvider->sourcePath();
591
592 return QString();
593}
594
596{
598
599 QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() );
600 if ( !vtProvider )
601 return QgsVectorTileRawData();
602
603 return vtProvider->readTile( mMatrixSet, tileID );
604}
605
613
615{
617
618 return mRenderer.get();
619}
620
628
630{
632
633 return mLabeling.get();
634}
635
637{
638 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
640
641 return mLabelsEnabled && static_cast< bool >( mLabeling );
642}
643
645{
647
648 mLabelsEnabled = enabled;
649}
650
652{
654
655 QList< QgsFeature > res;
656 res.reserve( mSelectedFeatures.size() );
657 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
658 res.append( it.value() );
659
660 return res;
661}
662
664{
666
667 return mSelectedFeatures.size();
668}
669
671{
673
674 if ( !isInScaleRange( context.scale() ) )
675 {
676 QgsDebugMsgLevel( u"Out of scale limits"_s, 2 );
677 return;
678 }
679
680 QSet< QgsFeatureId > prevSelection;
681 prevSelection.reserve( mSelectedFeatures.size() );
682 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
683 prevSelection.insert( it.key() );
684
686 {
687 switch ( behavior )
688 {
690 mSelectedFeatures.clear();
691 break;
692
696 break;
697 }
698 }
699
700 QgsGeometry selectionGeom = geometry;
701 bool isPointOrRectangle;
702 QgsPointXY point;
703 bool isSinglePoint = selectionGeom.type() == Qgis::GeometryType::Point;
704 if ( isSinglePoint )
705 {
706 isPointOrRectangle = true;
707 point = selectionGeom.asPoint();
709 }
710 else
711 {
712 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly instersection tests later
713 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
714 }
715
716 auto addDerivedFields = []( QgsFeature & feature, const int tileZoom, const QString & layer )
717 {
718 QgsFields fields = feature.fields();
719 fields.append( QgsField( u"tile_zoom"_s, QMetaType::Type::Int ) );
720 fields.append( QgsField( u"tile_layer"_s, QMetaType::Type::QString ) );
721 QgsAttributes attributes = feature.attributes();
722 attributes << tileZoom << layer;
723 feature.setFields( fields );
724 feature.setAttributes( attributes );
725 };
726
727 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
728 QList< QgsFeature > singleSelectCandidates;
729
730 QgsRectangle r;
731 if ( isSinglePoint )
732 {
733 r = QgsRectangle( point.x(), point.y(), point.x(), point.y() );
734 }
735 else
736 {
737 r = selectionGeom.boundingBox();
738
739 if ( !isPointOrRectangle || relationship == Qgis::SelectGeometryRelationship::Within )
740 {
741 // use prepared geometry for faster intersection test
742 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
743 }
744 }
745
746 switch ( behavior )
747 {
750 {
751 // when adding to or setting a selection, we retrieve the tile data for the current scale
752 const int tileZoom = tileMatrixSet().scaleToZoomLevel( context.scale() );
753 const QgsTileMatrix tileMatrix = tileMatrixSet().tileMatrix( tileZoom );
754 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
755 const QVector< QgsTileXYZ> tiles = tileMatrixSet().tilesInRange( tileRange, tileZoom );
756
757 for ( const QgsTileXYZ &tileID : tiles )
758 {
759 const QgsVectorTileRawData data = getRawTile( tileID );
760 if ( data.data.isEmpty() )
761 continue; // failed to get data
762
764 if ( !decoder.decode( data ) )
765 continue; // failed to decode
766
767 QMap<QString, QgsFields> perLayerFields;
768 const QStringList layerNames = decoder.layers();
769 for ( const QString &layerName : layerNames )
770 {
771 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
772 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
773 }
774
775 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
776 const QStringList featuresLayerNames = features.keys();
777 for ( const QString &layerName : featuresLayerNames )
778 {
779 const QgsFields fFields = perLayerFields[layerName];
780 const QVector<QgsFeature> &layerFeatures = features[layerName];
781 for ( const QgsFeature &f : layerFeatures )
782 {
783 if ( renderContext && mRenderer && !mRenderer->willRenderFeature( f, tileID.zoomLevel(), layerName, *renderContext ) )
784 continue;
785
786 if ( f.geometry().intersects( r ) )
787 {
788 bool selectFeature = true;
789 if ( selectionGeomPrepared )
790 {
791 switch ( relationship )
792 {
794 selectFeature = selectionGeomPrepared->intersects( f.geometry().constGet() );
795 break;
797 selectFeature = selectionGeomPrepared->contains( f.geometry().constGet() );
798 break;
799 }
800 }
801
802 if ( selectFeature )
803 {
804 QgsFeature derivedFeature = f;
805 addDerivedFields( derivedFeature, tileID.zoomLevel(), layerName );
807 singleSelectCandidates << derivedFeature;
808 else
809 mSelectedFeatures.insert( derivedFeature.id(), derivedFeature );
810 }
811 }
812 }
813 }
814 }
815 break;
816 }
817
820 {
821 // when removing from the selection, we instead just iterate over the current selection and test against the geometry
822 // we do this as we don't want the selection removal operation to depend at all on the tile zoom
823 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); )
824 {
825 bool matchesGeometry = false;
826 if ( selectionGeomPrepared )
827 {
828 switch ( relationship )
829 {
831 matchesGeometry = selectionGeomPrepared->intersects( it->geometry().constGet() );
832 break;
834 matchesGeometry = selectionGeomPrepared->contains( it->geometry().constGet() );
835 break;
836 }
837 }
838 else
839 {
840 switch ( relationship )
841 {
843 matchesGeometry = it->geometry().intersects( r );
844 break;
846 matchesGeometry = r.contains( it->geometry().boundingBox() );
847 break;
848 }
849 }
850
852 {
853 singleSelectCandidates << it.value();
854 it++;
855 }
856 else if ( ( matchesGeometry && behavior == Qgis::SelectBehavior::IntersectSelection )
857 || ( !matchesGeometry && behavior == Qgis::SelectBehavior::RemoveFromSelection ) )
858 {
859 it++;
860 }
861 else
862 {
863 it = mSelectedFeatures.erase( it );
864 }
865 }
866 break;
867 }
868 }
869
870 if ( ( flags & Qgis::SelectionFlag::SingleFeatureSelection ) && !singleSelectCandidates.empty() )
871 {
872 QgsFeature bestCandidate;
873
875 {
876 // 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
877
878 // find smallest feature in the current selection
879 double smallestArea = std::numeric_limits< double >::max();
880 double smallestLength = std::numeric_limits< double >::max();
881 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
882 {
883 if ( !mSelectedFeatures.contains( candidate.id() ) )
884 continue;
885
886 switch ( candidate.geometry().type() )
887 {
889 bestCandidate = candidate;
890 break;
892 {
893 const double length = candidate.geometry().length();
894 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
895 {
896 bestCandidate = candidate;
897 smallestLength = length;
898 }
899 break;
900 }
902 {
903 const double area = candidate.geometry().area();
904 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
905 {
906 bestCandidate = candidate;
907 smallestArea = area;
908 }
909 break;
910 }
913 break;
914 }
915 }
916 }
917
918 if ( !bestCandidate.isValid() )
919 {
920 // find smallest feature (ie. pick the "hardest" one to click on)
921 double smallestArea = std::numeric_limits< double >::max();
922 double smallestLength = std::numeric_limits< double >::max();
923 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
924 {
925 switch ( candidate.geometry().type() )
926 {
928 bestCandidate = candidate;
929 break;
931 {
932 const double length = candidate.geometry().length();
933 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
934 {
935 bestCandidate = candidate;
936 smallestLength = length;
937 }
938 break;
939 }
941 {
942 const double area = candidate.geometry().area();
943 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
944 {
945 bestCandidate = candidate;
946 smallestArea = area;
947 }
948 break;
949 }
952 break;
953 }
954 }
955 }
956
958 {
959 if ( prevSelection.contains( bestCandidate.id() ) )
960 mSelectedFeatures.remove( bestCandidate.id() );
961 else
962 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
963 }
964 else
965 {
966 switch ( behavior )
967 {
970 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
971 break;
972
974 {
975 if ( mSelectedFeatures.contains( bestCandidate.id() ) )
976 {
977 mSelectedFeatures.clear();
978 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
979 }
980 else
981 {
982 mSelectedFeatures.clear();
983 }
984 break;
985 }
986
988 {
989 mSelectedFeatures.remove( bestCandidate.id() );
990 break;
991 }
992 }
993 }
994 }
995
996 QSet< QgsFeatureId > newSelection;
997 newSelection.reserve( mSelectedFeatures.size() );
998 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
999 newSelection.insert( it.key() );
1000
1001 // signal
1002 if ( prevSelection != newSelection )
1003 emit selectionChanged();
1004}
1005
1007{
1009
1010 if ( mSelectedFeatures.empty() )
1011 return;
1012
1013 mSelectedFeatures.clear();
1014 emit selectionChanged();
1015}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:59
@ IsBasemapLayer
Layer is considered a 'basemap' layer, and certain properties of the layer should be ignored when cal...
Definition qgis.h:2339
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata().
Definition qgis.h:5890
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
Definition qgis.h:5872
QFlags< SelectionFlag > SelectionFlags
Flags which control feature selection behavior.
Definition qgis.h:1865
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5037
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
@ Unknown
Unknown types.
Definition qgis.h:369
@ Null
No geometry.
Definition qgis.h:370
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:505
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
@ Millimeters
Millimeters.
Definition qgis.h:5291
@ ToggleSelection
Enables a "toggle" selection mode, where previously selected matching features will be deselected and...
Definition qgis.h:1857
@ SingleFeatureSelection
Select only a single feature, picking the "best" match for the selection geometry.
Definition qgis.h:1856
QFlags< MapLayerProperty > MapLayerProperties
Map layer properties.
Definition qgis.h:2343
SelectGeometryRelationship
Geometry relationship test to apply for selecting features.
Definition qgis.h:1843
@ Within
Select where features are within the reference geometry.
Definition qgis.h:1845
@ Intersect
Select where features intersect the reference geometry.
Definition qgis.h:1844
SelectBehavior
Specifies how a selection should be applied.
Definition qgis.h:1829
@ SetSelection
Set selection, removing any existing selection.
Definition qgis.h:1830
@ AddToSelection
Add selection to current selection.
Definition qgis.h:1831
@ IntersectSelection
Modify current selection to include only select features which match.
Definition qgis.h:1832
@ RemoveFromSelection
Remove from current selection.
Definition qgis.h:1833
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:60
QgsAttributes attributes
Definition qgsfeature.h:69
QgsFields fields
Definition qgsfeature.h:70
QgsFeatureId id
Definition qgsfeature.h:68
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:71
bool isValid() const
Returns the validity of this feature.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
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:76
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:87
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:89
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:99
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:95
@ 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:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
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:432
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition qgstiles.cpp:160
int scaleToZoomLevel(double scale, bool clamp=true) const
Finds the best fitting (integer) zoom level given a map scale denominator.
Definition qgstiles.cpp:287
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition qgstiles.h:162
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition qgstiles.cpp:101
A range of tiles in a tile matrix.
Definition qgstiles.h:118
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:43
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:63
#define QgsDebugError(str)
Definition qgslogger.h:59
#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.