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