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