QGIS API Documentation 3.99.0-Master (09f76ad7019)
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
21#include <limits>
22#include <nlohmann/json.hpp>
23
24#include "qgsapplication.h"
25#include "qgsauthmanager.h"
30#include "qgslogger.h"
31#include "qgsmatrix4x4.h"
32#include "qgsorientedbox3d.h"
33#include "qgsprovidermetadata.h"
37#include "qgstiledsceneindex.h"
39#include "qgstiledscenetile.h"
40#include "qgstiles.h"
41#include "qgsvectortileutils.h"
42
43#include <QString>
44#include <QUrlQuery>
45#include <qglobal.h>
46#include <qnetworkrequest.h>
47#include <qobject.h>
48#include <qstringliteral.h>
49#include <qvector.h>
50
51#include "moc_qgsquantizedmeshdataprovider.cpp"
52
53using namespace Qt::StringLiterals;
54
56
57class MissingFieldException : public std::exception
58{
59 public:
60 MissingFieldException( const char *field ) : mField( field ) { }
61 const char *what() const noexcept override
62 {
63 return QString( "Missing field: %1" ).arg( mField ).toLocal8Bit().constData();
64 }
65 private:
66 const char *mField;
67};
68
69template <typename T>
70static T jsonGet( nlohmann::json &json, const char *idx )
71{
72 auto &obj = json[idx];
73 if ( obj.is_null() )
74 {
75 throw MissingFieldException( idx );
76 }
77 return obj.get<T>();
78}
79
80
81QgsQuantizedMeshMetadata::QgsQuantizedMeshMetadata(
82 const QString &uri,
83 const QgsCoordinateTransformContext &transformContext,
84 QgsError &error )
85{
86 QgsDataSourceUri dsUri;
87 dsUri.setEncodedUri( uri );
88 mAuthCfg = dsUri.authConfigId();
89 mHeaders = dsUri.httpHeaders();
90
91 // The provided URL should be the metadata JSON's location
92 QUrl metadataUrl = dsUri.param( "url" );
93 QNetworkRequest requestData( metadataUrl );
94 mHeaders.updateNetworkRequest( requestData );
95 QgsSetRequestInitiatorClass( requestData,
96 u"QgsQuantizedMeshDataProvider"_s );
98 if ( !mAuthCfg.isEmpty() )
99 request.setAuthCfg( mAuthCfg );
100 const QgsBlockingNetworkRequest::ErrorCode respCode = request.get( requestData );
102 {
103 error.append(
104 QObject::tr( "Failed to retrieve quantized mesh tiles metadata: %1" )
105 .arg( request.errorMessage() ) );
106 return;
107 }
108 const QByteArray reply = request.reply().content();
109
110 try
111 {
112 auto replyJson = nlohmann::json::parse( reply.data() );
113
114 // The metadata is an (undocumented) variant of TileJSON
115 if ( jsonGet<std::string>( replyJson, "format" ) != "quantized-mesh-1.0" )
116 {
117 error.append( QObject::tr( "Unexpected tile format: %1" )
118 .arg( replyJson["format"].dump().c_str() ) );
119 return;
120 }
121
122 const QString crsString = QString::fromStdString( jsonGet<std::string>( replyJson, "projection" ) );
123 mCrs = QgsCoordinateReferenceSystem( crsString );
124 if ( !mCrs.isValid() )
125 {
126 error.append( QObject::tr( "Invalid CRS '%1'!" ).arg( crsString ) );
127 return;
128 }
129
130 try
131 {
132 std::vector<double> bounds = jsonGet<std::vector<double>>( replyJson, "bounds" );
133 if ( bounds.size() != 4 )
134 {
135 error.append( QObject::tr( "Bounds array doesn't have 4 items" ) );
136 return;
137 }
138 mExtent = QgsRectangle( bounds[0], bounds[1], bounds[2], bounds[3] );
139 }
140 catch ( MissingFieldException & )
141 {
142 mExtent = mCrs.bounds();
143 }
144
145 mBoundingVolume =
147 QgsBox3D(
148 mExtent.xMinimum(), mExtent.yMinimum(), dummyZRange.lower(),
149 mExtent.xMaximum(), mExtent.yMaximum(), dummyZRange.upper() ) );
150
151 // The TileJSON spec uses "scheme", but some real-world datasets use "schema"
152 if ( replyJson.find( "scheme" ) != replyJson.end() )
153 mTileScheme = QString::fromStdString( jsonGet<std::string>( replyJson, "scheme" ) );
154 else if ( replyJson.find( "schema" ) != replyJson.end() )
155 mTileScheme = QString::fromStdString( jsonGet<std::string>( replyJson, "schema" ) );
156 else throw MissingFieldException( "scheme/schema" );
157
158 if ( replyJson.find( "available" ) != replyJson.end() )
159 {
160 for ( auto &aabbs : replyJson.at( "available" ) )
161 {
162 QVector<QgsTileRange> tileRanges;
163 for ( auto &aabb : aabbs )
164 {
165 tileRanges.push_back(
167 jsonGet<int>( aabb, "startX" ), jsonGet<int>( aabb, "endX" ),
168 jsonGet<int>( aabb, "startY" ), jsonGet<int>( aabb, "endY" ) ) );
169 }
170 mAvailableTiles.push_back( tileRanges );
171 }
172 }
173
174 try
175 {
176 mMinZoom = jsonGet<uint8_t>( replyJson, "minzoom" );
177 mMaxZoom = jsonGet<uint8_t>( replyJson, "maxzoom" );
178 }
179 catch ( MissingFieldException & )
180 {
181 mMinZoom = 0;
182 if ( mAvailableTiles.isEmpty() )
183 {
184 mMaxZoom = 10;
185 error.append( QObject::tr( "Missing max zoom or tile availability info" ) );
186 }
187 else
188 {
189 mMaxZoom = mAvailableTiles.size() - 1;
190 }
191 }
192
193 QString versionStr =
194 QString::fromStdString( jsonGet<std::string>( replyJson, "version" ) );
195 for ( auto &urlStr : jsonGet<std::vector<std::string>>( replyJson, "tiles" ) )
196 {
197 QUrl url = metadataUrl.resolved( QString::fromStdString( urlStr ) );
198 mTileUrls.push_back(
199 url.toString( QUrl::DecodeReserved ).replace( "{version}", versionStr ) );
200 }
201
202 int rootTileCount = 1;
203 if ( crsString == "EPSG:4326"_L1 )
204 rootTileCount = 2;
205 else if ( crsString != "EPSG:3857"_L1 )
206 error.append( QObject::tr( "Unhandled CRS: %1" ).arg( crsString ) );
207
208 QgsCoordinateReferenceSystem wgs84( u"EPSG:4326"_s );
209 // Bounds of tile schema in projected coordinates
210 const QgsRectangle crsBounds =
211 QgsCoordinateTransform( wgs84, mCrs, transformContext )
212 .transform( mCrs.bounds() );
213 QgsPointXY topLeft( crsBounds.xMinimum(), crsBounds.yMaximum() );
214 double z0TileSize = crsBounds.height();
215
216 mTileMatrix = QgsTileMatrix::fromCustomDef( 0, mCrs, topLeft, z0TileSize, rootTileCount, 1 );
217 }
218 catch ( nlohmann::json::exception &ex )
219 {
220 error.append( QObject::tr( "Error parsing JSON metadata: %1" ).arg( ex.what() ) );
221 }
222 catch ( MissingFieldException &ex )
223 {
224 error.append( QObject::tr( "Error parsing JSON metadata: %1" ).arg( ex.what() ) );
225 }
226}
227
228const QgsDoubleRange QgsQuantizedMeshMetadata::dummyZRange = {-10000, 10000};
229
230static QgsTileXYZ tileToTms( QgsTileXYZ &xyzTile )
231{
232 // Flip Y axis for TMS schema
233 Q_ASSERT( xyzTile.zoomLevel() >= 0 );
234 return {xyzTile.column(),
235 ( 1 << xyzTile.zoomLevel() ) - xyzTile.row() - 1,
236 xyzTile.zoomLevel()};
237}
238
239bool QgsQuantizedMeshMetadata::containsTile( QgsTileXYZ tile ) const
240{
241 if ( tile.zoomLevel() < mMinZoom || tile.zoomLevel() > mMaxZoom )
242 return false;
243 if ( mAvailableTiles.isEmpty() )
244 return true; // we have no information about tile availability - let's hope the tile is there!
245 if ( tile.zoomLevel() >= mAvailableTiles.size() )
246 return false;
247 // We operate with XYZ-style tile coordinates, but the availability array may
248 // be given in TMS-style
249 if ( mTileScheme == "tms"_L1 )
250 tile = tileToTms( tile );
251 for ( const QgsTileRange &range : mAvailableTiles[tile.zoomLevel()] )
252 {
253 if ( range.startColumn() <= tile.column() && range.endColumn() >= tile.column() &&
254 range.startRow() <= tile.row() && range.endRow() >= tile.row() )
255 return true;
256 }
257 return false;
258}
259
260double QgsQuantizedMeshMetadata::geometricErrorAtZoom( int zoom ) const
261{
262 // The specification doesn't mandate any precision, we can only make a guess
263 // based on each tile's maximum possible numerical precision and some
264 // reasonable-looking constant.
265 return 400000 / pow( 2, zoom );
266}
267
268long long QgsQuantizedMeshIndex::encodeTileId( QgsTileXYZ tile )
269{
270 if ( tile.zoomLevel() == -1 )
271 {
272 Q_ASSERT( tile.column() == 0 && tile.row() == 0 );
273 return ROOT_TILE_ID;
274 }
275 Q_ASSERT( tile.zoomLevel() < ( 2 << 4 ) && ( tile.column() < ( 2 << 27 ) ) &&
276 ( tile.row() < ( 2 << 27 ) ) );
277 return tile.row() | ( ( long long )tile.column() << 28 ) |
278 ( ( long long )tile.zoomLevel() << 56 ) | ( ( long long ) 1 << 61 );
279}
280
281QgsTileXYZ QgsQuantizedMeshIndex::decodeTileId( long long id )
282{
283 if ( id == ROOT_TILE_ID )
284 return QgsTileXYZ( 0, 0, -1 );
285
286 Q_ASSERT( id >> 61 == 1 ); // Check reserved bits
287 return QgsTileXYZ(
288 ( int )( ( id >> 28 ) & ( ( 2 << 27 ) - 1 ) ),
289 ( int )( id & ( ( 2 << 27 ) - 1 ) ),
290 ( int )( ( id >> 56 ) & ( ( 2 << 4 ) - 1 ) ) );
291}
292
293QgsTiledSceneTile QgsQuantizedMeshIndex::rootTile() const
294{
295 // Returns virtual tile to paper over tiling schemes which have >1 tile at zoom 0
296 QgsTiledSceneTile tile = QgsTiledSceneTile( ROOT_TILE_ID );
297 const QgsRectangle bounds = mWgs84ToCrs.transform( mMetadata.mCrs.bounds() );
300 QgsBox3D( bounds, mMetadata.dummyZRange.lower(), mMetadata.dummyZRange.upper() ) ) );
301 tile.setGeometricError( std::numeric_limits<double>::max() );
302 return tile;
303}
304long long QgsQuantizedMeshIndex::parentTileId( long long id ) const
305{
306 if ( id == ROOT_TILE_ID )
307 return -1;
308 const QgsTileXYZ tile = decodeTileId( id );
309 if ( tile.zoomLevel() == 0 )
310 return ROOT_TILE_ID;
311 return encodeTileId( {tile.zoomLevel() - 1, tile.column() / 2, tile.row() / 2} );
312}
313QVector<long long> QgsQuantizedMeshIndex::childTileIds( long long id ) const
314{
315 const QgsTileXYZ tile = decodeTileId( id );
316 QVector<long long> children;
317 const int x = tile.column();
318 const int y = tile.row();
319 const int zoom = tile.zoomLevel();
320
321 if ( mMetadata.containsTile( {x * 2, y * 2, zoom + 1} ) )
322 children.push_back( encodeTileId( {x * 2, y * 2, zoom + 1} ) );
323 if ( mMetadata.containsTile( {x * 2 + 1, y * 2, zoom + 1} ) )
324 children.push_back( encodeTileId( {x * 2 + 1, y * 2, zoom + 1} ) );
325 if ( mMetadata.containsTile( {x * 2, y * 2 + 1, zoom + 1} ) )
326 children.push_back( encodeTileId( {x * 2, y * 2 + 1, zoom + 1} ) );
327 if ( mMetadata.containsTile( {x * 2 + 1, y * 2 + 1, zoom + 1} ) )
328 children.push_back( encodeTileId( {x * 2 + 1, y * 2 + 1, zoom + 1} ) );
329
330 return children;
331}
332QgsTiledSceneTile QgsQuantizedMeshIndex::getTile( long long id )
333{
334 QgsTileXYZ xyzTile = decodeTileId( id );
335 QgsTiledSceneTile sceneTile( id );
336
337 const QgsTileMatrix zoomedMatrix = QgsTileMatrix::fromTileMatrix( xyzTile.zoomLevel(), mMetadata.mTileMatrix );
338 const QgsRectangle tileExtent = zoomedMatrix.tileExtent( xyzTile );
339
340 sceneTile.setBoundingVolume(
342 QgsBox3D( tileExtent, mMetadata.dummyZRange.lower(), mMetadata.dummyZRange.upper() ) ) );
343 sceneTile.setGeometricError( mMetadata.geometricErrorAtZoom( xyzTile.zoomLevel() ) );
344
345 if ( id == ROOT_TILE_ID )
346 // The root tile is fictitious and has no content, don't bother pointing to any.
347 return sceneTile;
348
349 if ( mMetadata.mTileScheme == "tms"_L1 )
350 xyzTile = tileToTms( xyzTile );
351
352 if ( mMetadata.mTileUrls.size() == 0 )
353 {
354 QgsDebugError( "Quantized Mesh metadata has no URLs for tiles" );
355 }
356 else
357 {
358 // TODO: Intelligently choose from alternatives. Round robin?
359 const QString tileUri = QgsVectorTileUtils::formatXYZUrlTemplate(
360 mMetadata.mTileUrls[0], xyzTile, zoomedMatrix );
361 sceneTile.setResources( {{"content", tileUri}} );
362 sceneTile.setMetadata(
363 {
364 {u"gltfUpAxis"_s, static_cast<int>( Qgis::Axis::Z )},
365 {u"contentFormat"_s, u"quantizedmesh"_s},
366 } );
367 }
368
369 // Tile meshes have 0.0 -- 1.0 coordinates. Rescale them to the tile's real
370 // width and height in our CRS and move the tile to its position.
371 QgsMatrix4x4 transform(
372 tileExtent.width(), 0, 0, tileExtent.xMinimum(),
373 0, tileExtent.height(), 0, tileExtent.yMinimum(),
374 0, 0, 1, 0,
375 0, 0, 0, 1 );
376 sceneTile.setTransform( transform );
377
378 return sceneTile;
379}
380QVector<long long>
381QgsQuantizedMeshIndex::getTiles( const QgsTiledSceneRequest &request )
382{
383 uint8_t zoomLevel = mMetadata.mMinZoom;
384 if ( request.requiredGeometricError() != 0 )
385 {
386 while ( zoomLevel < mMetadata.mMaxZoom &&
387 mMetadata.geometricErrorAtZoom( zoomLevel ) > request.requiredGeometricError() )
388 zoomLevel++;
389 }
390 const QgsTileMatrix tileMatrix = QgsTileMatrix::fromTileMatrix( zoomLevel, mMetadata.mTileMatrix );
391
392 QVector<long long> ids;
393 // We can only filter on X and Y
394 QgsRectangle extent = request.filterBox().extent().toRectangle();
395 if ( request.parentTileId() != -1 )
396 {
397 const QgsTileXYZ parentTile = decodeTileId( request.parentTileId() );
398 extent = extent.intersect( tileMatrix.tileExtent( parentTile ) );
399 }
400
401 const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( extent );
402 if ( !tileRange.isValid() )
403 return {};
404
405 for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); col++ )
406 for ( int row = tileRange.startRow(); row <= tileRange.endRow(); row++ )
407 {
408 const QgsTileXYZ xyzTile = QgsTileXYZ( col, row, zoomLevel );
409 if ( mMetadata.containsTile( xyzTile ) )
410 ids.push_back( encodeTileId( xyzTile ) );
411 }
412
413 return ids;
414}
416QgsQuantizedMeshIndex::childAvailability( long long id ) const
417{
418 const QVector<long long> childIds = childTileIds( id );
419 if ( childIds.count() == 0 )
422}
423bool QgsQuantizedMeshIndex::fetchHierarchy( long long id, QgsFeedback *feedback )
424{
425 // The API was built for Cesium 3D tiles, which have tiles (actual files with
426 // metadata) as nodes of a hierarchy tree, with the actual data in children
427 // of those tiles. For us, tiles are represented by int IDs, so they don't
428 // need to be fetched.
429 Q_UNUSED( id );
430 Q_UNUSED( feedback );
431 return true;
432}
433
434QByteArray QgsQuantizedMeshIndex::fetchContent( const QString &uri,
435 QgsFeedback *feedback )
436{
437 QNetworkRequest requestData( uri );
438 requestData.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
439 requestData.setRawHeader( "Accept", "application/vnd.quantized-mesh,application/octet-stream;q=0.9" );
440 mMetadata.mHeaders.updateNetworkRequest( requestData );
441 if ( !mMetadata.mAuthCfg.isEmpty() )
442 QgsApplication::authManager()->updateNetworkRequest( requestData, mMetadata.mAuthCfg );
443 QgsSetRequestInitiatorClass( requestData,
444 u"QgsQuantizedMeshIndex"_s );
445
446 std::unique_ptr<QgsTileDownloadManagerReply> reply( QgsApplication::tileDownloadManager()->get( requestData ) );
447
448 QEventLoop loop;
449 if ( feedback )
450 QObject::connect( feedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );
451 QObject::connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit );
452 loop.exec();
453
454 if ( reply->error() != QNetworkReply::NoError )
455 {
456 QgsDebugError( u"Request failed (%1): %2"_s.arg( uri ).arg( reply->errorString() ) );
457 return {};
458 }
459 return reply->data();
460}
461
462QgsQuantizedMeshDataProvider::QgsQuantizedMeshDataProvider(
463 const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions,
465 : QgsTiledSceneDataProvider( uri, providerOptions, flags ), mUri( uri ),
466 mProviderOptions( providerOptions )
467{
468 if ( uri.startsWith( "ion://"_L1 ) )
469 {
470 QString updatedUri = uriFromIon( uri );
471 mMetadata = QgsQuantizedMeshMetadata( updatedUri, transformContext(), mError );
472 }
473 else
474 {
475 mMetadata = QgsQuantizedMeshMetadata( uri, transformContext(), mError );
476 }
477
478 if ( mError.isEmpty() )
479 {
480 QgsCoordinateReferenceSystem wgs84( u"EPSG:4326"_s );
481 QgsCoordinateTransform wgs84ToCrs( wgs84, mMetadata->mCrs, transformContext() );
482 mIndex.emplace( new QgsQuantizedMeshIndex( *mMetadata, wgs84ToCrs ) );
483 mIsValid = true;
484 }
485}
486
487QString QgsQuantizedMeshDataProvider::uriFromIon( const QString &uri )
488{
489 // we expect one of the two options:
490 // ion://?assetId=123&accessToken=xyz
491 // ion://?assetId=123&authcfg=abc
492
493 QUrl url( uri );
494 const QString assetId = QUrlQuery( url ).queryItemValue( u"assetId"_s );
495 const QString accessToken = QUrlQuery( url ).queryItemValue( u"accessToken"_s );
496
497 const QString CESIUM_ION_URL = u"https://api.cesium.com/"_s;
498
499 QgsDataSourceUri dsUri;
500 dsUri.setEncodedUri( uri );
501 QString authCfg = dsUri.authConfigId();
502 QgsHttpHeaders headers = dsUri.httpHeaders();
503
504 // get asset info
505 {
506 const QString assetInfoEndpoint = CESIUM_ION_URL + u"v1/assets/%1"_s.arg( assetId );
507 QNetworkRequest request = QNetworkRequest( assetInfoEndpoint );
508 QgsSetRequestInitiatorClass( request, u"QgsQuantizedMeshDataProvider"_s )
509 headers.updateNetworkRequest( request );
510 if ( !accessToken.isEmpty() )
511 request.setRawHeader( "Authorization", u"Bearer %1"_s.arg( accessToken ).toLocal8Bit() );
512
513 QgsBlockingNetworkRequest networkRequest;
514 if ( accessToken.isEmpty() )
515 networkRequest.setAuthCfg( authCfg );
516
517 switch ( networkRequest.get( request ) )
518 {
520 break;
521
525 // TODO -- error reporting
526 return QString();
527 }
528
529 const QgsNetworkReplyContent content = networkRequest.reply();
530 const json assetInfoJson = json::parse( content.content().toStdString() );
531 if ( assetInfoJson["type"] != "TERRAIN" )
532 {
533 appendError( QgsErrorMessage( tr( "Only ion TERRAIN content can be accessed, not %1" ).arg( QString::fromStdString( assetInfoJson["type"].get<std::string>() ) ) ) );
534 return QString();
535 }
536 }
537
538 // get tileset access details
539 QString tileSetUri;
540 {
541 const QString tileAccessEndpoint = CESIUM_ION_URL + u"v1/assets/%1/endpoint"_s.arg( assetId );
542 QNetworkRequest request = QNetworkRequest( tileAccessEndpoint );
543 QgsSetRequestInitiatorClass( request, u"QgsQuantizedMeshDataProvider"_s )
544 headers.updateNetworkRequest( request );
545 if ( !accessToken.isEmpty() )
546 request.setRawHeader( "Authorization", u"Bearer %1"_s.arg( accessToken ).toLocal8Bit() );
547
548 QgsBlockingNetworkRequest networkRequest;
549 if ( accessToken.isEmpty() )
550 networkRequest.setAuthCfg( authCfg );
551
552 switch ( networkRequest.get( request ) )
553 {
555 break;
556
560 // TODO -- error reporting
561 return QString();
562 }
563
564 const QgsNetworkReplyContent content = networkRequest.reply();
565 const json tileAccessJson = json::parse( content.content().toStdString() );
566
567 if ( tileAccessJson.contains( "url" ) )
568 {
569 tileSetUri = QString::fromStdString( tileAccessJson["url"].get<std::string>() );
570 }
571 else if ( tileAccessJson.contains( "options" ) )
572 {
573 const auto &optionsJson = tileAccessJson["options"];
574 if ( optionsJson.contains( "url" ) )
575 {
576 tileSetUri = QString::fromStdString( optionsJson["url"].get<std::string>() );
577 }
578 }
579
580 if ( tileAccessJson.contains( "accessToken" ) )
581 {
582 // The tileset accessToken is NOT the same as the token we use to access the asset details -- ie we can't
583 // use the same authentication as we got from the providers auth cfg!
584 headers.insert( u"Authorization"_s,
585 u"Bearer %1"_s.arg( QString::fromStdString( tileAccessJson["accessToken"].get<std::string>() ) ) );
586 }
587 }
588
589 QgsDataSourceUri finalUri;
590 finalUri.setParam( "url", tileSetUri + "layer.json" );
591 finalUri.setHttpHeaders( headers );
592 return finalUri.encodedUri();
593}
594
596QgsQuantizedMeshDataProvider::capabilities() const
597{
599}
600QgsTiledSceneDataProvider *QgsQuantizedMeshDataProvider::clone() const
601{
602 return new QgsQuantizedMeshDataProvider( mUri, mProviderOptions );
603}
605QgsQuantizedMeshDataProvider::sceneCrs() const
606{
607 return mMetadata->mCrs;
608}
610QgsQuantizedMeshDataProvider::boundingVolume() const
611{
612 return mMetadata->mBoundingVolume;
613}
614
615QgsTiledSceneIndex QgsQuantizedMeshDataProvider::index() const
616{
617 if ( !mIndex )
618 // Return dummy index, this provider is likely not valid
619 return QgsTiledSceneIndex( nullptr );
620 return *mIndex;
621}
622
623QgsDoubleRange QgsQuantizedMeshDataProvider::zRange() const
624{
625 return mMetadata->dummyZRange;
626}
627QgsCoordinateReferenceSystem QgsQuantizedMeshDataProvider::crs() const
628{
629 return mMetadata->mCrs;
630}
631QgsRectangle QgsQuantizedMeshDataProvider::extent() const
632{
633 return mMetadata->mExtent;
634}
635bool QgsQuantizedMeshDataProvider::isValid() const { return mIsValid; }
636QString QgsQuantizedMeshDataProvider::name() const { return providerName; }
637QString QgsQuantizedMeshDataProvider::description() const { return providerDescription; }
638
639const QgsQuantizedMeshMetadata &QgsQuantizedMeshDataProvider::quantizedMeshMetadata() const
640{
641 return *mMetadata;
642}
643
644QgsQuantizedMeshProviderMetadata::QgsQuantizedMeshProviderMetadata()
645 : QgsProviderMetadata( QgsQuantizedMeshDataProvider::providerName,
646 QgsQuantizedMeshDataProvider::providerDescription ) {}
647
648QgsDataProvider *QgsQuantizedMeshProviderMetadata::createProvider(
649 const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions,
651{
652 return new QgsQuantizedMeshDataProvider( uri, providerOptions, flags );
653}
654
QFlags< TiledSceneProviderCapability > TiledSceneProviderCapabilities
Tiled scene data provider capabilities.
Definition qgis.h:5932
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:505
TileChildrenAvailability
Possible availability states for a tile's children.
Definition qgis.h:5969
@ Available
Tile children are already available.
Definition qgis.h:5971
@ NoChildren
Tile is known to have no children.
Definition qgis.h:5970
@ Z
Z-axis.
Definition qgis.h:2512
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:45
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:381
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two 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.
Stores the component parts of a 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:236
Represents a single error message.
Definition qgserror.h:35
A container for error messages.
Definition qgserror.h:83
void append(const QString &message, const QString &tag)
Append new error message.
Definition qgserror.cpp:43
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.
Implements simple HTTP header management.
bool updateNetworkRequest(QNetworkRequest &request) const
Updates a request by adding all the HTTP headers.
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.
Represents a 2D point.
Definition qgspointxy.h:62
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:162
QgsRectangle tileExtent(QgsTileXYZ id) const
Returns extent of the given tile in this matrix.
Definition qgstiles.cpp:85
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition qgstiles.cpp:101
static QgsTileMatrix fromTileMatrix(int zoomLevel, const QgsTileMatrix &tileMatrix)
Returns a tile matrix based on another one.
Definition qgstiles.cpp:65
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:35
A range of tiles in a tile matrix.
Definition qgstiles.h:118
int endColumn() const
Returns index of the last column in the range.
Definition qgstiles.h:130
int endRow() const
Returns index of the last row in the range.
Definition qgstiles.h:134
int startRow() const
Returns index of the first row in the range.
Definition qgstiles.h:132
int startColumn() const
Returns index of the first column in the range.
Definition qgstiles.h:128
bool isValid() const
Returns whether the range is valid (when all row/column numbers are not negative).
Definition qgstiles.h:125
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:43
int zoomLevel() const
Returns tile's zoom level (Z).
Definition qgstiles.h:56
int column() const
Returns tile's column index (X).
Definition qgstiles.h:52
int row() const
Returns tile's row index (Y).
Definition qgstiles.h:54
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}...
#define QgsDebugError(str)
Definition qgslogger.h:59
#define QgsSetRequestInitiatorClass(request, _class)
Setting options for creating vector data providers.