QGIS API Documentation 4.3.0-Master (16eddb2670e)
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
129
131{
133
134 const QgsVectorTileLayer::LayerOptions options( mTransformContext );
135 QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name(), options );
136 layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
137 layer->setLabeling( labeling() ? labeling()->clone() : nullptr );
138 return layer;
139}
140
147
149{
151
152 return mDataProvider.get();
153}
154
161
162bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
163{
165
166 setValid( loadDataSource() );
167
168 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
169 {
170 const QDomElement matrixSetElement = layerNode.firstChildElement( u"matrixSet"_s );
171 if ( !matrixSetElement.isNull() )
172 {
173 mMatrixSet.readXml( matrixSetElement, context );
174 }
175 }
176 setCrs( mMatrixSet.crs() );
177
178 QString errorMsg;
179 if ( !readSymbology( layerNode, errorMsg, context ) )
180 return false;
181
182 readStyleManager( layerNode );
183 return true;
184}
185
186bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
187{
189
190 QDomElement mapLayerNode = layerNode.toElement();
191 mapLayerNode.setAttribute( u"type"_s, QgsMapLayerFactory::typeToString( Qgis::LayerType::VectorTile ) );
192
193 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
194 {
195 mapLayerNode.appendChild( mMatrixSet.writeXml( doc, context ) );
196 }
197
198 // add provider node
199 if ( mDataProvider )
200 {
201 QDomElement provider = doc.createElement( u"provider"_s );
202 const QDomText providerText = doc.createTextNode( providerType() );
203 provider.appendChild( providerText );
204 mapLayerNode.appendChild( provider );
205 }
206
207 writeStyleManager( layerNode, doc );
208
209 QString errorMsg;
210 return writeSymbology( layerNode, doc, errorMsg, context );
211}
212
213bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
214{
216
217 const QDomElement elem = node.toElement();
218
219 readCommonStyle( elem, context, categories );
220
221 const QDomElement elemRenderer = elem.firstChildElement( u"renderer"_s );
222 if ( elemRenderer.isNull() )
223 {
224 errorMessage = tr( "Missing <renderer> tag" );
225 return false;
226 }
227 const QString rendererType = elemRenderer.attribute( u"type"_s );
228
229 if ( categories.testFlag( Symbology ) )
230 {
231 QgsVectorTileRenderer *r = nullptr;
232 if ( rendererType == "basic"_L1 )
234 else
235 {
236 errorMessage = tr( "Unknown renderer type: " ) + rendererType;
237 return false;
238 }
239
240 r->readXml( elemRenderer, context );
241 setRenderer( r );
242 }
243
244 if ( categories.testFlag( Labeling ) )
245 {
246 setLabeling( nullptr );
247 const QDomElement elemLabeling = elem.firstChildElement( u"labeling"_s );
248 if ( !elemLabeling.isNull() )
249 {
250 const QString labelingType = elemLabeling.attribute( u"type"_s );
252 if ( labelingType == "basic"_L1 )
254 else
255 {
256 errorMessage = tr( "Unknown labeling type: " ) + rendererType;
257 }
258
259 if ( elemLabeling.hasAttribute( u"labelsEnabled"_s ) )
260 mLabelsEnabled = elemLabeling.attribute( u"labelsEnabled"_s ).toInt();
261 else
262 mLabelsEnabled = true;
263
264 if ( labeling )
265 {
266 labeling->readXml( elemLabeling, context );
268 }
269 }
270 }
271
272 if ( categories.testFlag( Symbology ) )
273 {
274 // get and set the blend mode if it exists
275 const QDomNode blendModeNode = node.namedItem( u"blendMode"_s );
276 if ( !blendModeNode.isNull() )
277 {
278 const QDomElement e = blendModeNode.toElement();
279 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
280 }
281 }
282
283 // get and set the layer transparency
284 if ( categories.testFlag( Rendering ) )
285 {
286 const QDomNode layerOpacityNode = node.namedItem( u"layerOpacity"_s );
287 if ( !layerOpacityNode.isNull() )
288 {
289 const QDomElement e = layerOpacityNode.toElement();
290 setOpacity( e.text().toDouble() );
291 }
292 }
293
294 return true;
295}
296
297bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
298{
300
301 Q_UNUSED( errorMessage )
302 QDomElement elem = node.toElement();
303
304 writeCommonStyle( elem, doc, context, categories );
305
306 if ( mRenderer )
307 {
308 QDomElement elemRenderer = doc.createElement( u"renderer"_s );
309 elemRenderer.setAttribute( u"type"_s, mRenderer->type() );
310 if ( categories.testFlag( Symbology ) )
311 {
312 mRenderer->writeXml( elemRenderer, context );
313 }
314 elem.appendChild( elemRenderer );
315 }
316
317 if ( mLabeling && categories.testFlag( Labeling ) )
318 {
319 QDomElement elemLabeling = doc.createElement( u"labeling"_s );
320 elemLabeling.setAttribute( u"type"_s, mLabeling->type() );
321 elemLabeling.setAttribute( u"labelsEnabled"_s, mLabelsEnabled ? u"1"_s : u"0"_s );
322 mLabeling->writeXml( elemLabeling, context );
323 elem.appendChild( elemLabeling );
324 }
325
326 if ( categories.testFlag( Symbology ) )
327 {
328 // add the blend mode field
329 QDomElement blendModeElem = doc.createElement( u"blendMode"_s );
330 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
331 blendModeElem.appendChild( blendModeText );
332 node.appendChild( blendModeElem );
333 }
334
335 // add the layer opacity
336 if ( categories.testFlag( Rendering ) )
337 {
338 QDomElement layerOpacityElem = doc.createElement( u"layerOpacity"_s );
339 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
340 layerOpacityElem.appendChild( layerOpacityText );
341 node.appendChild( layerOpacityElem );
342 }
343
344 return true;
345}
346
348{
350
351 if ( mDataProvider )
352 mDataProvider->setTransformContext( transformContext );
353
354 mTransformContext = transformContext;
356}
357
358QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
359{
361
362 QString error;
363 QStringList warnings;
364 resultFlag = loadDefaultStyle( error, warnings );
365 return error;
366}
367
369{
371
373 if ( mSourceType == "xyz"_L1 )
374 {
375 // always consider xyz vector tiles as basemap layers
377 }
378 else
379 {
380 // TODO when should we consider mbtiles layers as basemap layers? potentially if their extent is "large"?
381 }
382
383 return res;
384}
385
386bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
387{
389
390 return loadDefaultStyleAndSubLayersPrivate( error, warnings, nullptr );
391}
392
393bool QgsVectorTileLayer::loadDefaultStyleAndSubLayers( QString &error, QStringList &warnings, QList<QgsMapLayer *> &subLayers )
394{
396
397 return loadDefaultStyleAndSubLayersPrivate( error, warnings, &subLayers );
398}
399
400bool QgsVectorTileLayer::loadDefaultStyleAndSubLayersPrivate( QString &error, QStringList &warnings, QList<QgsMapLayer *> *subLayers )
401{
403 QgsVectorTileDataProvider *vtProvider = qgis::down_cast< QgsVectorTileDataProvider *>( mDataProvider.get() );
404 if ( !vtProvider )
405 return false;
406
407 QgsDataSourceUri dsUri;
408 dsUri.setEncodedUri( mDataSource );
409
410 QVariantMap styleDefinition;
412 QString styleUrl;
413 if ( !dsUri.param( u"styleUrl"_s ).isEmpty() )
414 {
415 styleUrl = dsUri.param( u"styleUrl"_s );
416 }
417 else
418 {
419 styleUrl = vtProvider->styleUrl();
420 }
421
422 styleDefinition = vtProvider->styleDefinition();
423 const QVariantMap spriteDefinition = vtProvider->spriteDefinition();
424 if ( !spriteDefinition.isEmpty() )
425 {
426 const QImage spriteImage = vtProvider->spriteImage();
427 context.setSprites( spriteImage, spriteDefinition );
428 }
429
430 if ( !styleDefinition.isEmpty() || !styleUrl.isEmpty() )
431 {
432 if ( styleDefinition.isEmpty() )
433 {
434 QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
435
436 QgsSetRequestInitiatorClass( request, u"QgsVectorTileLayer"_s );
437
438 QgsBlockingNetworkRequest networkRequest;
439 switch ( networkRequest.get( request ) )
440 {
442 break;
443
447 error = QObject::tr( "Error retrieving default style" );
448 return false;
449 }
450
451 const QgsNetworkReplyContent content = networkRequest.reply();
452 styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
453 }
454
455 QgsVectorTileUtils::loadSprites( styleDefinition, context, styleUrl );
456 }
457
458 if ( !styleDefinition.isEmpty() )
459 {
460 // convert automatically from pixel sizes to millimeters, because pixel sizes
461 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
463 //assume source uses 96 dpi
464 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
465
466 QgsMapBoxGlStyleConverter converter;
467 if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
468 {
469 warnings = converter.warnings();
470 error = converter.errorMessage();
471 return false;
472 }
473
474 setRenderer( converter.renderer().release() );
475 setLabeling( converter.labeling().release() );
476 warnings = converter.warnings();
477
478 if ( subLayers )
479 {
480 *subLayers = converter.createSubLayers();
481 }
482
483 return true;
484 }
485 else
486 {
487 bool resultFlag = false;
488 error = QgsMapLayer::loadDefaultStyle( resultFlag );
489 return resultFlag;
490 }
491}
492
494{
496
497 resultFlag = false;
498 if ( !mDataProvider || !mDataProvider->isValid() )
499 return QString();
500
501 if ( qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerCapabilities() & Qgis::VectorTileProviderCapability::ReadLayerMetadata )
502 {
503 setMetadata( mDataProvider->layerMetadata() );
504 }
505 else
506 {
508 }
509 resultFlag = true;
510 return QString();
511}
512
519
520QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
521{
523
524 return QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, source, context );
525}
526
528{
530
531 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
532
533 QString info = u"<html><head></head>\n<body>\n"_s;
534
535 info += generalHtmlMetadata();
536
537 info += u"<h1>"_s + tr( "Information from provider" ) + u"</h1>\n<hr>\n"_s % u"<table class=\"list-view\">\n"_s;
538
539 info += u"<tr><td class=\"highlight\">"_s % tr( "Source type" ) % u"</td><td>"_s % sourceType() % u"</td></tr>\n"_s;
540
541 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;
542
543 if ( mDataProvider )
544 info += qobject_cast< const QgsVectorTileDataProvider * >( mDataProvider.get() )->htmlMetadata();
545
546 info += "</table>\n<br>"_L1;
547
548 // CRS
549 info += crsHtmlMetadata();
550
551 // Identification section
552 info += u"<h1>"_s
553 % tr( "Identification" )
554 % u"</h1>\n<hr>\n"_s
555 % htmlFormatter.identificationSectionHtml()
556 % u"<br>\n"_s
557 %
558
559 // extent section
560 u"<h1>"_s
561 % tr( "Extent" )
562 % u"</h1>\n<hr>\n"_s
563 % htmlFormatter.extentSectionHtml()
564 % u"<br>\n"_s
565 %
566
567 // Start the Access section
568 u"<h1>"_s
569 % tr( "Access" )
570 % u"</h1>\n<hr>\n"_s
571 % htmlFormatter.accessSectionHtml()
572 % u"<br>\n"_s
573 %
574
575
576 // Start the contacts section
577 u"<h1>"_s
578 % tr( "Contacts" )
579 % u"</h1>\n<hr>\n"_s
580 % htmlFormatter.contactsSectionHtml()
581 % u"<br><br>\n"_s
582 %
583
584 // Start the links section
585 u"<h1>"_s
586 % tr( "References" )
587 % u"</h1>\n<hr>\n"_s
588 % htmlFormatter.linksSectionHtml()
589 % u"<br>\n"_s
590 %
591
592 // Start the history section
593 u"<h1>"_s
594 % tr( "History" )
595 % u"</h1>\n<hr>\n"_s
596 % htmlFormatter.historySectionHtml()
597 % u"<br>\n"_s
598 %
599
601 %
602
603 u"\n</body>\n</html>\n"_s;
604
605 return info;
606}
607
609{
610 if ( QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() ) )
611 return vtProvider->sourcePath();
612
613 return QString();
614}
615
617{
619
620 QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() );
621 if ( !vtProvider )
622 return QgsVectorTileRawData();
623
624 return vtProvider->readTile( mMatrixSet, tileID );
625}
626
634
636{
638
639 return mRenderer.get();
640}
641
649
651{
653
654 return mLabeling.get();
655}
656
658{
659 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
661
662 return mLabelsEnabled && static_cast< bool >( mLabeling );
663}
664
666{
668
669 mLabelsEnabled = enabled;
670}
671
673{
675
676 QList< QgsFeature > res;
677 res.reserve( mSelectedFeatures.size() );
678 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
679 res.append( it.value() );
680
681 return res;
682}
683
685{
687
688 return mSelectedFeatures.size();
689}
690
692 const QgsGeometry &geometry, const QgsSelectionContext &context, Qgis::SelectBehavior behavior, Qgis::SelectGeometryRelationship relationship, Qgis::SelectionFlags flags, QgsRenderContext *renderContext
693)
694{
696
697 if ( !isInScaleRange( context.scale() ) )
698 {
699 QgsDebugMsgLevel( u"Out of scale limits"_s, 2 );
700 return;
701 }
702
703 QSet< QgsFeatureId > prevSelection;
704 prevSelection.reserve( mSelectedFeatures.size() );
705 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
706 prevSelection.insert( it.key() );
707
709 {
710 switch ( behavior )
711 {
713 mSelectedFeatures.clear();
714 break;
715
719 break;
720 }
721 }
722
723 QgsGeometry selectionGeom = geometry;
724 bool isPointOrRectangle;
725 QgsPointXY point;
726 bool isSinglePoint = selectionGeom.type() == Qgis::GeometryType::Point;
727 if ( isSinglePoint )
728 {
729 isPointOrRectangle = true;
730 point = selectionGeom.asPoint();
732 }
733 else
734 {
735 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly instersection tests later
736 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isTopologicallyEqual( selectionGeom );
737 }
738
739 auto addDerivedFields = []( QgsFeature &feature, const int tileZoom, const QString &layer ) {
740 QgsFields fields = feature.fields();
741 fields.append( QgsField( u"tile_zoom"_s, QMetaType::Type::Int ) );
742 fields.append( QgsField( u"tile_layer"_s, QMetaType::Type::QString ) );
743 QgsAttributes attributes = feature.attributes();
744 attributes << tileZoom << layer;
745 feature.setFields( fields );
746 feature.setAttributes( attributes );
747 };
748
749 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
750 QList< QgsFeature > singleSelectCandidates;
751
752 QgsRectangle r;
753 if ( isSinglePoint )
754 {
755 r = QgsRectangle( point.x(), point.y(), point.x(), point.y() );
756 }
757 else
758 {
759 r = selectionGeom.boundingBox();
760
761 if ( !isPointOrRectangle || relationship == Qgis::SelectGeometryRelationship::Within )
762 {
763 // use prepared geometry for faster intersection test
764 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
765 }
766 }
767
768 switch ( behavior )
769 {
772 {
773 // when adding to or setting a selection, we retrieve the tile data for the current scale
774 const int tileZoom = tileMatrixSet().scaleToZoomLevel( context.scale() );
775 const QgsTileMatrix tileMatrix = tileMatrixSet().tileMatrix( tileZoom );
776 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
777 const QVector< QgsTileXYZ> tiles = tileMatrixSet().tilesInRange( tileRange, tileZoom );
778
779 for ( const QgsTileXYZ &tileID : tiles )
780 {
781 const QgsVectorTileRawData data = getRawTile( tileID );
782 if ( data.data.isEmpty() )
783 continue; // failed to get data
784
786 if ( !decoder.decode( data ) )
787 continue; // failed to decode
788
789 QMap<QString, QgsFields> perLayerFields;
790 const QStringList layerNames = decoder.layers();
791 for ( const QString &layerName : layerNames )
792 {
793 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
794 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
795 }
796
797 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
798 const QStringList featuresLayerNames = features.keys();
799 for ( const QString &layerName : featuresLayerNames )
800 {
801 const QgsFields fFields = perLayerFields[layerName];
802 const QVector<QgsFeature> &layerFeatures = features[layerName];
803 for ( const QgsFeature &f : layerFeatures )
804 {
805 if ( renderContext && mRenderer && !mRenderer->willRenderFeature( f, tileID.zoomLevel(), layerName, *renderContext ) )
806 continue;
807
808 if ( f.geometry().intersects( r ) )
809 {
810 bool selectFeature = true;
811 if ( selectionGeomPrepared )
812 {
813 switch ( relationship )
814 {
816 selectFeature = selectionGeomPrepared->intersects( f.geometry().constGet() );
817 break;
819 selectFeature = selectionGeomPrepared->contains( f.geometry().constGet() );
820 break;
821 }
822 }
823
824 if ( selectFeature )
825 {
826 QgsFeature derivedFeature = f;
827 addDerivedFields( derivedFeature, tileID.zoomLevel(), layerName );
829 singleSelectCandidates << derivedFeature;
830 else
831 mSelectedFeatures.insert( derivedFeature.id(), derivedFeature );
832 }
833 }
834 }
835 }
836 }
837 break;
838 }
839
842 {
843 // when removing from the selection, we instead just iterate over the current selection and test against the geometry
844 // we do this as we don't want the selection removal operation to depend at all on the tile zoom
845 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); )
846 {
847 bool matchesGeometry = false;
848 if ( selectionGeomPrepared )
849 {
850 switch ( relationship )
851 {
853 matchesGeometry = selectionGeomPrepared->intersects( it->geometry().constGet() );
854 break;
856 matchesGeometry = selectionGeomPrepared->contains( it->geometry().constGet() );
857 break;
858 }
859 }
860 else
861 {
862 switch ( relationship )
863 {
865 matchesGeometry = it->geometry().intersects( r );
866 break;
868 matchesGeometry = r.contains( it->geometry().boundingBox() );
869 break;
870 }
871 }
872
874 {
875 singleSelectCandidates << it.value();
876 it++;
877 }
878 else if ( ( matchesGeometry && behavior == Qgis::SelectBehavior::IntersectSelection ) || ( !matchesGeometry && behavior == Qgis::SelectBehavior::RemoveFromSelection ) )
879 {
880 it++;
881 }
882 else
883 {
884 it = mSelectedFeatures.erase( it );
885 }
886 }
887 break;
888 }
889 }
890
891 if ( ( flags & Qgis::SelectionFlag::SingleFeatureSelection ) && !singleSelectCandidates.empty() )
892 {
893 QgsFeature bestCandidate;
894
896 {
897 // 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
898
899 // find smallest feature in the current selection
900 double smallestArea = std::numeric_limits< double >::max();
901 double smallestLength = std::numeric_limits< double >::max();
902 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
903 {
904 if ( !mSelectedFeatures.contains( candidate.id() ) )
905 continue;
906
907 switch ( candidate.geometry().type() )
908 {
910 bestCandidate = candidate;
911 break;
913 {
914 const double length = candidate.geometry().length();
915 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
916 {
917 bestCandidate = candidate;
918 smallestLength = length;
919 }
920 break;
921 }
923 {
924 const double area = candidate.geometry().area();
925 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
926 {
927 bestCandidate = candidate;
928 smallestArea = area;
929 }
930 break;
931 }
934 break;
935 }
936 }
937 }
938
939 if ( !bestCandidate.isValid() )
940 {
941 // find smallest feature (ie. pick the "hardest" one to click on)
942 double smallestArea = std::numeric_limits< double >::max();
943 double smallestLength = std::numeric_limits< double >::max();
944 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
945 {
946 switch ( candidate.geometry().type() )
947 {
949 bestCandidate = candidate;
950 break;
952 {
953 const double length = candidate.geometry().length();
954 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
955 {
956 bestCandidate = candidate;
957 smallestLength = length;
958 }
959 break;
960 }
962 {
963 const double area = candidate.geometry().area();
964 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
965 {
966 bestCandidate = candidate;
967 smallestArea = area;
968 }
969 break;
970 }
973 break;
974 }
975 }
976 }
977
979 {
980 if ( prevSelection.contains( bestCandidate.id() ) )
981 mSelectedFeatures.remove( bestCandidate.id() );
982 else
983 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
984 }
985 else
986 {
987 switch ( behavior )
988 {
991 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
992 break;
993
995 {
996 if ( mSelectedFeatures.contains( bestCandidate.id() ) )
997 {
998 mSelectedFeatures.clear();
999 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
1000 }
1001 else
1002 {
1003 mSelectedFeatures.clear();
1004 }
1005 break;
1006 }
1007
1009 {
1010 mSelectedFeatures.remove( bestCandidate.id() );
1011 break;
1012 }
1013 }
1014 }
1015 }
1016
1017 QSet< QgsFeatureId > newSelection;
1018 newSelection.reserve( mSelectedFeatures.size() );
1019 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
1020 newSelection.insert( it.key() );
1021
1022 // signal
1023 if ( prevSelection != newSelection )
1024 emit selectionChanged();
1025}
1026
1028{
1030
1031 if ( mSelectedFeatures.empty() )
1032 return;
1033
1034 mSelectedFeatures.clear();
1035 emit selectionChanged();
1036}
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:62
@ IsBasemapLayer
Layer is considered a 'basemap' layer, and certain properties of the layer should be ignored when cal...
Definition qgis.h:2467
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata().
Definition qgis.h:6409
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
Definition qgis.h:6391
QFlags< SelectionFlag > SelectionFlags
Flags which control feature selection behavior.
Definition qgis.h:1977
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5485
@ Point
Points.
Definition qgis.h:400
@ Line
Lines.
Definition qgis.h:401
@ Polygon
Polygons.
Definition qgis.h:402
@ Unknown
Unknown types.
Definition qgis.h:403
@ Null
No geometry.
Definition qgis.h:404
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:532
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Millimeters
Millimeters.
Definition qgis.h:5759
@ ToggleSelection
Enables a "toggle" selection mode, where previously selected matching features will be deselected and...
Definition qgis.h:1969
@ SingleFeatureSelection
Select only a single feature, picking the "best" match for the selection geometry.
Definition qgis.h:1968
QFlags< MapLayerProperty > MapLayerProperties
Map layer properties.
Definition qgis.h:2471
SelectGeometryRelationship
Geometry relationship test to apply for selecting features.
Definition qgis.h:1955
@ Within
Select where features are within the reference geometry.
Definition qgis.h:1957
@ Intersect
Select where features intersect the reference geometry.
Definition qgis.h:1956
SelectBehavior
Specifies how a selection should be applied.
Definition qgis.h:1941
@ SetSelection
Set selection, removing any existing selection.
Definition qgis.h:1942
@ AddToSelection
Add selection to current selection.
Definition qgis.h:1943
@ IntersectSelection
Modify current selection to include only select features which match.
Definition qgis.h:1944
@ RemoveFromSelection
Remove from current selection.
Definition qgis.h:1945
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:64
QgsFields fields
Definition qgsfeature.h:65
QgsFeatureId id
Definition qgsfeature.h:63
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:66
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:45
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
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
bool isTopologicallyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::GEOS) const
Compares the geometry with another geometry using the specified backend.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
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.
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.
std::unique_ptr< QgsVectorTileLabeling > labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
QString errorMessage() const
Returns a descriptive error message if an error was encountered during the style conversion,...
std::unique_ptr< QgsVectorTileRenderer > renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
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.
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
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:440
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition qgstiles.cpp:174
int scaleToZoomLevel(double scale, bool clamp=true) const
Finds the best fitting (integer) zoom level given a map scale denominator.
Definition qgstiles.cpp:301
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition qgstiles.h:171
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition qgstiles.cpp:112
A range of tiles in a tile matrix.
Definition qgstiles.h:123
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.
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:80
#define QgsDebugError(str)
Definition qgslogger.h:71
#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.