QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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#include "moc_qgsvectortilelayer.cpp"
18
19#include "qgslogger.h"
24#include "qgsvectortileloader.h"
25#include "qgsvectortileutils.h"
27#include "qgsdatasourceuri.h"
31#include "qgsjsonutils.h"
32#include "qgspainting.h"
33#include "qgsmaplayerfactory.h"
34#include "qgsselectioncontext.h"
35#include "qgsgeometryengine.h"
37#include "qgsthreadingutils.h"
38#include "qgsproviderregistry.h"
40
41#include <QUrl>
42#include <QUrlQuery>
43
44QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName, const LayerOptions &options )
45 : QgsMapLayer( Qgis::LayerType::VectorTile, baseName )
46 , mTransformContext( options.transformContext )
47{
48 mDataSource = uri;
49
50 if ( !uri.isEmpty() )
51 setValid( loadDataSource() );
52
53 // set a default renderer
57
58 connect( this, &QgsVectorTileLayer::selectionChanged, this, [this] { triggerRepaint(); } );
59}
60
61void QgsVectorTileLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &, const QgsDataProvider::ProviderOptions &, Qgis::DataProviderReadFlags )
62{
64
65 mDataSource = dataSource;
66 mLayerName = baseName;
67 mDataProvider.reset();
68
69 setValid( loadDataSource() );
70}
71
72bool QgsVectorTileLayer::loadDataSource()
73{
75
76 QgsDataSourceUri dsUri;
78
79 setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
80
81 const QgsDataProvider::ProviderOptions providerOptions { mTransformContext };
83
84 mSourceType = dsUri.param( QStringLiteral( "type" ) );
85 QString providerKey;
86 if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
87 {
88 providerKey = QStringLiteral( "arcgisvectortileservice" );
89 }
90 else if ( mSourceType == QLatin1String( "xyz" ) )
91 {
92 providerKey = QStringLiteral( "xyzvectortiles" );
93 }
94 else if ( mSourceType == QLatin1String( "mbtiles" ) )
95 {
96 providerKey = QStringLiteral( "mbtilesvectortiles" );
97 }
98 else if ( mSourceType == QLatin1String( "vtpk" ) )
99 {
100 providerKey = QStringLiteral( "vtpkvectortiles" );
101 }
102 else
103 {
104 QgsDebugError( QStringLiteral( "Unknown source type: " ) + mSourceType );
105 return false;
106 }
107
108 mDataProvider.reset( qobject_cast<QgsVectorTileDataProvider *>( QgsProviderRegistry::instance()->createProvider( providerKey, mDataSource, providerOptions, flags ) ) );
109 mProviderKey = mDataProvider->name();
110
111 if ( mDataProvider )
112 {
113 mMatrixSet = qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->tileMatrixSet();
114 setCrs( mDataProvider->crs() );
115 setExtent( mDataProvider->extent() );
116 }
117
118 return mDataProvider && mDataProvider->isValid();
119}
120
122
124{
126
127 const QgsVectorTileLayer::LayerOptions options( mTransformContext );
128 QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name(), options );
129 layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
130 layer->setLabeling( labeling() ? labeling()->clone() : nullptr );
131 return layer;
132}
133
140
142{
144
145 return mDataProvider.get();
146}
147
154
155bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
156{
158
159 setValid( loadDataSource() );
160
161 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
162 {
163 const QDomElement matrixSetElement = layerNode.firstChildElement( QStringLiteral( "matrixSet" ) );
164 if ( !matrixSetElement.isNull() )
165 {
166 mMatrixSet.readXml( matrixSetElement, context );
167 }
168 }
169 setCrs( mMatrixSet.crs() );
170
171 QString errorMsg;
172 if ( !readSymbology( layerNode, errorMsg, context ) )
173 return false;
174
175 readStyleManager( layerNode );
176 return true;
177}
178
179bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
180{
182
183 QDomElement mapLayerNode = layerNode.toElement();
184 mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( Qgis::LayerType::VectorTile ) );
185
186 if ( !mDataProvider || !( qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerFlags() & Qgis::VectorTileProviderFlag::AlwaysUseTileMatrixSetFromProvider ) )
187 {
188 mapLayerNode.appendChild( mMatrixSet.writeXml( doc, context ) );
189 }
190
191 // add provider node
192 if ( mDataProvider )
193 {
194 QDomElement provider = doc.createElement( QStringLiteral( "provider" ) );
195 const QDomText providerText = doc.createTextNode( providerType() );
196 provider.appendChild( providerText );
197 mapLayerNode.appendChild( provider );
198 }
199
200 writeStyleManager( layerNode, doc );
201
202 QString errorMsg;
203 return writeSymbology( layerNode, doc, errorMsg, context );
204}
205
206bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
207{
209
210 const QDomElement elem = node.toElement();
211
212 readCommonStyle( elem, context, categories );
213
214 const QDomElement elemRenderer = elem.firstChildElement( QStringLiteral( "renderer" ) );
215 if ( elemRenderer.isNull() )
216 {
217 errorMessage = tr( "Missing <renderer> tag" );
218 return false;
219 }
220 const QString rendererType = elemRenderer.attribute( QStringLiteral( "type" ) );
221
222 if ( categories.testFlag( Symbology ) )
223 {
224 QgsVectorTileRenderer *r = nullptr;
225 if ( rendererType == QLatin1String( "basic" ) )
227 else
228 {
229 errorMessage = tr( "Unknown renderer type: " ) + rendererType;
230 return false;
231 }
232
233 r->readXml( elemRenderer, context );
234 setRenderer( r );
235 }
236
237 if ( categories.testFlag( Labeling ) )
238 {
239 setLabeling( nullptr );
240 const QDomElement elemLabeling = elem.firstChildElement( QStringLiteral( "labeling" ) );
241 if ( !elemLabeling.isNull() )
242 {
243 const QString labelingType = elemLabeling.attribute( QStringLiteral( "type" ) );
245 if ( labelingType == QLatin1String( "basic" ) )
247 else
248 {
249 errorMessage = tr( "Unknown labeling type: " ) + rendererType;
250 }
251
252 if ( elemLabeling.hasAttribute( QStringLiteral( "labelsEnabled" ) ) )
253 mLabelsEnabled = elemLabeling.attribute( QStringLiteral( "labelsEnabled" ) ).toInt();
254 else
255 mLabelsEnabled = true;
256
257 if ( labeling )
258 {
259 labeling->readXml( elemLabeling, context );
261 }
262 }
263 }
264
265 if ( categories.testFlag( Symbology ) )
266 {
267 // get and set the blend mode if it exists
268 const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
269 if ( !blendModeNode.isNull() )
270 {
271 const QDomElement e = blendModeNode.toElement();
272 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
273 }
274 }
275
276 // get and set the layer transparency
277 if ( categories.testFlag( Rendering ) )
278 {
279 const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
280 if ( !layerOpacityNode.isNull() )
281 {
282 const QDomElement e = layerOpacityNode.toElement();
283 setOpacity( e.text().toDouble() );
284 }
285 }
286
287 return true;
288}
289
290bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
291{
293
294 Q_UNUSED( errorMessage )
295 QDomElement elem = node.toElement();
296
297 writeCommonStyle( elem, doc, context, categories );
298
299 if ( mRenderer )
300 {
301 QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
302 elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
303 if ( categories.testFlag( Symbology ) )
304 {
305 mRenderer->writeXml( elemRenderer, context );
306 }
307 elem.appendChild( elemRenderer );
308 }
309
310 if ( mLabeling && categories.testFlag( Labeling ) )
311 {
312 QDomElement elemLabeling = doc.createElement( QStringLiteral( "labeling" ) );
313 elemLabeling.setAttribute( QStringLiteral( "type" ), mLabeling->type() );
314 elemLabeling.setAttribute( QStringLiteral( "labelsEnabled" ), mLabelsEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
315 mLabeling->writeXml( elemLabeling, context );
316 elem.appendChild( elemLabeling );
317 }
318
319 if ( categories.testFlag( Symbology ) )
320 {
321 // add the blend mode field
322 QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
323 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
324 blendModeElem.appendChild( blendModeText );
325 node.appendChild( blendModeElem );
326 }
327
328 // add the layer opacity
329 if ( categories.testFlag( Rendering ) )
330 {
331 QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
332 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
333 layerOpacityElem.appendChild( layerOpacityText );
334 node.appendChild( layerOpacityElem );
335 }
336
337 return true;
338}
339
341{
343
344 if ( mDataProvider )
345 mDataProvider->setTransformContext( transformContext );
346
347 mTransformContext = transformContext;
349}
350
351QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
352{
354
355 QString error;
356 QStringList warnings;
357 resultFlag = loadDefaultStyle( error, warnings );
358 return error;
359}
360
362{
364
366 if ( mSourceType == QLatin1String( "xyz" ) )
367 {
368 // always consider xyz vector tiles as basemap layers
370 }
371 else
372 {
373 // TODO when should we consider mbtiles layers as basemap layers? potentially if their extent is "large"?
374 }
375
376 return res;
377}
378
379bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
380{
382
383 return loadDefaultStyleAndSubLayersPrivate( error, warnings, nullptr );
384}
385
386bool QgsVectorTileLayer::loadDefaultStyleAndSubLayers( QString &error, QStringList &warnings, QList<QgsMapLayer *> &subLayers )
387{
389
390 return loadDefaultStyleAndSubLayersPrivate( error, warnings, &subLayers );
391}
392
393bool QgsVectorTileLayer::loadDefaultStyleAndSubLayersPrivate( QString &error, QStringList &warnings, QList<QgsMapLayer *> *subLayers )
394{
396 QgsVectorTileDataProvider *vtProvider = qgis::down_cast< QgsVectorTileDataProvider *> ( mDataProvider.get() );
397 if ( !vtProvider )
398 return false;
399
400 QgsDataSourceUri dsUri;
401 dsUri.setEncodedUri( mDataSource );
402
403 QVariantMap styleDefinition;
405 QString styleUrl;
406 if ( !dsUri.param( QStringLiteral( "styleUrl" ) ).isEmpty() )
407 {
408 styleUrl = dsUri.param( QStringLiteral( "styleUrl" ) );
409 }
410 else
411 {
412 styleUrl = vtProvider->styleUrl();
413 }
414
415 styleDefinition = vtProvider->styleDefinition();
416 const QVariantMap spriteDefinition = vtProvider->spriteDefinition();
417 if ( !spriteDefinition.isEmpty() )
418 {
419 const QImage spriteImage = vtProvider->spriteImage();
420 context.setSprites( spriteImage, spriteDefinition );
421 }
422
423 if ( !styleDefinition.isEmpty() || !styleUrl.isEmpty() )
424 {
425 if ( styleDefinition.isEmpty() )
426 {
427 QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
428
429 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );
430
431 QgsBlockingNetworkRequest networkRequest;
432 switch ( networkRequest.get( request ) )
433 {
435 break;
436
440 error = QObject::tr( "Error retrieving default style" );
441 return false;
442 }
443
444 const QgsNetworkReplyContent content = networkRequest.reply();
445 styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
446 }
447
448 QgsVectorTileUtils::loadSprites( styleDefinition, context, styleUrl );
449 }
450
451 if ( !styleDefinition.isEmpty() )
452 {
453 // convert automatically from pixel sizes to millimeters, because pixel sizes
454 // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
456 //assume source uses 96 dpi
457 context.setPixelSizeConversionFactor( 25.4 / 96.0 );
458
460 if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
461 {
462 warnings = converter.warnings();
463 error = converter.errorMessage();
464 return false;
465 }
466
467 setRenderer( converter.renderer() );
468 setLabeling( converter.labeling() );
469 warnings = converter.warnings();
470
471 if ( subLayers )
472 {
473 *subLayers = converter.createSubLayers();
474 }
475
476 return true;
477 }
478 else
479 {
480 bool resultFlag = false;
481 error = QgsMapLayer::loadDefaultStyle( resultFlag );
482 return resultFlag;
483 }
484}
485
487{
489
490 resultFlag = false;
491 if ( !mDataProvider || !mDataProvider->isValid() )
492 return QString();
493
494 if ( qgis::down_cast< QgsVectorTileDataProvider * >( mDataProvider.get() )->providerCapabilities() & Qgis::VectorTileProviderCapability::ReadLayerMetadata )
495 {
496 setMetadata( mDataProvider->layerMetadata() );
497 }
498 else
499 {
501 }
502 resultFlag = true;
503 return QString();
504}
505
506QString QgsVectorTileLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
507{
509
511}
512
513QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
514{
516
517 return QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, source, context );
518}
519
521{
523
524 const QgsLayerMetadataFormatter htmlFormatter( metadata() );
525
526 QString info = QStringLiteral( "<html><head></head>\n<body>\n" );
527
528 info += generalHtmlMetadata();
529
530 info += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
531 QStringLiteral( "<table class=\"list-view\">\n" );
532
533 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source type" ) % QStringLiteral( "</td><td>" ) % sourceType() % QStringLiteral( "</td></tr>\n" );
534
535 info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Zoom levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % QStringLiteral( "</td></tr>\n" );
536
537 if ( mDataProvider )
538 info += qobject_cast< const QgsVectorTileDataProvider * >( mDataProvider.get() )->htmlMetadata();
539
540 info += QLatin1String( "</table>\n<br>" );
541
542 // CRS
543 info += crsHtmlMetadata();
544
545 // Identification section
546 info += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
547 htmlFormatter.identificationSectionHtml() %
548 QStringLiteral( "<br>\n" ) %
549
550 // extent section
551 QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
552 htmlFormatter.extentSectionHtml( ) %
553 QStringLiteral( "<br>\n" ) %
554
555 // Start the Access section
556 QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
557 htmlFormatter.accessSectionHtml( ) %
558 QStringLiteral( "<br>\n" ) %
559
560
561 // Start the contacts section
562 QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
563 htmlFormatter.contactsSectionHtml( ) %
564 QStringLiteral( "<br><br>\n" ) %
565
566 // Start the links section
567 QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
568 htmlFormatter.linksSectionHtml( ) %
569 QStringLiteral( "<br>\n" ) %
570
571 // Start the history section
572 QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
573 htmlFormatter.historySectionHtml( ) %
574 QStringLiteral( "<br>\n" ) %
575
577
578 QStringLiteral( "\n</body>\n</html>\n" );
579
580 return info;
581}
582
584{
585 if ( QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() ) )
586 return vtProvider->sourcePath();
587
588 return QString();
589}
590
592{
594
595 QgsVectorTileDataProvider *vtProvider = qobject_cast< QgsVectorTileDataProvider * >( mDataProvider.get() );
596 if ( !vtProvider )
597 return QgsVectorTileRawData();
598
599 return vtProvider->readTile( mMatrixSet, tileID );
600}
601
609
611{
613
614 return mRenderer.get();
615}
616
624
626{
628
629 return mLabeling.get();
630}
631
633{
634 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
636
637 return mLabelsEnabled && static_cast< bool >( mLabeling );
638}
639
641{
643
644 mLabelsEnabled = enabled;
645}
646
648{
650
651 QList< QgsFeature > res;
652 res.reserve( mSelectedFeatures.size() );
653 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
654 res.append( it.value() );
655
656 return res;
657}
658
660{
662
663 return mSelectedFeatures.size();
664}
665
667{
669
670 if ( !isInScaleRange( context.scale() ) )
671 {
672 QgsDebugMsgLevel( QStringLiteral( "Out of scale limits" ), 2 );
673 return;
674 }
675
676 QSet< QgsFeatureId > prevSelection;
677 prevSelection.reserve( mSelectedFeatures.size() );
678 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
679 prevSelection.insert( it.key() );
680
682 {
683 switch ( behavior )
684 {
686 mSelectedFeatures.clear();
687 break;
688
692 break;
693 }
694 }
695
696 QgsGeometry selectionGeom = geometry;
697 bool isPointOrRectangle;
698 QgsPointXY point;
699 bool isSinglePoint = selectionGeom.type() == Qgis::GeometryType::Point;
700 if ( isSinglePoint )
701 {
702 isPointOrRectangle = true;
703 point = selectionGeom.asPoint();
705 }
706 else
707 {
708 // we have a polygon - maybe it is a rectangle - in such case we can avoid costly instersection tests later
709 isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
710 }
711
712 auto addDerivedFields = []( QgsFeature & feature, const int tileZoom, const QString & layer )
713 {
714 QgsFields fields = feature.fields();
715 fields.append( QgsField( QStringLiteral( "tile_zoom" ), QMetaType::Type::Int ) );
716 fields.append( QgsField( QStringLiteral( "tile_layer" ), QMetaType::Type::QString ) );
717 QgsAttributes attributes = feature.attributes();
718 attributes << tileZoom << layer;
719 feature.setFields( fields );
720 feature.setAttributes( attributes );
721 };
722
723 std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
724 QList< QgsFeature > singleSelectCandidates;
725
726 QgsRectangle r;
727 if ( isSinglePoint )
728 {
729 r = QgsRectangle( point.x(), point.y(), point.x(), point.y() );
730 }
731 else
732 {
733 r = selectionGeom.boundingBox();
734
735 if ( !isPointOrRectangle || relationship == Qgis::SelectGeometryRelationship::Within )
736 {
737 // use prepared geometry for faster intersection test
738 selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
739 }
740 }
741
742 switch ( behavior )
743 {
746 {
747 // when adding to or setting a selection, we retrieve the tile data for the current scale
748 const int tileZoom = tileMatrixSet().scaleToZoomLevel( context.scale() );
749 const QgsTileMatrix tileMatrix = tileMatrixSet().tileMatrix( tileZoom );
750 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
751 const QVector< QgsTileXYZ> tiles = tileMatrixSet().tilesInRange( tileRange, tileZoom );
752
753 for ( const QgsTileXYZ &tileID : tiles )
754 {
755 const QgsVectorTileRawData data = getRawTile( tileID );
756 if ( data.data.isEmpty() )
757 continue; // failed to get data
758
760 if ( !decoder.decode( data ) )
761 continue; // failed to decode
762
763 QMap<QString, QgsFields> perLayerFields;
764 const QStringList layerNames = decoder.layers();
765 for ( const QString &layerName : layerNames )
766 {
767 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
768 perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
769 }
770
771 const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
772 const QStringList featuresLayerNames = features.keys();
773 for ( const QString &layerName : featuresLayerNames )
774 {
775 const QgsFields fFields = perLayerFields[layerName];
776 const QVector<QgsFeature> &layerFeatures = features[layerName];
777 for ( const QgsFeature &f : layerFeatures )
778 {
779 if ( renderContext && mRenderer && !mRenderer->willRenderFeature( f, tileID.zoomLevel(), layerName, *renderContext ) )
780 continue;
781
782 if ( f.geometry().intersects( r ) )
783 {
784 bool selectFeature = true;
785 if ( selectionGeomPrepared )
786 {
787 switch ( relationship )
788 {
790 selectFeature = selectionGeomPrepared->intersects( f.geometry().constGet() );
791 break;
793 selectFeature = selectionGeomPrepared->contains( f.geometry().constGet() );
794 break;
795 }
796 }
797
798 if ( selectFeature )
799 {
800 QgsFeature derivedFeature = f;
801 addDerivedFields( derivedFeature, tileID.zoomLevel(), layerName );
803 singleSelectCandidates << derivedFeature;
804 else
805 mSelectedFeatures.insert( derivedFeature.id(), derivedFeature );
806 }
807 }
808 }
809 }
810 }
811 break;
812 }
813
816 {
817 // when removing from the selection, we instead just iterate over the current selection and test against the geometry
818 // we do this as we don't want the selection removal operation to depend at all on the tile zoom
819 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); )
820 {
821 bool matchesGeometry = false;
822 if ( selectionGeomPrepared )
823 {
824 switch ( relationship )
825 {
827 matchesGeometry = selectionGeomPrepared->intersects( it->geometry().constGet() );
828 break;
830 matchesGeometry = selectionGeomPrepared->contains( it->geometry().constGet() );
831 break;
832 }
833 }
834 else
835 {
836 switch ( relationship )
837 {
839 matchesGeometry = it->geometry().intersects( r );
840 break;
842 matchesGeometry = r.contains( it->geometry().boundingBox() );
843 break;
844 }
845 }
846
848 {
849 singleSelectCandidates << it.value();
850 it++;
851 }
852 else if ( ( matchesGeometry && behavior == Qgis::SelectBehavior::IntersectSelection )
853 || ( !matchesGeometry && behavior == Qgis::SelectBehavior::RemoveFromSelection ) )
854 {
855 it++;
856 }
857 else
858 {
859 it = mSelectedFeatures.erase( it );
860 }
861 }
862 break;
863 }
864 }
865
866 if ( ( flags & Qgis::SelectionFlag::SingleFeatureSelection ) && !singleSelectCandidates.empty() )
867 {
868 QgsFeature bestCandidate;
869
871 {
872 // 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
873
874 // find smallest feature in the current selection
875 double smallestArea = std::numeric_limits< double >::max();
876 double smallestLength = std::numeric_limits< double >::max();
877 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
878 {
879 if ( !mSelectedFeatures.contains( candidate.id() ) )
880 continue;
881
882 switch ( candidate.geometry().type() )
883 {
885 bestCandidate = candidate;
886 break;
888 {
889 const double length = candidate.geometry().length();
890 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
891 {
892 bestCandidate = candidate;
893 smallestLength = length;
894 }
895 break;
896 }
898 {
899 const double area = candidate.geometry().area();
900 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
901 {
902 bestCandidate = candidate;
903 smallestArea = area;
904 }
905 break;
906 }
909 break;
910 }
911 }
912 }
913
914 if ( !bestCandidate.isValid() )
915 {
916 // find smallest feature (ie. pick the "hardest" one to click on)
917 double smallestArea = std::numeric_limits< double >::max();
918 double smallestLength = std::numeric_limits< double >::max();
919 for ( const QgsFeature &candidate : std::as_const( singleSelectCandidates ) )
920 {
921 switch ( candidate.geometry().type() )
922 {
924 bestCandidate = candidate;
925 break;
927 {
928 const double length = candidate.geometry().length();
929 if ( length < smallestLength && bestCandidate.geometry().type() != Qgis::GeometryType::Point )
930 {
931 bestCandidate = candidate;
932 smallestLength = length;
933 }
934 break;
935 }
937 {
938 const double area = candidate.geometry().area();
939 if ( area < smallestArea && bestCandidate.geometry().type() != Qgis::GeometryType::Point && bestCandidate.geometry().type() != Qgis::GeometryType::Line )
940 {
941 bestCandidate = candidate;
942 smallestArea = area;
943 }
944 break;
945 }
948 break;
949 }
950 }
951 }
952
954 {
955 if ( prevSelection.contains( bestCandidate.id() ) )
956 mSelectedFeatures.remove( bestCandidate.id() );
957 else
958 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
959 }
960 else
961 {
962 switch ( behavior )
963 {
966 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
967 break;
968
970 {
971 if ( mSelectedFeatures.contains( bestCandidate.id() ) )
972 {
973 mSelectedFeatures.clear();
974 mSelectedFeatures.insert( bestCandidate.id(), bestCandidate );
975 }
976 else
977 {
978 mSelectedFeatures.clear();
979 }
980 break;
981 }
982
984 {
985 mSelectedFeatures.remove( bestCandidate.id() );
986 break;
987 }
988 }
989 }
990 }
991
992 QSet< QgsFeatureId > newSelection;
993 newSelection.reserve( mSelectedFeatures.size() );
994 for ( auto it = mSelectedFeatures.begin(); it != mSelectedFeatures.end(); ++it )
995 newSelection.insert( it.key() );
996
997 // signal
998 if ( prevSelection != newSelection )
999 emit selectionChanged();
1000}
1001
1003{
1005
1006 if ( mSelectedFeatures.empty() )
1007 return;
1008
1009 mSelectedFeatures.clear();
1010 emit selectionChanged();
1011}
1012
1013
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
@ IsBasemapLayer
Layer is considered a 'basemap' layer, and certain properties of the layer should be ignored when cal...
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata()
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
QFlags< SelectionFlag > SelectionFlags
Flags which control feature selection behavior.
Definition qgis.h:1654
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:4594
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:450
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Millimeters
Millimeters.
@ ToggleSelection
Enables a "toggle" selection mode, where previously selected matching features will be deselected and...
@ SingleFeatureSelection
Select only a single feature, picking the "best" match for the selection geometry.
QFlags< MapLayerProperty > MapLayerProperties
Map layer properties.
Definition qgis.h:2118
SelectGeometryRelationship
Geometry relationship test to apply for selecting features.
Definition qgis.h:1632
@ Within
Select where features are within the reference geometry.
@ Intersect
Select where features intersect the reference geometry.
SelectBehavior
Specifies how a selection should be applied.
Definition qgis.h:1618
@ SetSelection
Set selection, removing any existing selection.
@ AddToSelection
Add selection to current selection.
@ IntersectSelection
Modify current selection to include only select features which match.
@ RemoveFromSelection
Remove from current selection.
A vector of attributes.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, 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...
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Abstract base class for spatial data provider implementations.
Class for storing the component parts of a RDBMS data source URI (e.g.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h: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:53
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:70
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double length() const
Returns the planar, 2-dimensional length of geometry.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
Qgis::GeometryType type
double area() const
Returns the planar, 2-dimensional area of the geometry.
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 ...
Class for metadata formatter.
Context for a MapBox GL style conversion operation.
void setTargetUnit(Qgis::RenderUnit targetUnit)
Sets the target unit type.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
void setSprites(const QImage &image, const QVariantMap &definitions)
Sets the sprite image and definitions JSON to use during conversion.
Handles conversion of MapBox GL styles to QGIS vector tile renderers and labeling settings.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
QgsVectorTileLabeling * labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
QList< QgsMapLayer * > createSubLayers() const
Returns a list of new map layers corresponding to sublayers of the style, e.g.
@ Success
Conversion was successful.
QString errorMessage() const
Returns a descriptive error message if an error was encountered during the style conversion,...
QStringList warnings() const
Returns a list of user-friendly warnings generated during the conversion, e.g.
static QString typeToString(Qgis::LayerType type)
Converts a map layer type to a string value.
Base class for utility classes that encapsulate information necessary for rendering of map layers.
Base class for all map layer types.
Definition qgsmaplayer.h:76
QString name
Definition qgsmaplayer.h:80
void readStyleManager(const QDomNode &layerNode)
Read style manager's configuration (if any). To be called by subclasses.
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
void writeStyleManager(QDomNode &layerNode, QDomDocument &doc) const
Write style manager's configuration (if exists). To be called by subclasses.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
virtual QString loadDefaultStyle(bool &resultFlag)
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
QString mLayerName
Name of the layer - used for display.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
QString crsHtmlMetadata() const
Returns a HTML fragment containing the layer's CRS metadata, for use in the htmlMetadata() method.
QgsLayerMetadata metadata
Definition qgsmaplayer.h:82
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.
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:88
@ 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.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
static Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a Qgis::BlendMode corresponding to a QPainter::CompositionMode.
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a Qgis::BlendMode.
A class to represent a 2D point.
Definition qgspointxy.h: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.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
Contains information about the context of a rendering operation.
Encapsulates the context of a layer selection operation.
double scale() const
Returns the map scale at which the selection should occur.
virtual QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Writes the set to an XML element.
Definition qgstiles.cpp:391
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system associated with the tiles.
Definition qgstiles.cpp:218
QVector< QgsTileXYZ > tilesInRange(QgsTileRange range, int zoomLevel) const
Returns a list of tiles in the given tile range.
Definition qgstiles.cpp:428
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition qgstiles.cpp:156
virtual bool readXml(const QDomElement &element, QgsReadWriteContext &context)
Reads the set from an XML element.
Definition qgstiles.cpp:342
int scaleToZoomLevel(double scale, bool clamp=true) const
Finds the best fitting (integer) zoom level given a map scale denominator.
Definition qgstiles.cpp:283
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition qgstiles.h:136
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition qgstiles.cpp:97
Range of tiles in a tile matrix to be rendered.
Definition qgstiles.h:99
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:40
Basic labeling configuration for vector tile layers.
The default vector tile renderer implementation.
static QList< QgsVectorTileBasicRendererStyle > simpleStyleWithRandomColors()
Returns a list of styles to render all layers, using random colors.
Base class for vector tile layer data providers.
Base class for labeling configuration classes for vector tile layers.
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)=0
Reads labeling properties from given XML element.
virtual QgsVectorTileLabeling * clone() const =0SIP_FACTORY
Returns a new copy of the object.
This class provides map rendering functionality for vector tile layers.
Implements a map layer that is dedicated to rendering of vector tiles.
QgsVectorTileLayer(const QString &path=QString(), const QString &baseName=QString(), const QgsVectorTileLayer::LayerOptions &options=QgsVectorTileLayer::LayerOptions())
Constructs a new vector tile layer.
bool readXml(const QDomNode &layerNode, QgsReadWriteContext &context) override
Called by readLayerXML(), used by children to read state specific to them from project files.
bool writeSymbology(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const override
Write the style for the layer into the document provided.
void setRenderer(QgsVectorTileRenderer *r)
Sets renderer for the map layer.
bool loadDefaultStyleAndSubLayers(QString &error, QStringList &warnings, QList< QgsMapLayer * > &subLayers)
Loads the default style for the layer, and returns true if the style was successfully loaded.
QString decodedSource(const QString &source, const QString &provider, const QgsReadWriteContext &context) const FINAL
Called by readLayerXML(), used by derived classes to decode provider's specific data source from proj...
QString sourceType() const
Returns type of the data source.
void setLabelsEnabled(bool enabled)
Sets whether labels should be enabled for the layer.
bool writeXml(QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context) const override
Called by writeLayerXML(), used by children to write state specific to them to project files.
void selectByGeometry(const QgsGeometry &geometry, const QgsSelectionContext &context, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection, Qgis::SelectGeometryRelationship relationship=Qgis::SelectGeometryRelationship::Intersect, Qgis::SelectionFlags flags=Qgis::SelectionFlags(), QgsRenderContext *renderContext=nullptr)
Selects features found within the search geometry (in layer's coordinates).
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
void setLabeling(QgsVectorTileLabeling *labeling)
Sets labeling for the map layer.
QList< QgsFeature > selectedFeatures() const
Returns the list of features currently selected in the layer.
QgsVectorTileLabeling * labeling() const
Returns currently assigned labeling.
~QgsVectorTileLayer() override
bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) override
Read the symbology for the current layer from the DOM node supplied.
int sourceMinZoom() const
Returns minimum zoom level at which source has any valid tiles (negative = unconstrained)
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) override
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
QString loadDefaultStyle(bool &resultFlag) override
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
Qgis::MapLayerProperties properties() const override
Returns the map layer properties of this layer.
QString htmlMetadata() const override
Obtain a formatted HTML string containing assorted metadata for this layer.
QString encodedSource(const QString &source, const QgsReadWriteContext &context) const FINAL
Called by writeLayerXML(), used by derived classes to encode provider's specific data source to proje...
int sourceMaxZoom() const
Returns maximum zoom level at which source has any valid tiles (negative = unconstrained)
QgsVectorTileRenderer * renderer() const
Returns currently assigned renderer.
void removeSelection()
Clear selection.
QgsDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
QString loadDefaultMetadata(bool &resultFlag) override
Retrieve the default metadata for this layer if one exists (either as a .qmd file on disk or as a rec...
QString sourcePath() const
Returns URL/path of the data source (syntax different to each data source type)
QgsVectorTileRawData getRawTile(QgsTileXYZ tileID)
Fetches raw tile data for the give tile coordinates.
void setTransformContext(const QgsCoordinateTransformContext &transformContext) override
Sets the coordinate transform context to transformContext.
QgsVectorTileMatrixSet & tileMatrixSet()
Returns the vector tile matrix set.
void selectionChanged()
Emitted whenever the selected features in the layer are changed.
QgsVectorTileLayer * clone() const override
Returns a new instance equivalent to this one except for the id which is still unique.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
This class is responsible for decoding raw tile data written with Mapbox Vector Tiles encoding.
QStringList layerFieldNames(const QString &layerName) const
Returns a list of all field names in a tile. It can only be called after a successful decode()
QStringList layers() const
Returns a list of sub-layer names in a tile. It can only be called after a successful decode()
bool decode(const QgsVectorTileRawData &rawTileData)
Tries to decode raw tile data, returns true on success.
QgsVectorTileFeatures layerFeatures(const QMap< QString, QgsFields > &perLayerFields, const QgsCoordinateTransform &ct, const QSet< QString > *layerSubset=nullptr) const
Returns decoded features grouped by sub-layers.
Keeps track of raw tile data 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:39
#define QgsDebugError(str)
Definition qgslogger.h:38
#define QgsSetRequestInitiatorClass(request, _class)
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QMap< QString, QVector< QgsFeature > > QgsVectorTileFeatures
Features of a vector tile, grouped by sub-layer names (key of the map)
Setting options for creating vector data providers.
Setting options for loading vector tile layers.