Branch data Line data Source code
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"
19 : : #include "qgsvectortilelayerrenderer.h"
20 : : #include "qgsmbtiles.h"
21 : : #include "qgsvectortilebasiclabeling.h"
22 : : #include "qgsvectortilebasicrenderer.h"
23 : : #include "qgsvectortilelabeling.h"
24 : : #include "qgsvectortileloader.h"
25 : : #include "qgsvectortileutils.h"
26 : : #include "qgsnetworkaccessmanager.h"
27 : :
28 : : #include "qgsdatasourceuri.h"
29 : : #include "qgslayermetadataformatter.h"
30 : : #include "qgsblockingnetworkrequest.h"
31 : : #include "qgsmapboxglstyleconverter.h"
32 : : #include "qgsjsonutils.h"
33 : : #include "qgspainting.h"
34 : : #include "qgsmaplayerfactory.h"
35 : :
36 : : #include <QUrl>
37 : : #include <QUrlQuery>
38 : :
39 : 0 : QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName )
40 : 0 : : QgsMapLayer( QgsMapLayerType::VectorTileLayer, baseName )
41 : 0 : {
42 : 0 : mDataSource = uri;
43 : :
44 : 0 : setValid( loadDataSource() );
45 : :
46 : : // set a default renderer
47 : 0 : QgsVectorTileBasicRenderer *renderer = new QgsVectorTileBasicRenderer;
48 : 0 : renderer->setStyles( QgsVectorTileBasicRenderer::simpleStyleWithRandomColors() );
49 : 0 : setRenderer( renderer );
50 : 0 : }
51 : :
52 : 0 : bool QgsVectorTileLayer::loadDataSource()
53 : : {
54 : 0 : QgsDataSourceUri dsUri;
55 : 0 : dsUri.setEncodedUri( mDataSource );
56 : :
57 : 0 : mSourceType = dsUri.param( QStringLiteral( "type" ) );
58 : 0 : mSourcePath = dsUri.param( QStringLiteral( "url" ) );
59 : 0 : if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
60 : : {
61 : 0 : if ( !setupArcgisVectorTileServiceConnection( mSourcePath, dsUri ) )
62 : 0 : return false;
63 : 0 : }
64 : 0 : else if ( mSourceType == QLatin1String( "xyz" ) )
65 : : {
66 : 0 : if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
67 : : {
68 : 0 : QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
69 : 0 : return false;
70 : : }
71 : :
72 : : // online tiles
73 : 0 : mSourceMinZoom = 0;
74 : 0 : mSourceMaxZoom = 14;
75 : :
76 : 0 : if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
77 : 0 : mSourceMinZoom = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
78 : 0 : if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
79 : 0 : mSourceMaxZoom = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
80 : :
81 : 0 : setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );
82 : 0 : }
83 : 0 : else if ( mSourceType == QLatin1String( "mbtiles" ) )
84 : : {
85 : 0 : QgsMbTiles reader( mSourcePath );
86 : 0 : if ( !reader.open() )
87 : : {
88 : 0 : QgsDebugMsg( QStringLiteral( "failed to open MBTiles file: " ) + mSourcePath );
89 : 0 : return false;
90 : : }
91 : :
92 : 0 : QString format = reader.metadataValue( QStringLiteral( "format" ) );
93 : 0 : if ( format != QLatin1String( "pbf" ) )
94 : : {
95 : 0 : QgsDebugMsg( QStringLiteral( "Cannot open MBTiles for vector tiles. Format = " ) + format );
96 : 0 : return false;
97 : : }
98 : :
99 : 0 : QgsDebugMsgLevel( QStringLiteral( "name: " ) + reader.metadataValue( QStringLiteral( "name" ) ), 2 );
100 : : bool minZoomOk, maxZoomOk;
101 : 0 : int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk );
102 : 0 : int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk );
103 : 0 : if ( minZoomOk )
104 : 0 : mSourceMinZoom = minZoom;
105 : 0 : if ( maxZoomOk )
106 : 0 : mSourceMaxZoom = maxZoom;
107 : 0 : QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mSourceMinZoom ).arg( mSourceMaxZoom ), 2 );
108 : :
109 : 0 : QgsRectangle r = reader.extent();
110 : 0 : QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ),
111 : 0 : QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), transformContext() );
112 : 0 : r = ct.transformBoundingBox( r );
113 : 0 : setExtent( r );
114 : 0 : }
115 : : else
116 : : {
117 : 0 : QgsDebugMsg( QStringLiteral( "Unknown source type: " ) + mSourceType );
118 : 0 : return false;
119 : : }
120 : :
121 : 0 : setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) );
122 : 0 : return true;
123 : 0 : }
124 : :
125 : 0 : bool QgsVectorTileLayer::setupArcgisVectorTileServiceConnection( const QString &uri, const QgsDataSourceUri &dataSourceUri )
126 : : {
127 : 0 : QUrl url( uri );
128 : : // some services don't default to json format, while others do... so let's explicitly request it!
129 : : // (refs https://github.com/qgis/QGIS/issues/4231)
130 : 0 : QUrlQuery query;
131 : 0 : query.addQueryItem( QStringLiteral( "f" ), QStringLiteral( "pjson" ) );
132 : 0 : url.setQuery( query );
133 : :
134 : 0 : QNetworkRequest request = QNetworkRequest( url );
135 : :
136 : 0 : QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
137 : :
138 : 0 : QgsBlockingNetworkRequest networkRequest;
139 : 0 : switch ( networkRequest.get( request ) )
140 : : {
141 : : case QgsBlockingNetworkRequest::NoError:
142 : 0 : break;
143 : :
144 : : case QgsBlockingNetworkRequest::NetworkError:
145 : : case QgsBlockingNetworkRequest::TimeoutError:
146 : : case QgsBlockingNetworkRequest::ServerExceptionError:
147 : 0 : return false;
148 : : }
149 : :
150 : 0 : const QgsNetworkReplyContent content = networkRequest.reply();
151 : 0 : const QByteArray raw = content.content();
152 : :
153 : : // Parse data
154 : : QJsonParseError err;
155 : 0 : QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
156 : 0 : if ( doc.isNull() )
157 : : {
158 : 0 : return false;
159 : : }
160 : 0 : mArcgisLayerConfiguration = doc.object().toVariantMap();
161 : 0 : if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
162 : : {
163 : 0 : return false;
164 : : }
165 : :
166 : 0 : mArcgisLayerConfiguration.insert( QStringLiteral( "serviceUri" ), uri );
167 : 0 : mSourcePath = uri + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).toList().value( 0 ).toString();
168 : 0 : if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
169 : : {
170 : 0 : QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
171 : 0 : return false;
172 : : }
173 : :
174 : : // if hardcoded zoom limits aren't specified, take them from the server
175 : 0 : if ( !dataSourceUri.hasParam( QStringLiteral( "zmin" ) ) )
176 : 0 : mSourceMinZoom = 0;
177 : : else
178 : 0 : mSourceMinZoom = dataSourceUri.param( QStringLiteral( "zmin" ) ).toInt();
179 : :
180 : 0 : if ( !dataSourceUri.hasParam( QStringLiteral( "zmax" ) ) )
181 : 0 : mSourceMaxZoom = mArcgisLayerConfiguration.value( QStringLiteral( "maxzoom" ) ).toInt();
182 : : else
183 : 0 : mSourceMaxZoom = dataSourceUri.param( QStringLiteral( "zmax" ) ).toInt();
184 : :
185 : 0 : setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );
186 : :
187 : 0 : return true;
188 : 0 : }
189 : 0 :
190 : 0 : QgsVectorTileLayer::~QgsVectorTileLayer() = default;
191 : 0 :
192 : :
193 : 0 : QgsVectorTileLayer *QgsVectorTileLayer::clone() const
194 : : {
195 : 0 : QgsVectorTileLayer *layer = new QgsVectorTileLayer( source(), name() );
196 : 0 : layer->setRenderer( renderer() ? renderer()->clone() : nullptr );
197 : 0 : return layer;
198 : 0 : }
199 : :
200 : 0 : QgsMapLayerRenderer *QgsVectorTileLayer::createMapRenderer( QgsRenderContext &rendererContext )
201 : : {
202 : 0 : return new QgsVectorTileLayerRenderer( this, rendererContext );
203 : 0 : }
204 : :
205 : 0 : bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
206 : : {
207 : 0 : setValid( loadDataSource() );
208 : :
209 : 0 : QString errorMsg;
210 : 0 : if ( !readSymbology( layerNode, errorMsg, context ) )
211 : 0 : return false;
212 : :
213 : 0 : readStyleManager( layerNode );
214 : 0 : return true;
215 : 0 : }
216 : :
217 : 0 : bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const
218 : : {
219 : 0 : QDomElement mapLayerNode = layerNode.toElement();
220 : 0 : mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::VectorTileLayer ) );
221 : :
222 : 0 : writeStyleManager( layerNode, doc );
223 : :
224 : 0 : QString errorMsg;
225 : 0 : return writeSymbology( layerNode, doc, errorMsg, context );
226 : 0 : }
227 : :
228 : 0 : bool QgsVectorTileLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
229 : : {
230 : 0 : QDomElement elem = node.toElement();
231 : :
232 : 0 : readCommonStyle( elem, context, categories );
233 : :
234 : 0 : const QDomElement elemRenderer = elem.firstChildElement( QStringLiteral( "renderer" ) );
235 : 0 : if ( elemRenderer.isNull() )
236 : : {
237 : 0 : errorMessage = tr( "Missing <renderer> tag" );
238 : 0 : return false;
239 : : }
240 : 0 : const QString rendererType = elemRenderer.attribute( QStringLiteral( "type" ) );
241 : :
242 : 0 : if ( categories.testFlag( Symbology ) )
243 : : {
244 : 0 : QgsVectorTileRenderer *r = nullptr;
245 : 0 : if ( rendererType == QLatin1String( "basic" ) )
246 : 0 : r = new QgsVectorTileBasicRenderer;
247 : : else
248 : : {
249 : 0 : errorMessage = tr( "Unknown renderer type: " ) + rendererType;
250 : 0 : return false;
251 : : }
252 : :
253 : 0 : r->readXml( elemRenderer, context );
254 : 0 : setRenderer( r );
255 : 0 : }
256 : :
257 : 0 : if ( categories.testFlag( Labeling ) )
258 : : {
259 : 0 : setLabeling( nullptr );
260 : 0 : const QDomElement elemLabeling = elem.firstChildElement( QStringLiteral( "labeling" ) );
261 : 0 : if ( !elemLabeling.isNull() )
262 : : {
263 : 0 : const QString labelingType = elemLabeling.attribute( QStringLiteral( "type" ) );
264 : 0 : QgsVectorTileLabeling *labeling = nullptr;
265 : 0 : if ( labelingType == QLatin1String( "basic" ) )
266 : 0 : labeling = new QgsVectorTileBasicLabeling;
267 : : else
268 : : {
269 : 0 : errorMessage = tr( "Unknown labeling type: " ) + rendererType;
270 : : }
271 : :
272 : 0 : if ( labeling )
273 : : {
274 : 0 : labeling->readXml( elemLabeling, context );
275 : 0 : setLabeling( labeling );
276 : 0 : }
277 : 0 : }
278 : 0 : }
279 : :
280 : 0 : if ( categories.testFlag( Symbology ) )
281 : : {
282 : : // get and set the blend mode if it exists
283 : 0 : QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
284 : 0 : if ( !blendModeNode.isNull() )
285 : : {
286 : 0 : QDomElement e = blendModeNode.toElement();
287 : 0 : setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
288 : 0 : }
289 : 0 : }
290 : :
291 : : // get and set the layer transparency
292 : 0 : if ( categories.testFlag( Rendering ) )
293 : : {
294 : 0 : QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
295 : 0 : if ( !layerOpacityNode.isNull() )
296 : : {
297 : 0 : QDomElement e = layerOpacityNode.toElement();
298 : 0 : setOpacity( e.text().toDouble() );
299 : 0 : }
300 : 0 : }
301 : :
302 : 0 : return true;
303 : 0 : }
304 : :
305 : 0 : bool QgsVectorTileLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
306 : : {
307 : 0 : Q_UNUSED( errorMessage )
308 : 0 : QDomElement elem = node.toElement();
309 : :
310 : 0 : writeCommonStyle( elem, doc, context, categories );
311 : :
312 : 0 : if ( mRenderer )
313 : : {
314 : 0 : QDomElement elemRenderer = doc.createElement( QStringLiteral( "renderer" ) );
315 : 0 : elemRenderer.setAttribute( QStringLiteral( "type" ), mRenderer->type() );
316 : 0 : if ( categories.testFlag( Symbology ) )
317 : : {
318 : 0 : mRenderer->writeXml( elemRenderer, context );
319 : 0 : }
320 : 0 : elem.appendChild( elemRenderer );
321 : 0 : }
322 : :
323 : 0 : if ( mLabeling && categories.testFlag( Labeling ) )
324 : : {
325 : 0 : QDomElement elemLabeling = doc.createElement( QStringLiteral( "labeling" ) );
326 : 0 : elemLabeling.setAttribute( QStringLiteral( "type" ), mLabeling->type() );
327 : 0 : mLabeling->writeXml( elemLabeling, context );
328 : 0 : elem.appendChild( elemLabeling );
329 : 0 : }
330 : :
331 : 0 : if ( categories.testFlag( Symbology ) )
332 : : {
333 : : // add the blend mode field
334 : 0 : QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
335 : 0 : QDomText blendModeText = doc.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
336 : 0 : blendModeElem.appendChild( blendModeText );
337 : 0 : node.appendChild( blendModeElem );
338 : 0 : }
339 : :
340 : : // add the layer opacity
341 : 0 : if ( categories.testFlag( Rendering ) )
342 : : {
343 : 0 : QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
344 : 0 : QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
345 : 0 : layerOpacityElem.appendChild( layerOpacityText );
346 : 0 : node.appendChild( layerOpacityElem );
347 : 0 : }
348 : :
349 : : return true;
350 : 0 : }
351 : :
352 : 0 : void QgsVectorTileLayer::setTransformContext( const QgsCoordinateTransformContext &transformContext )
353 : : {
354 : 0 : Q_UNUSED( transformContext )
355 : 0 : invalidateWgs84Extent();
356 : 0 : }
357 : :
358 : 0 : QString QgsVectorTileLayer::loadDefaultStyle( bool &resultFlag )
359 : : {
360 : 0 : QString error;
361 : 0 : QStringList warnings;
362 : 0 : resultFlag = loadDefaultStyle( error, warnings );
363 : 0 : return error;
364 : 0 : }
365 : :
366 : 0 : bool QgsVectorTileLayer::loadDefaultStyle( QString &error, QStringList &warnings )
367 : : {
368 : 0 : QgsDataSourceUri dsUri;
369 : 0 : dsUri.setEncodedUri( mDataSource );
370 : :
371 : 0 : QString styleUrl;
372 : 0 : if ( !dsUri.param( QStringLiteral( "styleUrl" ) ).isEmpty() )
373 : : {
374 : 0 : styleUrl = dsUri.param( QStringLiteral( "styleUrl" ) );
375 : 0 : }
376 : 0 : else if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
377 : : {
378 : : // for ArcMap VectorTileServices we default to the defaultStyles URL from the layer configuration
379 : 0 : styleUrl = mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString()
380 : 0 : + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "defaultStyles" ) ).toString();
381 : 0 : }
382 : :
383 : 0 : if ( !styleUrl.isEmpty() )
384 : : {
385 : 0 : QNetworkRequest request = QNetworkRequest( QUrl( styleUrl ) );
386 : :
387 : 0 : QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );
388 : :
389 : 0 : QgsBlockingNetworkRequest networkRequest;
390 : 0 : switch ( networkRequest.get( request ) )
391 : : {
392 : : case QgsBlockingNetworkRequest::NoError:
393 : 0 : break;
394 : :
395 : : case QgsBlockingNetworkRequest::NetworkError:
396 : : case QgsBlockingNetworkRequest::TimeoutError:
397 : : case QgsBlockingNetworkRequest::ServerExceptionError:
398 : 0 : error = QObject::tr( "Error retrieving default style" );
399 : 0 : return false;
400 : : }
401 : :
402 : 0 : const QgsNetworkReplyContent content = networkRequest.reply();
403 : 0 : const QVariantMap styleDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
404 : :
405 : 0 : QgsMapBoxGlStyleConversionContext context;
406 : : // convert automatically from pixel sizes to millimeters, because pixel sizes
407 : : // are a VERY edge case in QGIS and don't play nice with hidpi map renders or print layouts
408 : 0 : context.setTargetUnit( QgsUnitTypes::RenderMillimeters );
409 : : //assume source uses 96 dpi
410 : 0 : context.setPixelSizeConversionFactor( 25.4 / 96.0 );
411 : :
412 : 0 : if ( styleDefinition.contains( QStringLiteral( "sprite" ) ) )
413 : : {
414 : : // retrieve sprite definition
415 : 0 : QString spriteUriBase;
416 : 0 : if ( styleDefinition.value( QStringLiteral( "sprite" ) ).toString().startsWith( QLatin1String( "http" ) ) )
417 : : {
418 : 0 : spriteUriBase = styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
419 : 0 : }
420 : : else
421 : : {
422 : 0 : spriteUriBase = styleUrl + '/' + styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
423 : : }
424 : :
425 : 0 : for ( int resolution = 2; resolution > 0; resolution-- )
426 : : {
427 : 0 : QNetworkRequest request = QNetworkRequest( QUrl( spriteUriBase + QStringLiteral( "%1.json" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) ) );
428 : 0 : QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
429 : 0 : QgsBlockingNetworkRequest networkRequest;
430 : 0 : switch ( networkRequest.get( request ) )
431 : : {
432 : : case QgsBlockingNetworkRequest::NoError:
433 : : {
434 : 0 : const QgsNetworkReplyContent content = networkRequest.reply();
435 : 0 : const QVariantMap spriteDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
436 : :
437 : : // retrieve sprite images
438 : 0 : QNetworkRequest request = QNetworkRequest( QUrl( spriteUriBase + QStringLiteral( "%1.png" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) ) );
439 : :
440 : 0 : QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
441 : :
442 : 0 : QgsBlockingNetworkRequest networkRequest;
443 : 0 : switch ( networkRequest.get( request ) )
444 : : {
445 : : case QgsBlockingNetworkRequest::NoError:
446 : : {
447 : 0 : const QgsNetworkReplyContent imageContent = networkRequest.reply();
448 : 0 : QImage spriteImage( QImage::fromData( imageContent.content() ) );
449 : 0 : context.setSprites( spriteImage, spriteDefinition );
450 : : break;
451 : 0 : }
452 : :
453 : : case QgsBlockingNetworkRequest::NetworkError:
454 : : case QgsBlockingNetworkRequest::TimeoutError:
455 : : case QgsBlockingNetworkRequest::ServerExceptionError:
456 : 0 : break;
457 : : }
458 : :
459 : : break;
460 : 0 : }
461 : :
462 : : case QgsBlockingNetworkRequest::NetworkError:
463 : : case QgsBlockingNetworkRequest::TimeoutError:
464 : : case QgsBlockingNetworkRequest::ServerExceptionError:
465 : 0 : break;
466 : : }
467 : :
468 : 0 : if ( !context.spriteDefinitions().isEmpty() )
469 : 0 : break;
470 : 0 : }
471 : 0 : }
472 : :
473 : 0 : QgsMapBoxGlStyleConverter converter;
474 : 0 : if ( converter.convert( styleDefinition, &context ) != QgsMapBoxGlStyleConverter::Success )
475 : : {
476 : 0 : warnings = converter.warnings();
477 : 0 : error = converter.errorMessage();
478 : 0 : return false;
479 : : }
480 : :
481 : 0 : setRenderer( converter.renderer() );
482 : 0 : setLabeling( converter.labeling() );
483 : 0 : warnings = converter.warnings();
484 : 0 : return true;
485 : 0 : }
486 : : else
487 : : {
488 : 0 : bool resultFlag = false;
489 : 0 : error = QgsMapLayer::loadDefaultStyle( resultFlag );
490 : 0 : return resultFlag;
491 : : }
492 : 0 : }
493 : :
494 : 0 : QString QgsVectorTileLayer::loadDefaultMetadata( bool &resultFlag )
495 : : {
496 : 0 : QgsDataSourceUri dsUri;
497 : 0 : dsUri.setEncodedUri( mDataSource );
498 : 0 : if ( mSourceType == QLatin1String( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
499 : : {
500 : : // populate default metadata
501 : 0 : QgsLayerMetadata metadata;
502 : 0 : metadata.setIdentifier( mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString() );
503 : 0 : const QString parentIdentifier = mArcgisLayerConfiguration.value( QStringLiteral( "serviceItemId" ) ).toString();
504 : 0 : if ( !parentIdentifier.isEmpty() )
505 : : {
506 : 0 : metadata.setParentIdentifier( parentIdentifier );
507 : 0 : }
508 : 0 : metadata.setType( QStringLiteral( "dataset" ) );
509 : 0 : metadata.setTitle( mArcgisLayerConfiguration.value( QStringLiteral( "name" ) ).toString() );
510 : 0 : QString copyright = mArcgisLayerConfiguration.value( QStringLiteral( "copyrightText" ) ).toString();
511 : 0 : if ( !copyright.isEmpty() )
512 : 0 : metadata.setRights( QStringList() << copyright );
513 : 0 : metadata.addLink( QgsAbstractMetadataBase::Link( tr( "Source" ), QStringLiteral( "WWW:LINK" ), mArcgisLayerConfiguration.value( QStringLiteral( "serviceUri" ) ).toString() ) );
514 : :
515 : 0 : setMetadata( metadata );
516 : :
517 : 0 : resultFlag = true;
518 : 0 : return QString();
519 : 0 : }
520 : : else
521 : : {
522 : 0 : QgsMapLayer::loadDefaultMetadata( resultFlag );
523 : 0 : resultFlag = true;
524 : 0 : return QString();
525 : : }
526 : 0 : }
527 : :
528 : 0 : QString QgsVectorTileLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
529 : : {
530 : 0 : QgsDataSourceUri dsUri;
531 : 0 : dsUri.setEncodedUri( source );
532 : :
533 : 0 : QString sourceType = dsUri.param( QStringLiteral( "type" ) );
534 : 0 : QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
535 : 0 : if ( sourceType == QLatin1String( "xyz" ) )
536 : : {
537 : 0 : QUrl sourceUrl( sourcePath );
538 : 0 : if ( sourceUrl.isLocalFile() )
539 : : {
540 : : // relative path will become "file:./x.txt"
541 : 0 : QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
542 : 0 : dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
543 : 0 : dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
544 : 0 : return dsUri.encodedUri();
545 : 0 : }
546 : 0 : }
547 : 0 : else if ( sourceType == QLatin1String( "mbtiles" ) )
548 : : {
549 : 0 : sourcePath = context.pathResolver().writePath( sourcePath );
550 : 0 : dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
551 : 0 : dsUri.setParam( QStringLiteral( "url" ), sourcePath );
552 : 0 : return dsUri.encodedUri();
553 : : }
554 : :
555 : 0 : return source;
556 : 0 : }
557 : :
558 : 0 : QString QgsVectorTileLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
559 : : {
560 : 0 : Q_UNUSED( provider )
561 : :
562 : 0 : QgsDataSourceUri dsUri;
563 : 0 : dsUri.setEncodedUri( source );
564 : :
565 : 0 : QString sourceType = dsUri.param( QStringLiteral( "type" ) );
566 : 0 : QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
567 : 0 : if ( sourceType == QLatin1String( "xyz" ) )
568 : : {
569 : 0 : QUrl sourceUrl( sourcePath );
570 : 0 : if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
571 : : {
572 : 0 : QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
573 : 0 : dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
574 : 0 : dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
575 : 0 : return dsUri.encodedUri();
576 : 0 : }
577 : 0 : }
578 : 0 : else if ( sourceType == QLatin1String( "mbtiles" ) )
579 : : {
580 : 0 : sourcePath = context.pathResolver().readPath( sourcePath );
581 : 0 : dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
582 : 0 : dsUri.setParam( QStringLiteral( "url" ), sourcePath );
583 : 0 : return dsUri.encodedUri();
584 : : }
585 : :
586 : 0 : return source;
587 : 0 : }
588 : :
589 : 0 : QString QgsVectorTileLayer::htmlMetadata() const
590 : : {
591 : 0 : QgsLayerMetadataFormatter htmlFormatter( metadata() );
592 : :
593 : 0 : QString info = QStringLiteral( "<html><head></head>\n<body>\n" );
594 : :
595 : 0 : info += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
596 : 0 : QStringLiteral( "<table class=\"list-view\">\n" ) %
597 : :
598 : : // name
599 : 0 : QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Name" ) % QStringLiteral( "</td><td>" ) % name() % QStringLiteral( "</td></tr>\n" );
600 : :
601 : 0 : info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "URI" ) % QStringLiteral( "</td><td>" ) % source() % QStringLiteral( "</td></tr>\n" );
602 : 0 : info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source type" ) % QStringLiteral( "</td><td>" ) % sourceType() % QStringLiteral( "</td></tr>\n" );
603 : :
604 : 0 : const QString url = sourcePath();
605 : 0 : info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Source path" ) % QStringLiteral( "</td><td>%1" ).arg( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( QUrl( url ).toString(), sourcePath() ) ) + QStringLiteral( "</td></tr>\n" );
606 : :
607 : 0 : info += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Zoom levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( sourceMinZoom() ).arg( sourceMaxZoom() ) % QStringLiteral( "</td></tr>\n" );
608 : 0 : info += QLatin1String( "</table>" );
609 : :
610 : : // End Provider section
611 : 0 : info += QLatin1String( "</table>\n<br><br>" );
612 : :
613 : : // Identification section
614 : 0 : info += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
615 : 0 : htmlFormatter.identificationSectionHtml() %
616 : 0 : QStringLiteral( "<br><br>\n" ) %
617 : :
618 : : // extent section
619 : 0 : QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
620 : 0 : htmlFormatter.extentSectionHtml( ) %
621 : 0 : QStringLiteral( "<br><br>\n" ) %
622 : :
623 : : // Start the Access section
624 : 0 : QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
625 : 0 : htmlFormatter.accessSectionHtml( ) %
626 : 0 : QStringLiteral( "<br><br>\n" ) %
627 : :
628 : :
629 : : // Start the contacts section
630 : 0 : QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
631 : 0 : htmlFormatter.contactsSectionHtml( ) %
632 : 0 : QStringLiteral( "<br><br>\n" ) %
633 : :
634 : : // Start the links section
635 : 0 : QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
636 : 0 : htmlFormatter.linksSectionHtml( ) %
637 : 0 : QStringLiteral( "<br><br>\n" ) %
638 : :
639 : : // Start the history section
640 : 0 : QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
641 : 0 : htmlFormatter.historySectionHtml( ) %
642 : 0 : QStringLiteral( "<br><br>\n" ) %
643 : :
644 : 0 : QStringLiteral( "\n</body>\n</html>\n" );
645 : :
646 : 0 : return info;
647 : 0 : }
648 : :
649 : 0 : QByteArray QgsVectorTileLayer::getRawTile( QgsTileXYZ tileID )
650 : : {
651 : 0 : QgsTileMatrix tileMatrix = QgsTileMatrix::fromWebMercator( tileID.zoomLevel() );
652 : 0 : QgsTileRange tileRange( tileID.column(), tileID.column(), tileID.row(), tileID.row() );
653 : :
654 : 0 : QgsDataSourceUri dsUri;
655 : 0 : dsUri.setEncodedUri( mDataSource );
656 : 0 : const QString authcfg = dsUri.authConfigId();
657 : 0 : const QString referer = dsUri.param( QStringLiteral( "referer" ) );
658 : :
659 : 0 : QList<QgsVectorTileRawData> rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, tileMatrix, QPointF(), tileRange, authcfg, referer );
660 : 0 : if ( rawTiles.isEmpty() )
661 : 0 : return QByteArray();
662 : 0 : return rawTiles.first().data;
663 : 0 : }
664 : :
665 : 0 : void QgsVectorTileLayer::setRenderer( QgsVectorTileRenderer *r )
666 : : {
667 : 0 : mRenderer.reset( r );
668 : 0 : triggerRepaint();
669 : 0 : }
670 : :
671 : 0 : QgsVectorTileRenderer *QgsVectorTileLayer::renderer() const
672 : : {
673 : 0 : return mRenderer.get();
674 : : }
675 : :
676 : 0 : void QgsVectorTileLayer::setLabeling( QgsVectorTileLabeling *labeling )
677 : : {
678 : 0 : mLabeling.reset( labeling );
679 : 0 : triggerRepaint();
680 : 0 : }
681 : :
682 : 0 : QgsVectorTileLabeling *QgsVectorTileLayer::labeling() const
683 : : {
684 : 0 : return mLabeling.get();
685 : : }
|