QGIS API Documentation 3.41.0-Master (3440c17df1d)
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().constData();
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 const QgsBlockingNetworkRequest::ErrorCode respCode = request.get( requestData );
95 {
96 error.append(
97 QObject::tr( "Failed to retrieve quantized mesh tiles metadata: %1" )
98 .arg( request.errorMessage() ) );
99 return;
100 }
101 const QByteArray 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 const QString 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 mBoundingVolume =
140 QgsBox3D(
141 mExtent.xMinimum(), mExtent.yMinimum(), dummyZRange.lower(),
142 mExtent.xMaximum(), mExtent.yMaximum(), dummyZRange.upper() ) );
143
144 // The TileJSON spec uses "scheme", but some real-world datasets use "schema"
145 if ( replyJson.find( "scheme" ) != replyJson.end() )
146 mTileScheme = QString::fromStdString( jsonGet<std::string>( replyJson, "scheme" ) );
147 else if ( replyJson.find( "schema" ) != replyJson.end() )
148 mTileScheme = QString::fromStdString( jsonGet<std::string>( replyJson, "schema" ) );
149 else throw MissingFieldException( "scheme/schema" );
150
151 for ( auto &aabbs : replyJson.at( "available" ) )
152 {
153 QVector<QgsTileRange> tileRanges;
154 for ( auto &aabb : aabbs )
155 {
156 tileRanges.push_back(
158 jsonGet<int>( aabb, "startX" ), jsonGet<int>( aabb, "endX" ),
159 jsonGet<int>( aabb, "startY" ), jsonGet<int>( aabb, "endY" ) ) );
160 }
161 mAvailableTiles.push_back( tileRanges );
162 }
163
164 try
165 {
166 mMinZoom = jsonGet<uint8_t>( replyJson, "minzoom" );
167 mMaxZoom = jsonGet<uint8_t>( replyJson, "maxzoom" );
168 }
169 catch ( MissingFieldException & )
170 {
171 mMinZoom = 0;
172 mMaxZoom = mAvailableTiles.size() - 1;
173 }
174
175 QString versionStr =
176 QString::fromStdString( jsonGet<std::string>( replyJson, "version" ) );
177 for ( auto &urlStr : jsonGet<std::vector<std::string>>( replyJson, "tiles" ) )
178 {
179 QUrl url = metadataUrl.resolved( QString::fromStdString( urlStr ) );
180 mTileUrls.push_back(
181 url.toString( QUrl::DecodeReserved ).replace( "{version}", versionStr ) );
182 }
183
184 int rootTileCount = 1;
185 if ( crsString == QLatin1String( "EPSG:4326" ) )
186 rootTileCount = 2;
187 else if ( crsString != QLatin1String( "EPSG:3857" ) )
188 error.append( QObject::tr( "Unhandled CRS: %1" ).arg( crsString ) );
189
190 QgsCoordinateReferenceSystem wgs84( QStringLiteral( "EPSG:4326" ) );
191 // Bounds of tile schema in projected coordinates
192 const QgsRectangle crsBounds =
193 QgsCoordinateTransform( wgs84, mCrs, transformContext )
194 .transform( mCrs.bounds() );
195 QgsPointXY topLeft( crsBounds.xMinimum(), crsBounds.yMaximum() );
196 double z0TileSize = crsBounds.height();
197
198 mTileMatrix = QgsTileMatrix::fromCustomDef( 0, mCrs, topLeft, z0TileSize, rootTileCount, 1 );
199 }
200 catch ( nlohmann::json::exception &ex )
201 {
202 error.append( QObject::tr( "Error parsing JSON metadata: %1" ).arg( ex.what() ) );
203 }
204 catch ( MissingFieldException &ex )
205 {
206 error.append( QObject::tr( "Error parsing JSON metadata: %1" ).arg( ex.what() ) );
207 }
208}
209
210const QgsDoubleRange QgsQuantizedMeshMetadata::dummyZRange = {-10000, 10000};
211
212static QgsTileXYZ tileToTms( QgsTileXYZ &xyzTile )
213{
214 // Flip Y axis for TMS schema
215 Q_ASSERT( xyzTile.zoomLevel() >= 0 );
216 return {xyzTile.column(),
217 ( 1 << xyzTile.zoomLevel() ) - xyzTile.row() - 1,
218 xyzTile.zoomLevel()};
219}
220
221bool QgsQuantizedMeshMetadata::containsTile( QgsTileXYZ tile ) const
222{
223 if ( tile.zoomLevel() < mMinZoom || tile.zoomLevel() > mMaxZoom ||
224 tile.zoomLevel() >= mAvailableTiles.size() )
225 return false;
226 // We operate with XYZ-style tile coordinates, but the availability array may
227 // be given in TMS-style
228 if ( mTileScheme == QLatin1String( "tms" ) )
229 tile = tileToTms( tile );
230 for ( const QgsTileRange &range : mAvailableTiles[tile.zoomLevel()] )
231 {
232 if ( range.startColumn() <= tile.column() && range.endColumn() >= tile.column() &&
233 range.startRow() <= tile.row() && range.endRow() >= tile.row() )
234 return true;
235 }
236 return false;
237}
238
239double QgsQuantizedMeshMetadata::geometricErrorAtZoom( int zoom ) const
240{
241 // The specification doesn't mandate any precision, we can only make a guess
242 // based on each tile's maximum possible numerical precision and some
243 // reasonable-looking constant.
244 return 400000 / pow( 2, zoom );
245}
246
247long long QgsQuantizedMeshIndex::encodeTileId( QgsTileXYZ tile )
248{
249 if ( tile.zoomLevel() == -1 )
250 {
251 Q_ASSERT( tile.column() == 0 && tile.row() == 0 );
252 return ROOT_TILE_ID;
253 }
254 Q_ASSERT( tile.zoomLevel() < ( 2 << 4 ) && ( tile.column() < ( 2 << 27 ) ) &&
255 ( tile.row() < ( 2 << 27 ) ) );
256 return tile.row() | ( ( long long )tile.column() << 28 ) |
257 ( ( long long )tile.zoomLevel() << 56 ) | ( ( long long ) 1 << 61 );
258}
259
260QgsTileXYZ QgsQuantizedMeshIndex::decodeTileId( long long id )
261{
262 if ( id == ROOT_TILE_ID )
263 return QgsTileXYZ( 0, 0, -1 );
264
265 Q_ASSERT( id >> 61 == 1 ); // Check reserved bits
266 return QgsTileXYZ(
267 ( int )( ( id >> 28 ) & ( ( 2 << 27 ) - 1 ) ),
268 ( int )( id & ( ( 2 << 27 ) - 1 ) ),
269 ( int )( ( id >> 56 ) & ( ( 2 << 4 ) - 1 ) ) );
270}
271
272QgsTiledSceneTile QgsQuantizedMeshIndex::rootTile() const
273{
274 // Returns virtual tile to paper over tiling schemes which have >1 tile at zoom 0
275 QgsTiledSceneTile tile = QgsTiledSceneTile( ROOT_TILE_ID );
276 const QgsRectangle bounds = mWgs84ToCrs.transform( mMetadata.mCrs.bounds() );
279 QgsBox3D( bounds, mMetadata.dummyZRange.lower(), mMetadata.dummyZRange.upper() ) ) );
280 tile.setGeometricError( std::numeric_limits<double>::max() );
281 return tile;
282}
283long long QgsQuantizedMeshIndex::parentTileId( long long id ) const
284{
285 if ( id == ROOT_TILE_ID )
286 return -1;
287 const QgsTileXYZ tile = decodeTileId( id );
288 if ( tile.zoomLevel() == 0 )
289 return ROOT_TILE_ID;
290 return encodeTileId( {tile.zoomLevel() - 1, tile.column() / 2, tile.row() / 2} );
291}
292QVector<long long> QgsQuantizedMeshIndex::childTileIds( long long id ) const
293{
294 const QgsTileXYZ tile = decodeTileId( id );
295 QVector<long long> children;
296 const int x = tile.column();
297 const int y = tile.row();
298 const int zoom = tile.zoomLevel();
299
300 if ( mMetadata.containsTile( {x * 2, y * 2, zoom + 1} ) )
301 children.push_back( encodeTileId( {x * 2, y * 2, zoom + 1} ) );
302 if ( mMetadata.containsTile( {x * 2 + 1, y * 2, zoom + 1} ) )
303 children.push_back( encodeTileId( {x * 2 + 1, y * 2, zoom + 1} ) );
304 if ( mMetadata.containsTile( {x * 2, y * 2 + 1, zoom + 1} ) )
305 children.push_back( encodeTileId( {x * 2, y * 2 + 1, zoom + 1} ) );
306 if ( mMetadata.containsTile( {x * 2 + 1, y * 2 + 1, zoom + 1} ) )
307 children.push_back( encodeTileId( {x * 2 + 1, y * 2 + 1, zoom + 1} ) );
308
309 return children;
310}
311QgsTiledSceneTile QgsQuantizedMeshIndex::getTile( long long id )
312{
313 QgsTileXYZ xyzTile = decodeTileId( id );
314 QgsTiledSceneTile sceneTile( id );
315
316 const QgsTileMatrix zoomedMatrix = QgsTileMatrix::fromTileMatrix( xyzTile.zoomLevel(), mMetadata.mTileMatrix );
317 const QgsRectangle tileExtent = zoomedMatrix.tileExtent( xyzTile );
318
319 sceneTile.setBoundingVolume(
321 QgsBox3D( tileExtent, mMetadata.dummyZRange.lower(), mMetadata.dummyZRange.upper() ) ) );
322 sceneTile.setGeometricError( mMetadata.geometricErrorAtZoom( xyzTile.zoomLevel() ) );
323
324 if ( id == ROOT_TILE_ID )
325 // The root tile is fictitious and has no content, don't bother pointing to any.
326 return sceneTile;
327
328 if ( mMetadata.mTileScheme == QLatin1String( "tms" ) )
329 xyzTile = tileToTms( xyzTile );
330
331 if ( mMetadata.mTileUrls.size() == 0 )
332 {
333 QgsDebugError( "Quantized Mesh metadata has no URLs for tiles" );
334 }
335 else
336 {
337 // TODO: Intelligently choose from alternatives. Round robin?
338 const QString tileUri = QgsVectorTileUtils::formatXYZUrlTemplate(
339 mMetadata.mTileUrls[0], xyzTile, zoomedMatrix );
340 sceneTile.setResources( {{"content", tileUri}} );
341 sceneTile.setMetadata(
342 {
343 {QStringLiteral( "gltfUpAxis" ), static_cast<int>( Qgis::Axis::Z )},
344 {QStringLiteral( "contentFormat" ), QStringLiteral( "quantizedmesh" )},
345 } );
346 }
347
348 // Tile meshes have 0.0 -- 1.0 coordinates. Rescale them to the tile's real
349 // width and height in our CRS and move the tile to its position.
350 QgsMatrix4x4 transform(
351 tileExtent.width(), 0, 0, tileExtent.xMinimum(),
352 0, tileExtent.height(), 0, tileExtent.yMinimum(),
353 0, 0, 1, 0,
354 0, 0, 0, 1 );
355 sceneTile.setTransform( transform );
356
357 return sceneTile;
358}
359QVector<long long>
360QgsQuantizedMeshIndex::getTiles( const QgsTiledSceneRequest &request )
361{
362 uint8_t zoomLevel = mMetadata.mMinZoom;
363 if ( request.requiredGeometricError() != 0 )
364 {
365 while ( zoomLevel < mMetadata.mMaxZoom &&
366 mMetadata.geometricErrorAtZoom( zoomLevel ) > request.requiredGeometricError() )
367 zoomLevel++;
368 }
369 const QgsTileMatrix tileMatrix = QgsTileMatrix::fromTileMatrix( zoomLevel, mMetadata.mTileMatrix );
370
371 QVector<long long> ids;
372 // We can only filter on X and Y
373 const QgsRectangle extent = request.filterBox().extent().toRectangle();
374 if ( request.parentTileId() != -1 )
375 {
376 const QgsTileXYZ parentTile = decodeTileId( request.parentTileId() );
377 extent.intersect( tileMatrix.tileExtent( parentTile ) );
378 }
379
380 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( extent );
381 if ( !tileRange.isValid() )
382 return {};
383
384 for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); col++ )
385 for ( int row = tileRange.startRow(); row <= tileRange.endRow(); row++ )
386 {
387 const QgsTileXYZ xyzTile = QgsTileXYZ( col, row, zoomLevel );
388 if ( mMetadata.containsTile( xyzTile ) )
389 ids.push_back( encodeTileId( xyzTile ) );
390 }
391
392 return ids;
393}
395QgsQuantizedMeshIndex::childAvailability( long long id ) const
396{
397 const QVector<long long> childIds = childTileIds( id );
398 if ( childIds.count() == 0 )
401}
402bool QgsQuantizedMeshIndex::fetchHierarchy( long long id, QgsFeedback *feedback )
403{
404 // The API was built for Cesium 3D tiles, which have tiles (actual files with
405 // metadata) as nodes of a hierarchy tree, with the actual data in children
406 // of those tiles. For us, tiles are represented by int IDs, so they don't
407 // need to be fetched.
408 Q_UNUSED( id );
409 Q_UNUSED( feedback );
410 return true;
411}
412
413QByteArray QgsQuantizedMeshIndex::fetchContent( const QString &uri,
414 QgsFeedback *feedback )
415{
416 QNetworkRequest requestData( uri );
417 requestData.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
418 requestData.setRawHeader( "Accept", "application/vnd.quantized-mesh,application/octet-stream;q=0.9" );
419 mMetadata.mHeaders.updateNetworkRequest( requestData );
420 if ( !mMetadata.mAuthCfg.isEmpty() )
421 QgsApplication::authManager()->updateNetworkRequest( requestData, mMetadata.mAuthCfg );
422 QgsSetRequestInitiatorClass( requestData,
423 QStringLiteral( "QgsQuantizedMeshIndex" ) );
424
425 std::unique_ptr<QgsTileDownloadManagerReply> reply( QgsApplication::tileDownloadManager()->get( requestData ) );
426
427 QEventLoop loop;
428 if ( feedback )
429 QObject::connect( feedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );
430 QObject::connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit );
431 loop.exec();
432
433 if ( reply->error() != QNetworkReply::NoError )
434 {
435 QgsDebugError( QStringLiteral( "Request failed (%1): %2" ).arg( uri ).arg( reply->errorString() ) );
436 return {};
437 }
438 return reply->data();
439}
440
441QgsQuantizedMeshDataProvider::QgsQuantizedMeshDataProvider(
442 const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions,
444 : QgsTiledSceneDataProvider( uri, providerOptions, flags ), mUri( uri ),
445 mProviderOptions( providerOptions )
446{
447 if ( uri.startsWith( QLatin1String( "ion://" ) ) )
448 {
449 QString updatedUri = uriFromIon( uri );
450 mMetadata = QgsQuantizedMeshMetadata( updatedUri, transformContext(), mError );
451 }
452 else
453 {
454 mMetadata = QgsQuantizedMeshMetadata( uri, transformContext(), mError );
455 }
456
457 if ( mError.isEmpty() )
458 {
459 QgsCoordinateReferenceSystem wgs84( QStringLiteral( "EPSG:4326" ) );
460 QgsCoordinateTransform wgs84ToCrs( wgs84, mMetadata->mCrs, transformContext() );
461 mIndex.emplace( new QgsQuantizedMeshIndex( *mMetadata, wgs84ToCrs ) );
462 mIsValid = true;
463 }
464}
465
466QString QgsQuantizedMeshDataProvider::uriFromIon( const QString &uri )
467{
468 // we expect one of the two options:
469 // ion://?assetId=123&accessToken=xyz
470 // ion://?assetId=123&authcfg=abc
471
472 QUrl url( uri );
473 const QString assetId = QUrlQuery( url ).queryItemValue( QStringLiteral( "assetId" ) );
474 const QString accessToken = QUrlQuery( url ).queryItemValue( QStringLiteral( "accessToken" ) );
475
476 const QString CESIUM_ION_URL = QStringLiteral( "https://api.cesium.com/" );
477
478 QgsDataSourceUri dsUri;
479 dsUri.setEncodedUri( uri );
480 QString authCfg = dsUri.authConfigId();
481 QgsHttpHeaders headers = dsUri.httpHeaders();
482
483 // get asset info
484 {
485 const QString assetInfoEndpoint = CESIUM_ION_URL + QStringLiteral( "v1/assets/%1" ).arg( assetId );
486 QNetworkRequest request = QNetworkRequest( assetInfoEndpoint );
487 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsQuantizedMeshDataProvider" ) )
488 headers.updateNetworkRequest( request );
489 if ( !accessToken.isEmpty() )
490 request.setRawHeader( "Authorization", QStringLiteral( "Bearer %1" ).arg( accessToken ).toLocal8Bit() );
491
492 QgsBlockingNetworkRequest networkRequest;
493 if ( accessToken.isEmpty() )
494 networkRequest.setAuthCfg( authCfg );
495
496 switch ( networkRequest.get( request ) )
497 {
499 break;
500
504 // TODO -- error reporting
505 return QString();
506 }
507
508 const QgsNetworkReplyContent content = networkRequest.reply();
509 const json assetInfoJson = json::parse( content.content().toStdString() );
510 if ( assetInfoJson["type"] != "TERRAIN" )
511 {
512 appendError( QgsErrorMessage( tr( "Only ion TERRAIN content can be accessed, not %1" ).arg( QString::fromStdString( assetInfoJson["type"].get<std::string>() ) ) ) );
513 return QString();
514 }
515 }
516
517 // get tileset access details
518 QString tileSetUri;
519 {
520 const QString tileAccessEndpoint = CESIUM_ION_URL + QStringLiteral( "v1/assets/%1/endpoint" ).arg( assetId );
521 QNetworkRequest request = QNetworkRequest( tileAccessEndpoint );
522 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsQuantizedMeshDataProvider" ) )
523 headers.updateNetworkRequest( request );
524 if ( !accessToken.isEmpty() )
525 request.setRawHeader( "Authorization", QStringLiteral( "Bearer %1" ).arg( accessToken ).toLocal8Bit() );
526
527 QgsBlockingNetworkRequest networkRequest;
528 if ( accessToken.isEmpty() )
529 networkRequest.setAuthCfg( authCfg );
530
531 switch ( networkRequest.get( request ) )
532 {
534 break;
535
539 // TODO -- error reporting
540 return QString();
541 }
542
543 const QgsNetworkReplyContent content = networkRequest.reply();
544 const json tileAccessJson = json::parse( content.content().toStdString() );
545
546 if ( tileAccessJson.contains( "url" ) )
547 {
548 tileSetUri = QString::fromStdString( tileAccessJson["url"].get<std::string>() );
549 }
550 else if ( tileAccessJson.contains( "options" ) )
551 {
552 const auto &optionsJson = tileAccessJson["options"];
553 if ( optionsJson.contains( "url" ) )
554 {
555 tileSetUri = QString::fromStdString( optionsJson["url"].get<std::string>() );
556 }
557 }
558
559 if ( tileAccessJson.contains( "accessToken" ) )
560 {
561 // The tileset accessToken is NOT the same as the token we use to access the asset details -- ie we can't
562 // use the same authentication as we got from the providers auth cfg!
563 headers.insert( QStringLiteral( "Authorization" ),
564 QStringLiteral( "Bearer %1" ).arg( QString::fromStdString( tileAccessJson["accessToken"].get<std::string>() ) ) );
565 }
566 }
567
568 QgsDataSourceUri finalUri;
569 finalUri.setParam( "url", tileSetUri + "layer.json" );
570 finalUri.setHttpHeaders( headers );
571 return finalUri.encodedUri();
572}
573
575QgsQuantizedMeshDataProvider::capabilities() const
576{
578}
579QgsTiledSceneDataProvider *QgsQuantizedMeshDataProvider::clone() const
580{
581 return new QgsQuantizedMeshDataProvider( mUri, mProviderOptions );
582}
584QgsQuantizedMeshDataProvider::sceneCrs() const
585{
586 return mMetadata->mCrs;
587}
589QgsQuantizedMeshDataProvider::boundingVolume() const
590{
591 return mMetadata->mBoundingVolume;
592}
593
594QgsTiledSceneIndex QgsQuantizedMeshDataProvider::index() const
595{
596 if ( !mIndex )
597 // Return dummy index, this provider is likely not valid
598 return QgsTiledSceneIndex( nullptr );
599 return *mIndex;
600}
601
602QgsDoubleRange QgsQuantizedMeshDataProvider::zRange() const
603{
604 return mMetadata->dummyZRange;
605}
606QgsCoordinateReferenceSystem QgsQuantizedMeshDataProvider::crs() const
607{
608 return mMetadata->mCrs;
609}
610QgsRectangle QgsQuantizedMeshDataProvider::extent() const
611{
612 return mMetadata->mExtent;
613}
614bool QgsQuantizedMeshDataProvider::isValid() const { return mIsValid; }
615QString QgsQuantizedMeshDataProvider::name() const { return providerName; }
616QString QgsQuantizedMeshDataProvider::description() const { return providerDescription; }
617
618const QgsQuantizedMeshMetadata &QgsQuantizedMeshDataProvider::quantizedMeshMetadata() const
619{
620 return *mMetadata;
621}
622
623QgsQuantizedMeshProviderMetadata::QgsQuantizedMeshProviderMetadata()
624 : QgsProviderMetadata( QgsQuantizedMeshDataProvider::providerName,
625 QgsQuantizedMeshDataProvider::providerDescription ) {}
626
627QgsDataProvider *QgsQuantizedMeshProviderMetadata::createProvider(
628 const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions,
630{
631 return new QgsQuantizedMeshDataProvider( uri, providerOptions, flags );
632}
633
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.
double xMinimum
double yMinimum
double yMaximum
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
void finished()
Emitted when the reply has finished (either with a success or with a failure)
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition qgstiles.h:136
QgsRectangle tileExtent(QgsTileXYZ id) const
Returns extent of the given tile in this matrix.
Definition qgstiles.cpp:81
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition qgstiles.cpp:97
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
int endColumn() const
Returns index of the last column in the range.
Definition qgstiles.h:111
int endRow() const
Returns index of the last row in the range.
Definition qgstiles.h:115
int startRow() const
Returns index of the first row in the range.
Definition qgstiles.h:113
int startColumn() const
Returns index of the first column in the range.
Definition qgstiles.h:109
bool isValid() const
Returns whether the range is valid (when all row/column numbers are not negative)
Definition qgstiles.h:106
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.
void setGeometricError(double error)
Sets the tile's geometric error, which is the error, in meters, of the tile's simplified representati...
void setBoundingVolume(const QgsTiledSceneBoundingVolume &volume)
Sets the bounding volume for the tile.
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.