QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsquantizedmeshdataprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsquantizedmeshdataprovider.cpp
3 --------------------
4 begin : June 2024
5 copyright : (C) 2024 by David Koňařík
6 email : dvdkon at konarici dot cz
7 ******************************************************************
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
20#include "moc_qgsquantizedmeshdataprovider.cpp"
21#include "qgsapplication.h"
22#include "qgsauthmanager.h"
27#include "qgslogger.h"
28#include "qgsmatrix4x4.h"
29#include "qgsorientedbox3d.h"
30#include "qgsprovidermetadata.h"
34#include "qgstiledsceneindex.h"
36#include "qgstiledscenetile.h"
37#include "qgstiles.h"
38#include "qgsvectortileutils.h"
39#include <limits>
40#include <nlohmann/json.hpp>
41#include <qglobal.h>
42#include <qnetworkrequest.h>
43#include <qobject.h>
44#include <qstringliteral.h>
45#include <qvector.h>
46#include <QUrlQuery>
47
49
50class MissingFieldException : public std::exception
51{
52 public:
53 MissingFieldException( const char *field ) : mField( field ) { }
54 const char *what() const noexcept
55 {
56 return QString( "Missing field: %1" ).arg( mField ).toLocal8Bit().data();
57 }
58 private:
59 const char *mField;
60};
61
62template <typename T>
63static T jsonGet( nlohmann::json &json, const char *idx )
64{
65 auto &obj = json[idx];
66 if ( obj.is_null() )
67 {
68 throw MissingFieldException( idx );
69 }
70 return obj.get<T>();
71}
72
73
74QgsQuantizedMeshMetadata::QgsQuantizedMeshMetadata(
75 const QString &uri,
76 const QgsCoordinateTransformContext &transformContext,
77 QgsError &error )
78{
79 QgsDataSourceUri dsUri;
80 dsUri.setEncodedUri( uri );
81 mAuthCfg = dsUri.authConfigId();
82 mHeaders = dsUri.httpHeaders();
83
84 // The provided URL should be the metadata JSON's location
85 QUrl metadataUrl = dsUri.param( "url" );
86 QNetworkRequest requestData( metadataUrl );
87 mHeaders.updateNetworkRequest( requestData );
88 QgsSetRequestInitiatorClass( requestData,
89 QStringLiteral( "QgsQuantizedMeshDataProvider" ) );
91 if ( !mAuthCfg.isEmpty() )
92 request.setAuthCfg( mAuthCfg );
93 auto respCode = request.get( requestData );
95 {
96 error.append(
97 QObject::tr( "Failed to retrieve quantized mesh tiles metadata: %1" )
98 .arg( request.errorMessage().data() ) );
99 return;
100 }
101 auto reply = request.reply().content();
102
103 try
104 {
105 auto replyJson = nlohmann::json::parse( reply.data() );
106
107 // The metadata is an (undocumented) variant of TileJSON
108 if ( jsonGet<std::string>( replyJson, "format" ) != "quantized-mesh-1.0" )
109 {
110 error.append( QObject::tr( "Unexpected tile format: %1" )
111 .arg( replyJson["format"].dump().c_str() ) );
112 return;
113 }
114
115 auto crsString = QString::fromStdString( jsonGet<std::string>( replyJson, "projection" ) );
116 mCrs = QgsCoordinateReferenceSystem( crsString );
117 if ( !mCrs.isValid() )
118 {
119 error.append( QObject::tr( "Invalid CRS '%1'!" ).arg( crsString ) );
120 return;
121 }
122
123 try
124 {
125 std::vector<double> bounds = jsonGet<std::vector<double>>( replyJson, "bounds" );
126 if ( bounds.size() != 4 )
127 {
128 error.append( QObject::tr( "Bounds array doesn't have 4 items" ) );
129 return;
130 }
131 mExtent = QgsRectangle( bounds[0], bounds[1], bounds[2], bounds[3] );
132 }
133 catch ( MissingFieldException & )
134 {
135 mExtent = mCrs.bounds();
136 }
137
138 auto zRange = dummyZRange;
139 mBoundingVolume =
141 QgsBox3D(
142 mExtent.xMinimum(), mExtent.yMinimum(), zRange.lower(),
143 mExtent.xMaximum(), mExtent.yMaximum(), zRange.upper() ) );
144
145 // The TileJSON spec uses "scheme", but some real-world datasets use "schema"
146 if ( replyJson.find( "scheme" ) != replyJson.end() )
147 mTileScheme = QString::fromStdString( jsonGet<std::string>( replyJson, "scheme" ) );
148 else if ( replyJson.find( "schema" ) != replyJson.end() )
149 mTileScheme = QString::fromStdString( jsonGet<std::string>( replyJson, "schema" ) );
150 else throw MissingFieldException( "scheme/schema" );
151
152 for ( auto &aabbs : replyJson.at( "available" ) )
153 {
154 QVector<QgsTileRange> tileRanges;
155 for ( auto &aabb : aabbs )
156 {
157 tileRanges.push_back(
159 jsonGet<int>( aabb, "startX" ), jsonGet<int>( aabb, "endX" ),
160 jsonGet<int>( aabb, "startY" ), jsonGet<int>( aabb, "endY" ) ) );
161 }
162 mAvailableTiles.push_back( tileRanges );
163 }
164
165 try
166 {
167 mMinZoom = jsonGet<uint8_t>( replyJson, "minzoom" );
168 mMaxZoom = jsonGet<uint8_t>( replyJson, "maxzoom" );
169 }
170 catch ( MissingFieldException & )
171 {
172 mMinZoom = 0;
173 mMaxZoom = mAvailableTiles.size() - 1;
174 }
175
176 QString versionStr =
177 QString::fromStdString( jsonGet<std::string>( replyJson, "version" ) );
178 for ( auto &urlStr : jsonGet<std::vector<std::string>>( replyJson, "tiles" ) )
179 {
180 QUrl url = metadataUrl.resolved( QString::fromStdString( urlStr ) );
181 mTileUrls.push_back(
182 url.toString( QUrl::DecodeReserved ).replace( "{version}", versionStr ) );
183 }
184
185 int rootTileCount = 1;
186 if ( crsString == QLatin1String( "EPSG:4326" ) )
187 rootTileCount = 2;
188 else if ( crsString != QLatin1String( "EPSG:3857" ) )
189 error.append( QObject::tr( "Unhandled CRS: %1" ).arg( crsString ) );
190
191 QgsCoordinateReferenceSystem wgs84( QStringLiteral( "EPSG:4326" ) );
192 // Bounds of tile schema in projected coordinates
193 auto crsBounds =
194 QgsCoordinateTransform( wgs84, mCrs, transformContext )
195 .transform( mCrs.bounds() );
196 QgsPointXY topLeft( crsBounds.xMinimum(), crsBounds.yMaximum() );
197 double z0TileSize = crsBounds.height();
198
199 mTileMatrix = QgsTileMatrix::fromCustomDef( 0, mCrs, topLeft, z0TileSize, rootTileCount, 1 );
200 }
201 catch ( nlohmann::json::exception &ex )
202 {
203 error.append( QObject::tr( "Error parsing JSON metadata: %1" ).arg( ex.what() ) );
204 }
205 catch ( MissingFieldException &ex )
206 {
207 error.append( QObject::tr( "Error parsing JSON metadata: %1" ).arg( ex.what() ) );
208 }
209}
210
211const QgsDoubleRange QgsQuantizedMeshMetadata::dummyZRange = {-10000, 10000};
212
213static QgsTileXYZ tileToTms( QgsTileXYZ &xyzTile )
214{
215 // Flip Y axis for TMS schema
216 Q_ASSERT( xyzTile.zoomLevel() >= 0 );
217 return {xyzTile.column(),
218 ( 1 << xyzTile.zoomLevel() ) - xyzTile.row() - 1,
219 xyzTile.zoomLevel()};
220}
221
222bool QgsQuantizedMeshMetadata::containsTile( QgsTileXYZ tile ) const
223{
224 if ( tile.zoomLevel() < mMinZoom || tile.zoomLevel() > mMaxZoom ||
225 tile.zoomLevel() >= mAvailableTiles.size() )
226 return false;
227 // We operate with XYZ-style tile coordinates, but the availability array may
228 // be given in TMS-style
229 if ( mTileScheme == QLatin1String( "tms" ) )
230 tile = tileToTms( tile );
231 for ( auto &range : mAvailableTiles[tile.zoomLevel()] )
232 {
233 if ( range.startColumn() <= tile.column() && range.endColumn() >= tile.column() &&
234 range.startRow() <= tile.row() && range.endRow() >= tile.row() )
235 return true;
236 }
237 return false;
238}
239
240double QgsQuantizedMeshMetadata::geometricErrorAtZoom( int zoom ) const
241{
242 // The specification doesn't mandate any precision, we can only make a guess
243 // based on each tile's maximum possible numerical precision and some
244 // reasonable-looking constant.
245 return 400000 / pow( 2, zoom );
246}
247
248long long QgsQuantizedMeshIndex::encodeTileId( QgsTileXYZ tile )
249{
250 if ( tile.zoomLevel() == -1 )
251 {
252 Q_ASSERT( tile.column() == 0 && tile.row() == 0 );
253 return ROOT_TILE_ID;
254 }
255 Q_ASSERT( tile.zoomLevel() < ( 2 << 4 ) && ( tile.column() < ( 2 << 27 ) ) &&
256 ( tile.row() < ( 2 << 27 ) ) );
257 return tile.row() | ( ( long long )tile.column() << 28 ) |
258 ( ( long long )tile.zoomLevel() << 56 ) | ( ( long long ) 1 << 61 );
259}
260
261QgsTileXYZ QgsQuantizedMeshIndex::decodeTileId( long long id )
262{
263 if ( id == ROOT_TILE_ID )
264 return QgsTileXYZ( 0, 0, -1 );
265
266 Q_ASSERT( id >> 61 == 1 ); // Check reserved bits
267 return QgsTileXYZ(
268 ( int )( ( id >> 28 ) & ( ( 2 << 27 ) - 1 ) ),
269 ( int )( id & ( ( 2 << 27 ) - 1 ) ),
270 ( int )( ( id >> 56 ) & ( ( 2 << 4 ) - 1 ) ) );
271}
272
273QgsTiledSceneTile QgsQuantizedMeshIndex::rootTile() const
274{
275 // Returns virtual tile to paper over tiling schemes which have >1 tile at zoom 0
276 auto tile = QgsTiledSceneTile( ROOT_TILE_ID );
277 auto bounds = mWgs84ToCrs.transform( mMetadata.mCrs.bounds() );
278 tile.setBoundingVolume(
280 QgsBox3D( bounds, mMetadata.dummyZRange.lower(), mMetadata.dummyZRange.upper() ) ) );
281 tile.setGeometricError( std::numeric_limits<double>::max() );
282 return tile;
283}
284long long QgsQuantizedMeshIndex::parentTileId( long long id ) const
285{
286 if ( id == ROOT_TILE_ID )
287 return -1;
288 auto tile = decodeTileId( id );
289 if ( tile.zoomLevel() == 0 )
290 return ROOT_TILE_ID;
291 return encodeTileId( {tile.zoomLevel() - 1, tile.column() / 2, tile.row() / 2} );
292}
293QVector<long long> QgsQuantizedMeshIndex::childTileIds( long long id ) const
294{
295 auto tile = decodeTileId( id );
296 QVector<long long> children;
297 auto x = tile.column(), y = tile.row(), zoom = tile.zoomLevel();
298
299 if ( mMetadata.containsTile( {x * 2, y * 2, zoom + 1} ) )
300 children.push_back( encodeTileId( {x * 2, y * 2, zoom + 1} ) );
301 if ( mMetadata.containsTile( {x * 2 + 1, y * 2, zoom + 1} ) )
302 children.push_back( encodeTileId( {x * 2 + 1, y * 2, zoom + 1} ) );
303 if ( mMetadata.containsTile( {x * 2, y * 2 + 1, zoom + 1} ) )
304 children.push_back( encodeTileId( {x * 2, y * 2 + 1, zoom + 1} ) );
305 if ( mMetadata.containsTile( {x * 2 + 1, y * 2 + 1, zoom + 1} ) )
306 children.push_back( encodeTileId( {x * 2 + 1, y * 2 + 1, zoom + 1} ) );
307
308 return children;
309}
310QgsTiledSceneTile QgsQuantizedMeshIndex::getTile( long long id )
311{
312 auto xyzTile = decodeTileId( id );
313 QgsTiledSceneTile sceneTile( id );
314
315 auto zoomedMatrix = QgsTileMatrix::fromTileMatrix( xyzTile.zoomLevel(), mMetadata.mTileMatrix );
316 auto tileExtent = zoomedMatrix.tileExtent( xyzTile );
317
318 sceneTile.setBoundingVolume(
320 QgsBox3D( tileExtent, mMetadata.dummyZRange.lower(), mMetadata.dummyZRange.upper() ) ) );
321 sceneTile.setGeometricError( mMetadata.geometricErrorAtZoom( xyzTile.zoomLevel() ) );
322
323 if ( id == ROOT_TILE_ID )
324 // The root tile is fictitious and has no content, don't bother pointing to any.
325 return sceneTile;
326
327 if ( mMetadata.mTileScheme == QLatin1String( "tms" ) )
328 xyzTile = tileToTms( xyzTile );
329
330 if ( mMetadata.mTileUrls.size() == 0 )
331 {
332 QgsDebugError( "Quantized Mesh metadata has no URLs for tiles" );
333 }
334 else
335 {
336 // TODO: Intelligently choose from alternatives. Round robin?
338 mMetadata.mTileUrls[0], xyzTile, zoomedMatrix );
339 sceneTile.setResources( {{"content", tileUri}} );
340 sceneTile.setMetadata(
341 {
342 {QStringLiteral( "gltfUpAxis" ), static_cast<int>( Qgis::Axis::Z )},
343 {QStringLiteral( "contentFormat" ), QStringLiteral( "quantizedmesh" )},
344 } );
345 }
346
347 // Tile meshes have 0.0 -- 1.0 coordinates. Rescale them to the tile's real
348 // width and height in our CRS and move the tile to its position.
349 QgsMatrix4x4 transform(
350 tileExtent.width(), 0, 0, tileExtent.xMinimum(),
351 0, tileExtent.height(), 0, tileExtent.yMinimum(),
352 0, 0, 1, 0,
353 0, 0, 0, 1 );
354 sceneTile.setTransform( transform );
355
356 return sceneTile;
357}
358QVector<long long>
359QgsQuantizedMeshIndex::getTiles( const QgsTiledSceneRequest &request )
360{
361 uint8_t zoomLevel = mMetadata.mMinZoom;
362 if ( request.requiredGeometricError() != 0 )
363 {
364 while ( zoomLevel < mMetadata.mMaxZoom &&
365 mMetadata.geometricErrorAtZoom( zoomLevel ) > request.requiredGeometricError() )
366 zoomLevel++;
367 }
368 auto tileMatrix = QgsTileMatrix::fromTileMatrix( zoomLevel, mMetadata.mTileMatrix );
369
370 QVector<long long> ids;
371 // We can only filter on X and Y
372 auto extent = request.filterBox().extent().toRectangle();
373 if ( request.parentTileId() != -1 )
374 {
375 auto parentTile = decodeTileId( request.parentTileId() );
376 extent.intersect( tileMatrix.tileExtent( parentTile ) );
377 }
378
379 auto tileRange = tileMatrix.tileRangeFromExtent( extent );
380 if ( !tileRange.isValid() )
381 return {};
382
383 for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); col++ )
384 for ( int row = tileRange.startRow(); row <= tileRange.endRow(); row++ )
385 {
386 auto xyzTile = QgsTileXYZ( col, row, zoomLevel );
387 if ( mMetadata.containsTile( xyzTile ) )
388 ids.push_back( encodeTileId( xyzTile ) );
389 }
390
391 return ids;
392}
394QgsQuantizedMeshIndex::childAvailability( long long id ) const
395{
396 auto childIds = childTileIds( id );
397 if ( childIds.count() == 0 )
400}
401bool QgsQuantizedMeshIndex::fetchHierarchy( long long id, QgsFeedback *feedback )
402{
403 // The API was built for Cesium 3D tiles, which have tiles (actual files with
404 // metadata) as nodes of a hierarchy tree, with the actual data in children
405 // of those tiles. For us, tiles are represented by int IDs, so they don't
406 // need to be fetched.
407 Q_UNUSED( id );
408 Q_UNUSED( feedback );
409 return true;
410}
411
412QByteArray QgsQuantizedMeshIndex::fetchContent( const QString &uri,
413 QgsFeedback *feedback )
414{
415 QNetworkRequest requestData( uri );
416 requestData.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
417 requestData.setRawHeader( "Accept", "application/vnd.quantized-mesh,application/octet-stream;q=0.9" );
418 mMetadata.mHeaders.updateNetworkRequest( requestData );
419 if ( !mMetadata.mAuthCfg.isEmpty() )
420 QgsApplication::authManager()->updateNetworkRequest( requestData, mMetadata.mAuthCfg );
421 QgsSetRequestInitiatorClass( requestData,
422 QStringLiteral( "QgsQuantizedMeshIndex" ) );
423
424 std::unique_ptr<QgsTileDownloadManagerReply> reply( QgsApplication::tileDownloadManager()->get( requestData ) );
425
426 QEventLoop loop;
427 if ( feedback )
428 QObject::connect( feedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );
429 QObject::connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit );
430 loop.exec();
431
432 if ( reply->error() != QNetworkReply::NoError )
433 {
434 QgsDebugError( QStringLiteral( "Request failed (%1): %2" ).arg( uri ).arg( reply->errorString() ) );
435 return {};
436 }
437 return reply->data();
438}
439
440QgsQuantizedMeshDataProvider::QgsQuantizedMeshDataProvider(
441 const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions,
443 : QgsTiledSceneDataProvider( uri, providerOptions, flags ), mUri( uri ),
444 mProviderOptions( providerOptions )
445{
446 if ( uri.startsWith( QLatin1String( "ion://" ) ) )
447 {
448 QString updatedUri = uriFromIon( uri );
449 mMetadata = QgsQuantizedMeshMetadata( updatedUri, transformContext(), mError );
450 }
451 else
452 {
453 mMetadata = QgsQuantizedMeshMetadata( uri, transformContext(), mError );
454 }
455
456 if ( mError.isEmpty() )
457 {
458 QgsCoordinateReferenceSystem wgs84( QStringLiteral( "EPSG:4326" ) );
459 QgsCoordinateTransform wgs84ToCrs( wgs84, mMetadata->mCrs, transformContext() );
460 mIndex.emplace( new QgsQuantizedMeshIndex( *mMetadata, wgs84ToCrs ) );
461 mIsValid = true;
462 }
463}
464
465QString QgsQuantizedMeshDataProvider::uriFromIon( const QString &uri )
466{
467 // we expect one of the two options:
468 // ion://?assetId=123&accessToken=xyz
469 // ion://?assetId=123&authcfg=abc
470
471 QUrl url( uri );
472 const QString assetId = QUrlQuery( url ).queryItemValue( QStringLiteral( "assetId" ) );
473 const QString accessToken = QUrlQuery( url ).queryItemValue( QStringLiteral( "accessToken" ) );
474
475 const QString CESIUM_ION_URL = QStringLiteral( "https://api.cesium.com/" );
476
477 QgsDataSourceUri dsUri;
478 dsUri.setEncodedUri( uri );
479 QString authCfg = dsUri.authConfigId();
480 QgsHttpHeaders headers = dsUri.httpHeaders();
481
482 // get asset info
483 {
484 const QString assetInfoEndpoint = CESIUM_ION_URL + QStringLiteral( "v1/assets/%1" ).arg( assetId );
485 QNetworkRequest request = QNetworkRequest( assetInfoEndpoint );
486 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsQuantizedMeshDataProvider" ) )
487 headers.updateNetworkRequest( request );
488 if ( !accessToken.isEmpty() )
489 request.setRawHeader( "Authorization", QStringLiteral( "Bearer %1" ).arg( accessToken ).toLocal8Bit() );
490
491 QgsBlockingNetworkRequest networkRequest;
492 if ( accessToken.isEmpty() )
493 networkRequest.setAuthCfg( authCfg );
494
495 switch ( networkRequest.get( request ) )
496 {
498 break;
499
503 // TODO -- error reporting
504 return QString();
505 }
506
507 const QgsNetworkReplyContent content = networkRequest.reply();
508 const json assetInfoJson = json::parse( content.content().toStdString() );
509 if ( assetInfoJson["type"] != "TERRAIN" )
510 {
511 appendError( QgsErrorMessage( tr( "Only ion TERRAIN content can be accessed, not %1" ).arg( QString::fromStdString( assetInfoJson["type"].get<std::string>() ) ) ) );
512 return QString();
513 }
514 }
515
516 // get tileset access details
517 QString tileSetUri;
518 {
519 const QString tileAccessEndpoint = CESIUM_ION_URL + QStringLiteral( "v1/assets/%1/endpoint" ).arg( assetId );
520 QNetworkRequest request = QNetworkRequest( tileAccessEndpoint );
521 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsQuantizedMeshDataProvider" ) )
522 headers.updateNetworkRequest( request );
523 if ( !accessToken.isEmpty() )
524 request.setRawHeader( "Authorization", QStringLiteral( "Bearer %1" ).arg( accessToken ).toLocal8Bit() );
525
526 QgsBlockingNetworkRequest networkRequest;
527 if ( accessToken.isEmpty() )
528 networkRequest.setAuthCfg( authCfg );
529
530 switch ( networkRequest.get( request ) )
531 {
533 break;
534
538 // TODO -- error reporting
539 return QString();
540 }
541
542 const QgsNetworkReplyContent content = networkRequest.reply();
543 const json tileAccessJson = json::parse( content.content().toStdString() );
544
545 if ( tileAccessJson.contains( "url" ) )
546 {
547 tileSetUri = QString::fromStdString( tileAccessJson["url"].get<std::string>() );
548 }
549 else if ( tileAccessJson.contains( "options" ) )
550 {
551 const auto &optionsJson = tileAccessJson["options"];
552 if ( optionsJson.contains( "url" ) )
553 {
554 tileSetUri = QString::fromStdString( optionsJson["url"].get<std::string>() );
555 }
556 }
557
558 if ( tileAccessJson.contains( "accessToken" ) )
559 {
560 // The tileset accessToken is NOT the same as the token we use to access the asset details -- ie we can't
561 // use the same authentication as we got from the providers auth cfg!
562 headers.insert( QStringLiteral( "Authorization" ),
563 QStringLiteral( "Bearer %1" ).arg( QString::fromStdString( tileAccessJson["accessToken"].get<std::string>() ) ) );
564 }
565 }
566
567 QgsDataSourceUri finalUri;
568 finalUri.setParam( "url", tileSetUri + "layer.json" );
569 finalUri.setHttpHeaders( headers );
570 return finalUri.encodedUri();
571}
572
574QgsQuantizedMeshDataProvider::capabilities() const
575{
577}
578QgsTiledSceneDataProvider *QgsQuantizedMeshDataProvider::clone() const
579{
580 return new QgsQuantizedMeshDataProvider( mUri, mProviderOptions );
581}
583QgsQuantizedMeshDataProvider::sceneCrs() const
584{
585 return mMetadata->mCrs;
586}
588QgsQuantizedMeshDataProvider::boundingVolume() const
589{
590 return mMetadata->mBoundingVolume;
591}
592
593QgsTiledSceneIndex QgsQuantizedMeshDataProvider::index() const
594{
595 if ( !mIndex )
596 // Return dummy index, this provider is likely not valid
597 return QgsTiledSceneIndex( nullptr );
598 return *mIndex;
599}
600
601QgsDoubleRange QgsQuantizedMeshDataProvider::zRange() const
602{
603 return mMetadata->dummyZRange;
604}
605QgsCoordinateReferenceSystem QgsQuantizedMeshDataProvider::crs() const
606{
607 return mMetadata->mCrs;
608}
609QgsRectangle QgsQuantizedMeshDataProvider::extent() const
610{
611 return mMetadata->mExtent;
612}
613bool QgsQuantizedMeshDataProvider::isValid() const { return mIsValid; }
614QString QgsQuantizedMeshDataProvider::name() const { return providerName; }
615QString QgsQuantizedMeshDataProvider::description() const { return providerDescription; }
616
617const QgsQuantizedMeshMetadata &QgsQuantizedMeshDataProvider::quantizedMeshMetadata() const
618{
619 return *mMetadata;
620}
621
622QgsQuantizedMeshProviderMetadata::QgsQuantizedMeshProviderMetadata()
623 : QgsProviderMetadata( QgsQuantizedMeshDataProvider::providerName,
624 QgsQuantizedMeshDataProvider::providerDescription ) {}
625
626QgsDataProvider *QgsQuantizedMeshProviderMetadata::createProvider(
627 const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions,
629{
630 return new QgsQuantizedMeshDataProvider( uri, providerOptions, flags );
631}
632
QFlags< TiledSceneProviderCapability > TiledSceneProviderCapabilities
Tiled scene data provider capabilities.
Definition qgis.h:5229
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:450
TileChildrenAvailability
Possible availability states for a tile's children.
Definition qgis.h:5266
@ Available
Tile children are already available.
@ NoChildren
Tile is known to have no children.
@ Z
Z-axis.
static QgsTileDownloadManager * tileDownloadManager()
Returns the application's tile download manager, used for download of map tiles when rendering.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
QString errorMessage() const
Returns the error message string, after a get(), post(), head() or put() request has been made.
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...
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:394
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.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
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.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
QgsHttpHeaders httpHeaders() const
Returns http headers.
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.
void setHttpHeaders(const QgsHttpHeaders &headers)
Sets headers to headers.
QgsRange which stores a range of double values.
Definition qgsrange.h:231
QgsErrorMessage represents single error message.
Definition qgserror.h:33
A container for error messages.
Definition qgserror.h:81
void append(const QString &message, const QString &tag)
Append new error message.
Definition qgserror.cpp:39
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
void canceled()
Internal routines can connect to this signal if they use event loop.
This class implements simple http header management.
void insert(const QString &key, const QVariant &value)
insert a key with the specific value
A simple 4x4 matrix implementation useful for transformation in 3D space.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
QgsBox3D extent() const
Returns the overall bounding box of the object.
static QgsOrientedBox3D fromBox3D(const QgsBox3D &box)
Constructs an oriented box from an axis-aligned bounding box.
A class to represent a 2D point.
Definition qgspointxy.h:60
Holds data provider key, description, and associated shared library file or function pointer informat...
A rectangle specified with double values.
void finished()
Emitted when the reply has finished (either with a success or with a failure)
static QgsTileMatrix fromTileMatrix(int zoomLevel, const QgsTileMatrix &tileMatrix)
Returns a tile matrix based on another one.
Definition qgstiles.cpp:61
static QgsTileMatrix fromCustomDef(int zoomLevel, const QgsCoordinateReferenceSystem &crs, const QgsPointXY &z0TopLeftPoint, double z0Dimension, int z0MatrixWidth=1, int z0MatrixHeight=1)
Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units.
Definition qgstiles.cpp:31
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
int zoomLevel() const
Returns tile's zoom level (Z)
Definition qgstiles.h:53
int column() const
Returns tile's column index (X)
Definition qgstiles.h:49
int row() const
Returns tile's row index (Y)
Definition qgstiles.h:51
Represents a bounding volume for a tiled scene.
Base class for data providers for QgsTiledSceneLayer.
An index for tiled scene data providers.
Tiled scene data request.
QgsOrientedBox3D filterBox() const
Returns the box from which data will be taken.
long long parentTileId() const
Returns the parent tile ID, if filtering is limited to children of a specific tile.
double requiredGeometricError() const
Returns the required geometric error threshold for the returned tiles, in meters.
Represents an individual tile from a tiled scene data source.
static QString formatXYZUrlTemplate(const QString &url, QgsTileXYZ tile, const QgsTileMatrix &tileMatrix)
Returns formatted tile URL string replacing {x}, {y}, {z} placeholders (or {-y} instead of {y} for TM...
#define QgsDebugError(str)
Definition qgslogger.h:38
#define QgsSetRequestInitiatorClass(request, _class)
Setting options for creating vector data providers.