QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsvectortilelayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectortilelayer.cpp
3  --------------------------------------
4  Date : March 2020
5  Copyright : (C) 2020 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsvectortilelayer.h"
17 
18 #include "qgslogger.h"
20 #include "qgsmbtiles.h"
23 #include "qgsvectortilelabeling.h"
24 #include "qgsvectortileloader.h"
25 #include "qgsvectortileutils.h"
27 
28 #include "qgsdatasourceuri.h"
32 #include "qgsjsonutils.h"
33 #include "qgspainting.h"
34 #include "qgsmaplayerfactory.h"
35 #include "qgsarcgisrestutils.h"
36 
37 #include <QUrl>
38 #include <QUrlQuery>
39 
40 QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName, const LayerOptions &options )
42  , mTransformContext( options.transformContext )
43 {
45 
46  mDataSource = uri;
47 
48  setValid( loadDataSource() );
49 
50  // set a default renderer
54 }
55 
56 void QgsVectorTileLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &, const QgsDataProvider::ProviderOptions &, QgsDataProvider::ReadFlags )
57 {
58  mDataSource = dataSource;
59  mLayerName = baseName;
60  mDataProvider.reset();
61 
62  setValid( loadDataSource() );
63 }
64 
65 bool QgsVectorTileLayer::loadDataSource()
66 {
67  QgsDataSourceUri dsUri;
68  dsUri.setEncodedUri( mDataSource );
69 
70  setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
71 
72  mSourceType = dsUri.param( QStringLiteral( "type" ) );
73  mSourcePath = dsUri.param( QStringLiteral( "url" ) );
74  if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
75  {
76  if ( !setupArcgisVectorTileServiceConnection( mSourcePath, dsUri ) )
77  return false;
78  }
79  else if ( mSourceType == QLatin1String( "xyz" ) )
80  {
81  if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
82  {
83  QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
84  return false;
85  }
86 
87  // online tiles
88  int zMin = 0;
89  if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
90  zMin = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
91 
92  int zMax = 14;
93  if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
94  zMax = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
95 
96  mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
97  setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );
98  }
99  else if ( mSourceType == QLatin1String( "mbtiles" ) )
100  {
101  QgsMbTiles reader( mSourcePath );
102  if ( !reader.open() )
103  {
104  QgsDebugMsg( QStringLiteral( "failed to open MBTiles file: " ) + mSourcePath );
105  return false;
106  }
107 
108  const QString format = reader.metadataValue( QStringLiteral( "format" ) );
109  if ( format != QLatin1String( "pbf" ) )
110  {
111  QgsDebugMsg( QStringLiteral( "Cannot open MBTiles for vector tiles. Format = " ) + format );
112  return false;
113  }
114 
115  QgsDebugMsgLevel( QStringLiteral( "name: " ) + reader.metadataValue( QStringLiteral( "name" ) ), 2 );
116  bool minZoomOk, maxZoomOk;
117  const int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk );
118  const int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk );
119  if ( minZoomOk )
120  mMatrixSet.dropMatricesOutsideZoomRange( minZoom, 99 );
121  if ( maxZoomOk )
122  mMatrixSet.dropMatricesOutsideZoomRange( 0, maxZoom );
123  QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mMatrixSet.minimumZoom() ).arg( mMatrixSet.maximumZoom() ), 2 );
124 
125  QgsRectangle r = reader.extent();
126  QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ),
127  QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), transformContext() );
128  ct.setBallparkTransformsAreAppropriate( true );
129  r = ct.transformBoundingBox( r );
130  setExtent( r );
131  }
132  else
133  {
134  QgsDebugMsg( QStringLiteral( "Unknown source type: " ) + mSourceType );
135  return false;
136  }
137 
138  const QgsDataProvider::ProviderOptions providerOptions { mTransformContext };
139  const QgsDataProvider::ReadFlags flags;
140  mDataProvider.reset( new QgsVectorTileDataProvider( providerOptions, flags ) );
141  mProviderKey = mDataProvider->name();
142 
143  return true;
144 }
145 
146 bool QgsVectorTileLayer::setupArcgisVectorTileServiceConnection( const QString &uri, const QgsDataSourceUri &dataSourceUri )
147 {
148  QUrl url( uri );
149  // some services don't default to json format, while others do... so let's explicitly request it!
150  // (refs https://github.com/qgis/QGIS/issues/4231)
151  QUrlQuery query;
152  query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
153  url.setQuery( query );
154 
155  QNetworkRequest request = QNetworkRequest( url );
156 
157  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
158 
159  QgsBlockingNetworkRequest networkRequest;
160  switch ( networkRequest.get( request ) )
161  {
163  break;
164 
168  return false;
169  }
170 
171  const QgsNetworkReplyContent content = networkRequest.reply();
172  const QByteArray raw = content.content();
173 
174  // Parse data
175  QJsonParseError err;
176  const QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
177  if ( doc.isNull() )
178  {
179  return false;
180  }
181  mArcgisLayerConfiguration = doc.object().toVariantMap();
182  if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
183  {
184  return false;
185  }
186 
187  mArcgisLayerConfiguration.insert( QStringLiteral( "serviceUri" ), uri );
188  mSourcePath = uri + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).toList().value( 0 ).toString();
189  if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
190  {
191  QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
192  return false;
193  }
194 
195  mMatrixSet.fromEsriJson( mArcgisLayerConfiguration );
196  setCrs( mMatrixSet.crs() );
197 
198  // if hardcoded zoom limits aren't specified, take them from the server
199  if ( dataSourceUri.hasParam( QStringLiteral( "zmin" ) ) )
200  mMatrixSet.dropMatricesOutsideZoomRange( dataSourceUri.param( QStringLiteral( "zmin" ) ).toInt(), 99 );
201 
202  if ( dataSourceUri.hasParam( QStringLiteral( "zmax" ) ) )
203  mMatrixSet.dropMatricesOutsideZoomRange( 0, dataSourceUri.param( QStringLiteral( "zmax" ) ).toInt() );
204 
205  const QVariantMap fullExtent = mArcgisLayerConfiguration.value( QStringLiteral( "fullExtent" ) ).toMap();
206  if ( !fullExtent.isEmpty() )
207  {
208  const QgsRectangle fullExtentRect(
209  fullExtent.value( QStringLiteral( "xmin" ) ).toDouble(),
210  fullExtent.value( QStringLiteral( "ymin" ) ).toDouble(),
211  fullExtent.value( QStringLiteral( "xmax" ) ).toDouble(),
212  fullExtent.value( QStringLiteral( "ymax" ) ).toDouble()
213  );
214 
215  const QgsCoordinateReferenceSystem fullExtentCrs = QgsArcGisRestUtils::convertSpatialReference( fullExtent.value( QStringLiteral( "spatialReference" ) ).toMap() );
216  const QgsCoordinateTransform extentTransform( fullExtentCrs, crs(), transformContext() );
217  try
218  {
219  setExtent( extentTransform.transformBoundingBox( fullExtentRect ) );
220  }
221  catch ( QgsCsException & )
222  {
223  QgsDebugMsg( QStringLiteral( "Could not transform layer fullExtent to layer CRS" ) );
224  }
225  }
226  else
227  {
228  // if no fullExtent specified in JSON, default to web mercator specs full extent
229  const QgsCoordinateTransform extentTransform( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), crs(), transformContext() );
230  try
231  {
232  setExtent( extentTransform.transformBoundingBox( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) ) );
233  }
234  catch ( QgsCsException & )
235  {
236  QgsDebugMsg( QStringLiteral( "Could not transform layer extent to layer CRS" ) );
237  }
238  }
239 
240  return true;
241 }
242 
244 
245 
247 {
248  const QgsVectorTileLayer::LayerOptions options( mTransformContext );
249  QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name(), options );
250  layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
251  return layer;
252 }
253 
255 {
256  return mDataProvider.get();
257 }
258 
260 {
261  return mDataProvider.get();
262 }
263 
265 {
266  return new QgsVectorTileLayerRenderer( this, rendererContext );
267 }
268 
269 bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
270 {
271  setValid( loadDataSource() );
272 
273  const QDomElement matrixSetElement = layerNode.firstChildElement( QStringLiteral( "matrixSet" ) );
274  if ( !matrixSetElement.isNull() )
275  {
276  mMatrixSet.readXml( matrixSetElement, context );
277  setCrs( mMatrixSet.crs() );
278  }
279 
280  QString errorMsg;
281  if ( !readSymbology( layerNode, errorMsg, context ) )
282  return false;
283 
284  readStyleManager( layerNode );
285  return true;
286 }
287 
288 bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
289 {
290  QDomElement mapLayerNode = layerNode.toElement();
291  mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::VectorTileLayer ) );
292 
293  mapLayerNode.appendChild( mMatrixSet.writeXml( doc, context ) );
294 
295  // add provider node
296  if ( mDataProvider )
297  {
298  QDomElement provider = doc.createElement( QStringLiteral( "provider" ) );
299  const QDomText providerText = doc.createTextNode( providerType() );
300  provider.appendChild( providerText );
301  mapLayerNode.appendChild( provider );
302  }
303 
304  writeStyleManager( layerNode, doc );
305 
306  QString errorMsg;
307  return writeSymbology( layerNode, doc, errorMsg, context );
308 }
309 
310 bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
311 {
312  const QDomElement elem = node.toElement();
313 
314  readCommonStyle( elem, context, categories );
315 
316  const QDomElement elemRenderer = elem.firstChildElement( QStringLiteral( "renderer" ) );
317  if ( elemRenderer.isNull() )
318  {
319  errorMessage = tr( "Missing <renderer> tag" );
320  return false;
321  }
322  const QString rendererType = elemRenderer.attribute( QStringLiteral( "type" ) );
323 
324  if ( categories.testFlag( Symbology ) )
325  {
326  QgsVectorTileRenderer *r = nullptr;
327  if ( rendererType == QLatin1String( "basic" ) )
329  else
330  {
331  errorMessage = tr( "Unknown renderer type: " ) + rendererType;
332  return false;
333  }
334 
335  r->readXml( elemRenderer, context );
336  setRenderer( r );
337  }
338 
339  if ( categories.testFlag( Labeling ) )
340  {
341  setLabeling( nullptr );
342  const QDomElement elemLabeling = elem.firstChildElement( QStringLiteral( "labeling" ) );
343  if ( !elemLabeling.isNull() )
344  {
345  const QString labelingType = elemLabeling.attribute( QStringLiteral( "type" ) );
346  QgsVectorTileLabeling *labeling = nullptr;
347  if ( labelingType == QLatin1String( "basic" ) )
349  else
350  {
351  errorMessage = tr( "Unknown labeling type: " ) + rendererType;
352  }
353 
354  if ( labeling )
355  {
356  labeling->readXml( elemLabeling, context );
358  }
359  }
360  }
361 
362  if ( categories.testFlag( Symbology ) )
363  {
364  // get and set the blend mode if it exists
365  const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
366  if ( !blendModeNode.isNull() )
367  {
368  const QDomElement e = blendModeNode.toElement();
369  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
370  }
371  }
372 
373  // get and set the layer transparency
374  if ( categories.testFlag( Rendering ) )
375  {
376  const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
377  if ( !layerOpacityNode.isNull() )
378  {
379  const QDomElement e = layerOpacityNode.toElement();
380  setOpacity( e.text().toDouble() );
381  }
382  }
383 
384  return true;
385 }
386 
387 bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
388 {
389  Q_UNUSED( errorMessage )
390  QDomElement elem = node.toElement();
391 
392  writeCommonStyle( elem, doc, context, categories );
393 
394  if ( mRenderer )
395  {
396  QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
397  elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
398  if ( categories.testFlag( Symbology ) )
399  {
400  mRenderer->writeXml( elemRenderer, context );
401  }
402  elem.appendChild( elemRenderer );
403  }
404 
405  if ( mLabeling && categories.testFlag( Labeling ) )
406  {
407  QDomElement elemLabeling = doc.createElement( QStringLiteral( "labeling" ) );
408  elemLabeling.setAttribute( QStringLiteral( "type" ), mLabeling->type() );
409  mLabeling->writeXml( elemLabeling, context );
410  elem.appendChild( elemLabeling );
411  }
412 
413  if ( categories.testFlag( Symbology ) )
414  {
415  // add the blend mode field
416  QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
417  const QDomText blendModeText = doc.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
418  blendModeElem.appendChild( blendModeText );
419  node.appendChild( blendModeElem );
420  }
421 
422  // add the layer opacity
423  if ( categories.testFlag( Rendering ) )
424  {
425  QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
426  const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
427  layerOpacityElem.appendChild( layerOpacityText );
428  node.appendChild( layerOpacityElem );
429  }
430 
431  return true;
432 }
433 
435 {
436  if ( mDataProvider )
437  mDataProvider->setTransformContext( transformContext );
438 
439  mTransformContext = transformContext;
441 }
442 
443 QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
444 {
445  QString error;
446  QStringList warnings;
447  resultFlag = loadDefaultStyle( error, warnings );
448  return error;
449 }
450 
451 bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
452 {
453  QgsDataSourceUri dsUri;
454  dsUri.setEncodedUri( mDataSource );
455 
456  QString styleUrl;
457  if ( !dsUri.param( QStringLiteral( "styleUrl" ) ).isEmpty() )
458  {
459  styleUrl = dsUri.param( QStringLiteral( "styleUrl" ) );
460  }
461  else if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
462  {
463  // for ArcMap VectorTileServices we default to the defaultStyles URL from the layer configuration
464  styleUrl = mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString()
465  + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "defaultStyles" ) ).toString();
466  }
467 
468  if ( !styleUrl.isEmpty() )
469  {
470  QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
471 
472  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );
473 
474  QgsBlockingNetworkRequest networkRequest;
475  switch ( networkRequest.get( request ) )
476  {
478  break;
479 
483  error = QObject::tr( "Error retrieving default style" );
484  return false;
485  }
486 
487  const QgsNetworkReplyContent content = networkRequest.reply();
488  const QVariantMap styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
489 
491  // convert automatically from pixel sizes to millimeters, because pixel sizes
492  // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
494  //assume source uses 96 dpi
495  context.setPixelSizeConversionFactor( 25.4 / 96.0 );
496 
497  if ( styleDefinition.contains( QStringLiteral( "sprite" ) ) )
498  {
499  // retrieve sprite definition
500  QString spriteUriBase;
501  if ( styleDefinition.value( QStringLiteral( "sprite" ) ).toString().startsWith( QLatin1String( "http" ) ) )
502  {
503  spriteUriBase = styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
504  }
505  else
506  {
507  spriteUriBase = styleUrl + '/' + styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
508  }
509 
510  for ( int resolution = 2; resolution > 0; resolution-- )
511  {
512  QUrl spriteUrl = QUrl( spriteUriBase );
513  spriteUrl.setPath( spriteUrl.path() + QStringLiteral( "%1.json" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) );
514  QNetworkRequest request = QNetworkRequest( spriteUrl );
515  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
516  QgsBlockingNetworkRequest networkRequest;
517  switch ( networkRequest.get( request ) )
518  {
520  {
521  const QgsNetworkReplyContent content = networkRequest.reply();
522  const QVariantMap spriteDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
523 
524  // retrieve sprite images
525  QUrl spriteUrl = QUrl( spriteUriBase );
526  spriteUrl.setPath( spriteUrl.path() + QStringLiteral( "%1.png" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) );
527  QNetworkRequest request = QNetworkRequest( spriteUrl );
528  QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
529  QgsBlockingNetworkRequest networkRequest;
530  switch ( networkRequest.get( request ) )
531  {
533  {
534  const QgsNetworkReplyContent imageContent = networkRequest.reply();
535  const QImage spriteImage( QImage::fromData( imageContent.content() ) );
536  context.setSprites( spriteImage, spriteDefinition );
537  break;
538  }
539 
543  break;
544  }
545 
546  break;
547  }
548 
552  break;
553  }
554 
555  if ( !context.spriteDefinitions().isEmpty() )
556  break;
557  }
558  }
559 
560  QgsMapBoxGlStyleConverter converter;
561  if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
562  {
563  warnings = converter.warnings();
564  error = converter.errorMessage();
565  return false;
566  }
567 
568  setRenderer( converter.renderer() );
569  setLabeling( converter.labeling() );
570  warnings = converter.warnings();
571  return true;
572  }
573  else
574  {
575  bool resultFlag = false;
576  error = QgsMapLayer::loadDefaultStyle( resultFlag );
577  return resultFlag;
578  }
579 }
580 
581 QString QgsVectorTileLayer::loadDefaultMetadata( bool &resultFlag )
582 {
583  QgsDataSourceUri dsUri;
584  dsUri.setEncodedUri( mDataSource );
585  if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
586  {
587  // populate default metadata
589  metadata.setIdentifier( mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString() );
590  const QString parentIdentifier = mArcgisLayerConfiguration.value( QStringLiteral( "serviceItemId" ) ).toString();
591  if ( !parentIdentifier.isEmpty() )
592  {
593  metadata.setParentIdentifier( parentIdentifier );
594  }
595  metadata.setType( QStringLiteral( "dataset" ) );
596  metadata.setTitle( mArcgisLayerConfiguration.value( QStringLiteral( "name" ) ).toString() );
597  const QString copyright = mArcgisLayerConfiguration.value( QStringLiteral( "copyrightText" ) ).toString();
598  if ( !copyright.isEmpty() )
599  metadata.setRights( QStringList() << copyright );
600  metadata.addLink( QgsAbstractMetadataBase::Link( tr( "Source" ), QStringLiteral( "WWW:LINK" ), mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString() ) );
601 
603 
604  resultFlag = true;
605  return QString();
606  }
607  else
608  {
609  QgsMapLayer::loadDefaultMetadata( resultFlag );
610  resultFlag = true;
611  return QString();
612  }
613 }
614 
615 QString QgsVectorTileLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
616 {
617  QgsDataSourceUri dsUri;
618  dsUri.setEncodedUri( source );
619 
620  const QString sourceType = dsUri.param( QStringLiteral( "type" ) );
621  QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
622  if ( sourceType == QLatin1String( "xyz" ) )
623  {
624  const QUrl sourceUrl( sourcePath );
625  if ( sourceUrl.isLocalFile() )
626  {
627  // relative path will become "file:./x.txt"
628  const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
629  dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
630  dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
631  return dsUri.encodedUri();
632  }
633  }
634  else if ( sourceType == QLatin1String( "mbtiles" ) )
635  {
637  dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
638  dsUri.setParam( QStringLiteral( "url" ), sourcePath );
639  return dsUri.encodedUri();
640  }
641 
642  return source;
643 }
644 
645 QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
646 {
647  Q_UNUSED( provider )
648 
649  QgsDataSourceUri dsUri;
650  dsUri.setEncodedUri( source );
651 
652  const QString sourceType = dsUri.param( QStringLiteral( "type" ) );
653  QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
654  if ( sourceType == QLatin1String( "xyz" ) )
655  {
656  const QUrl sourceUrl( sourcePath );
657  if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
658  {
659  const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
660  dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
661  dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
662  return dsUri.encodedUri();
663  }
664  }
665  else if ( sourceType == QLatin1String( "mbtiles" ) )
666  {
668  dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
669  dsUri.setParam( QStringLiteral( "url" ), sourcePath );
670  return dsUri.encodedUri();
671  }
672 
673  return source;
674 }
675 
677 {
678  const QgsLayerMetadataFormatter htmlFormatter( metadata() );
679 
680  QString info = QStringLiteral( "<html><head></head>\n<body>\n" );
681 
682  info += generalHtmlMetadata();
683 
684  info += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
685  QStringLiteral( "<table class=\"list-view\">\n" );
686 
687  info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source type" ) % QStringLiteral( "</td><td>" ) % sourceType() % QStringLiteral( "</td></tr>\n" );
688 
689  info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Zoom levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % QStringLiteral( "</td></tr>\n" );
690 
691  info += QLatin1String( "</table>\n<br>" );
692 
693  // CRS
694  info += crsHtmlMetadata();
695 
696  // Identification section
697  info += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
698  htmlFormatter.identificationSectionHtml() %
699  QStringLiteral( "<br>\n" ) %
700 
701  // extent section
702  QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
703  htmlFormatter.extentSectionHtml( ) %
704  QStringLiteral( "<br>\n" ) %
705 
706  // Start the Access section
707  QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
708  htmlFormatter.accessSectionHtml( ) %
709  QStringLiteral( "<br>\n" ) %
710 
711 
712  // Start the contacts section
713  QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
714  htmlFormatter.contactsSectionHtml( ) %
715  QStringLiteral( "<br><br>\n" ) %
716 
717  // Start the links section
718  QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
719  htmlFormatter.linksSectionHtml( ) %
720  QStringLiteral( "<br>\n" ) %
721 
722  // Start the history section
723  QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
724  htmlFormatter.historySectionHtml( ) %
725  QStringLiteral( "<br>\n" ) %
726 
727  QStringLiteral( "\n</body>\n</html>\n" );
728 
729  return info;
730 }
731 
733 {
734  const QgsTileMatrix tileMatrix = mMatrixSet.tileMatrix( tileID.zoomLevel() );
735  const QgsTileRange tileRange( tileID.column(), tileID.column(), tileID.row(), tileID.row() );
736 
737  QgsDataSourceUri dsUri;
738  dsUri.setEncodedUri( mDataSource );
739  const QString authcfg = dsUri.authConfigId();
740  QgsHttpHeaders headers;
741  headers [QStringLiteral( "referer" ) ] = dsUri.param( QStringLiteral( "referer" ) );
742 
743  QList<QgsVectorTileRawData> rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, tileMatrix, QPointF(), tileRange, authcfg, headers );
744  if ( rawTiles.isEmpty() )
745  return QByteArray();
746  return rawTiles.first().data;
747 }
748 
750 {
751  mRenderer.reset( r );
752  triggerRepaint();
753 }
754 
756 {
757  return mRenderer.get();
758 }
759 
761 {
762  mLabeling.reset( labeling );
763  triggerRepaint();
764 }
765 
767 {
768  return mLabeling.get();
769 }
770 
771 
772 
773 //
774 // QgsVectorTileDataProvider
775 //
777 QgsVectorTileDataProvider::QgsVectorTileDataProvider(
778  const ProviderOptions &options,
779  QgsDataProvider::ReadFlags flags )
780  : QgsDataProvider( QString(), options, flags )
781 {}
782 
784 {
785  return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
786 }
787 
788 QString QgsVectorTileDataProvider::name() const
789 {
790  return QStringLiteral( "vectortile" );
791 }
792 
793 QString QgsVectorTileDataProvider::description() const
794 {
795  return QString();
796 }
797 
798 QgsRectangle QgsVectorTileDataProvider::extent() const
799 {
800  return QgsRectangle();
801 }
802 
803 bool QgsVectorTileDataProvider::isValid() const
804 {
805  return true;
806 }
void setType(const QString &type)
Sets the type (nature) of the resource.
void setParentIdentifier(const QString &parentIdentifier)
Sets a reference, URI, URL or some other mechanism to identify the parent resource that this resource...
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results.
void setIdentifier(const QString &identifier)
Sets the reference, URI, URL or some other mechanism to identify the resource.
void addLink(const QgsAbstractMetadataBase::Link &link)
Adds an individual link to the existing links.
static QgsCoordinateReferenceSystem convertSpatialReference(const QVariantMap &spatialReferenceMap)
Converts a spatial reference JSON definition to a QgsCoordinateReferenceSystem value.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
@ NetworkError
A network error occurred.
@ ServerExceptionError
An exception was raised by the server.
@ NoError
No error was encountered.
@ TimeoutError
Timeout was reached before a reply was received.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get() or post() request has been made.
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.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Abstract base class for spatial data provider implementations.
Class for storing the component parts of a RDBMS data source URI (e.g.
QByteArray encodedUri() const
Returns the complete encoded URI as a byte array.
bool hasParam(const QString &key) const
Returns true if a parameter with the specified key exists.
int removeParam(const QString &key)
Removes a generic parameter by key.
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.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on the URI.
QString authConfigId() const
Returns any associated authentication configuration ID stored in the URI.
This class implements simple http header management.
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.
QString linksSectionHtml() const
Formats the "Links" section according to a metadata object.
QString extentSectionHtml(const bool showSpatialExtent=true) const
Formats the "Extents" section according to a metadata object (extent and temporal).
QString contactsSectionHtml() const
Formats the "Contacts" section according to a metadata object.
QString identificationSectionHtml() const
Formats the "Identification" section according to a metadata object.
QString historySectionHtml() const
Formats the "History" section according to a metadata object.
QString accessSectionHtml() const
Formats the "Access" section according to a metadata object.
A structured metadata store for a map layer.
void setRights(const QStringList &rights)
Sets a list of rights (attribution or copyright strings) associated with the resource.
Context for a MapBox GL style conversion operation.
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.
void setTargetUnit(QgsUnitTypes::RenderUnit targetUnit)
Sets the target unit type.
QVariantMap spriteDefinitions() const
Returns the sprite definitions 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.
@ 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(QgsMapLayerType 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:73
QString name
Definition: qgsmaplayer.h:76
void readStyleManager(const QDomNode &layerNode)
Read style manager's configuration (if any). To be called by subclasses.
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.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
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.
Definition: qgsmaplayer.h:1931
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:78
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
virtual void setOpacity(double opacity)
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
virtual void setMetadata(const QgsLayerMetadata &metadata)
Sets the layer's metadata store.
QString mProviderKey
Data provider key (name of the data provider)
Definition: qgsmaplayer.h:1969
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 void setExtent(const QgsRectangle &rect)
Sets the extent.
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:1928
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:82
@ Symbology
Symbology.
Definition: qgsmaplayer.h:161
@ Rendering
Rendering: scale visibility, simplify method, opacity.
Definition: qgsmaplayer.h:170
@ Labeling
Labeling.
Definition: qgsmaplayer.h:163
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.
Utility class for reading and writing MBTiles files (which are SQLite3 databases).
Definition: qgsmbtiles.h:39
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 QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
BlendMode
Blending modes enum defining the available composition modes that can be used when rendering a layer.
Definition: qgspainting.h:37
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
The class is used as a container of context for various read/write operations on other objects.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Contains information about the context of a rendering operation.
virtual QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Writes the set to an XML element.
Definition: qgstiles.cpp:329
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system associated with the tiles.
Definition: qgstiles.cpp:190
int minimumZoom() const
Returns the minimum zoom level for tiles present in the set.
Definition: qgstiles.cpp:153
int maximumZoom() const
Returns the maximum zoom level for tiles present in the set.
Definition: qgstiles.cpp:164
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition: qgstiles.cpp:143
virtual bool readXml(const QDomElement &element, QgsReadWriteContext &context)
Reads the set from an XML element.
Definition: qgstiles.cpp:298
void dropMatricesOutsideZoomRange(int minimumZoom, int maximumZoom)
Deletes any existing matrices which fall outside the zoom range specified by minimumZoom to maximumZo...
Definition: qgstiles.cpp:175
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition: qgstiles.h:108
Range of tiles in a tile matrix to be rendered.
Definition: qgstiles.h:71
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
int zoomLevel() const
Returns tile's zoom level (Z)
Definition: qgstiles.h:51
int column() const
Returns tile's column index (X)
Definition: qgstiles.h:47
int row() const
Returns tile's row index (Y)
Definition: qgstiles.h:49
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
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 labeling configuration classes for vector tile layers.
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)=0
Reads labeling properties from given XML element.
This class provides map rendering functionality for vector tile layers.
Implements a map layer that is dedicated to rendering of vector tiles.
QgsVectorTileLayer(const QString &path=QString(), const QString &baseName=QString(), const QgsVectorTileLayer::LayerOptions &options=QgsVectorTileLayer::LayerOptions())
Constructs a new vector tile layer.
QByteArray getRawTile(QgsTileXYZ tileID)
Fetches raw tile data for the give tile coordinates.
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.
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.
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 setLabeling(QgsVectorTileLabeling *labeling)
Sets labeling for the map layer.
QgsVectorTileLabeling * labeling() const
Returns currently assigned labeling.
~QgsVectorTileLayer() override
bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) override
Read the symbology for the current layer from the DOM node supplied.
int sourceMinZoom() const
Returns minimum zoom level at which source has any valid tiles (negative = unconstrained)
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) override
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
QString loadDefaultStyle(bool &resultFlag) override
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
QString 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.
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)
void setTransformContext(const QgsCoordinateTransformContext &transformContext) override
Sets the coordinate transform context to transformContext.
QgsVectorTileLayer * clone() const override
Returns a new instance equivalent to this one except for the id which is still unique.
static QList< QgsVectorTileRawData > blockingFetchTileRawData(const QString &sourceType, const QString &sourcePath, const QgsTileMatrix &tileMatrix, const QPointF &viewCenter, const QgsTileRange &range, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback=nullptr)
Returns raw tile data for the specified range of tiles. Blocks the caller until all tiles are fetched...
bool fromEsriJson(const QVariantMap &json)
Initializes the tile structure settings from an ESRI REST VectorTileService json map.
static QgsVectorTileMatrixSet fromWebMercator(int minimumZoom=0, int maximumZoom=14)
Returns a vector tile structure corresponding to the standard web mercator/GoogleCRS84Quad setup.
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 bool checkXYZUrlTemplate(const QString &url)
Checks whether the URL template string is correct (contains {x}, {y} / {-y}, {z} placeholders)
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
#define QgsSetRequestInitiatorClass(request, _class)
const QgsCoordinateReferenceSystem & crs
Setting options for creating vector data providers.
Setting options for loading vector tile layers.