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