QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
qgsquantizedmeshdataprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsquantizedmeshdataprovider.cpp
3 --------------------
4 begin : June 2024
5 copyright : (C) 2024 by David Koňařík
6 email : dvdkon at konarici dot cz
7 ******************************************************************
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
20#include "qgsapplication.h"
21#include "qgsauthmanager.h"
26#include "qgslogger.h"
27#include "qgsmatrix4x4.h"
28#include "qgsorientedbox3d.h"
29#include "qgsprovidermetadata.h"
33#include "qgstiledsceneindex.h"
35#include "qgstiledscenetile.h"
36#include "qgstiles.h"
37#include "qgsvectortileutils.h"
38#include <limits>
39#include <nlohmann/json.hpp>
40#include <qglobal.h>
41#include <qnetworkrequest.h>
42#include <qobject.h>
43#include <qstringliteral.h>
44#include <qvector.h>
45
47
48class MissingFieldException : public std::exception
49{
50 public:
51 MissingFieldException( const char *field ) : mField( field ) { }
52 const char *what() const noexcept
53 {
54 return QString( "Missing field: %1" ).arg( mField ).toLocal8Bit().data();
55 }
56 private:
57 const char *mField;
58};
59
60template <typename T>
61static T jsonGet( nlohmann::json &json, const char *idx )
62{
63 auto &obj = json[idx];
64 if ( obj.is_null() )
65 {
66 throw MissingFieldException( idx );
67 }
68 return obj.get<T>();
69}
70
71
72QgsQuantizedMeshMetadata::QgsQuantizedMeshMetadata(
73 const QString &uri,
74 const QgsCoordinateTransformContext &transformContext,
75 QgsError &error )
76{
77 QgsDataSourceUri dsUri;
78 dsUri.setEncodedUri( uri );
79 mAuthCfg = dsUri.authConfigId();
80 mHeaders = dsUri.httpHeaders();
81
82 // The provided URL should be the metadata JSON's location
83 QUrl metadataUrl = dsUri.param( "url" );
84 QNetworkRequest requestData( metadataUrl );
85 mHeaders.updateNetworkRequest( requestData );
86 QgsSetRequestInitiatorClass( requestData,
87 QStringLiteral( "QgsQuantizedMeshDataProvider" ) );
89 request.setAuthCfg( mAuthCfg );
90 auto respCode = request.get( requestData );
92 {
93 error.append(
94 QObject::tr( "Failed to retrieve quantized mesh tiles metadata: %1" )
95 .arg( request.errorMessage().data() ) );
96 return;
97 }
98 auto reply = request.reply().content();
99
100 try
101 {
102 auto replyJson = nlohmann::json::parse( reply.data() );
103
104 // The metadata is an (undocumented) variant of TileJSON
105 if ( jsonGet<std::string>( replyJson, "format" ) != "quantized-mesh-1.0" )
106 {
107 error.append( QObject::tr( "Unexpected tile format: %1" )
108 .arg( replyJson["format"].dump().c_str() ) );
109 return;
110 }
111
112 auto crsString = QString::fromStdString( jsonGet<std::string>( replyJson, "projection" ) );
113 mCrs = QgsCoordinateReferenceSystem( crsString );
114 if ( !mCrs.isValid() )
115 {
116 error.append( QObject::tr( "Invalid CRS '%1'!" ).arg( crsString ) );
117 return;
118 }
119
120 try
121 {
122 std::vector<double> bounds = jsonGet<std::vector<double>>( replyJson, "bounds" );
123 if ( bounds.size() != 4 )
124 {
125 error.append( QObject::tr( "Bounds array doesn't have 4 items" ) );
126 return;
127 }
128 mExtent = QgsRectangle( bounds[0], bounds[1], bounds[2], bounds[3] );
129 }
130 catch ( MissingFieldException & )
131 {
132 mExtent = mCrs.bounds();
133 }
134
135 auto zRange = dummyZRange;
136 mBoundingVolume =
138 QgsBox3D(
139 mExtent.xMinimum(), mExtent.yMinimum(), zRange.lower(),
140 mExtent.xMaximum(), mExtent.yMaximum(), zRange.upper() ) );
141
142 // The TileJSON spec uses "scheme", but some real-world datasets use "schema"
143 if ( replyJson.find( "scheme" ) != replyJson.end() )
144 mTileScheme = QString::fromStdString( jsonGet<std::string>( replyJson, "scheme" ) );
145 else if ( replyJson.find( "schema" ) != replyJson.end() )
146 mTileScheme = QString::fromStdString( jsonGet<std::string>( replyJson, "schema" ) );
147 else throw MissingFieldException( "scheme/schema" );
148
149 for ( auto &aabbs : replyJson.at( "available" ) )
150 {
151 QVector<QgsTileRange> tileRanges;
152 for ( auto &aabb : aabbs )
153 {
154 tileRanges.push_back(
156 jsonGet<int>( aabb, "startX" ), jsonGet<int>( aabb, "endX" ),
157 jsonGet<int>( aabb, "startY" ), jsonGet<int>( aabb, "endY" ) ) );
158 }
159 mAvailableTiles.push_back( tileRanges );
160 }
161
162 try
163 {
164 mMinZoom = jsonGet<uint8_t>( replyJson, "minzoom" );
165 mMaxZoom = jsonGet<uint8_t>( replyJson, "maxzoom" );
166 }
167 catch ( MissingFieldException & )
168 {
169 mMinZoom = 0;
170 mMaxZoom = mAvailableTiles.size() - 1;
171 }
172
173 QString versionStr =
174 QString::fromStdString( jsonGet<std::string>( replyJson, "version" ) );
175 for ( auto &urlStr : jsonGet<std::vector<std::string>>( replyJson, "tiles" ) )
176 {
177 QUrl url = metadataUrl.resolved( QString::fromStdString( urlStr ) );
178 mTileUrls.push_back(
179 url.toString( QUrl::DecodeReserved ).replace( "{version}", versionStr ) );
180 }
181
182 int rootTileCount = 1;
183 if ( crsString == QLatin1String( "EPSG:4326" ) )
184 rootTileCount = 2;
185 else if ( crsString != QLatin1String( "EPSG:3857" ) )
186 error.append( QObject::tr( "Unhandled CRS: %1" ).arg( crsString ) );
187
188 QgsCoordinateReferenceSystem wgs84( QStringLiteral( "EPSG:4326" ) );
189 // Bounds of tile schema in projected coordinates
190 auto crsBounds =
191 QgsCoordinateTransform( wgs84, mCrs, transformContext )
192 .transform( mCrs.bounds() );
193 QgsPointXY topLeft( crsBounds.xMinimum(), crsBounds.yMaximum() );
194 double z0TileSize = crsBounds.height();
195
196 mTileMatrix = QgsTileMatrix::fromCustomDef( 0, mCrs, topLeft, z0TileSize, rootTileCount, 1 );
197 }
198 catch ( nlohmann::json::exception &ex )
199 {
200 error.append( QObject::tr( "Error parsing JSON metadata: %1" ).arg( ex.what() ) );
201 }
202 catch ( MissingFieldException &ex )
203 {
204 error.append( QObject::tr( "Error parsing JSON metadata: %1" ).arg( ex.what() ) );
205 }
206}
207
208const QgsDoubleRange QgsQuantizedMeshMetadata::dummyZRange = {-10000, 10000};
209
210static QgsTileXYZ tileToTms( QgsTileXYZ &xyzTile )
211{
212 // Flip Y axis for TMS schema
213 Q_ASSERT( xyzTile.zoomLevel() >= 0 );
214 return {xyzTile.column(),
215 ( 1 << xyzTile.zoomLevel() ) - xyzTile.row() - 1,
216 xyzTile.zoomLevel()};
217}
218
219bool QgsQuantizedMeshMetadata::containsTile( QgsTileXYZ tile ) const
220{
221 if ( tile.zoomLevel() < mMinZoom || tile.zoomLevel() > mMaxZoom ||
222 tile.zoomLevel() >= mAvailableTiles.size() )
223 return false;
224 // We operate with XYZ-style tile coordinates, but the availability array may
225 // be given in TMS-style
226 if ( mTileScheme == QLatin1String( "tms" ) )
227 tile = tileToTms( tile );
228 for ( auto &range : mAvailableTiles[tile.zoomLevel()] )
229 {
230 if ( range.startColumn() <= tile.column() && range.endColumn() >= tile.column() &&
231 range.startRow() <= tile.row() && range.endRow() >= tile.row() )
232 return true;
233 }
234 return false;
235}
236
237double QgsQuantizedMeshMetadata::geometricErrorAtZoom( int zoom ) const
238{
239 // The specification doesn't mandate any precision, we can only make a guess
240 // based on each tile's maximum possible numerical precision and some
241 // reasonable-looking constant.
242 return 400000 / pow( 2, zoom );
243}
244
245long long QgsQuantizedMeshIndex::encodeTileId( QgsTileXYZ tile )
246{
247 if ( tile.zoomLevel() == -1 )
248 {
249 Q_ASSERT( tile.column() == 0 && tile.row() == 0 );
250 return ROOT_TILE_ID;
251 }
252 Q_ASSERT( tile.zoomLevel() < ( 2 << 4 ) && ( tile.column() < ( 2 << 27 ) ) &&
253 ( tile.row() < ( 2 << 27 ) ) );
254 return tile.row() | ( ( long long )tile.column() << 28 ) |
255 ( ( 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(
265 ( int )( ( id >> 28 ) & ( ( 2 << 27 ) - 1 ) ),
266 ( int )( id & ( ( 2 << 27 ) - 1 ) ),
267 ( int )( ( id >> 56 ) & ( ( 2 << 4 ) - 1 ) ) );
268}
269
270QgsTiledSceneTile QgsQuantizedMeshIndex::rootTile() const
271{
272 // Returns virtual tile to paper over tiling schemes which have >1 tile at zoom 0
273 auto tile = QgsTiledSceneTile( ROOT_TILE_ID );
274 auto bounds = mWgs84ToCrs.transform( mMetadata.mCrs.bounds() );
275 tile.setBoundingVolume(
277 QgsBox3D( bounds, mMetadata.dummyZRange.lower(), mMetadata.dummyZRange.upper() ) ) );
278 tile.setGeometricError( std::numeric_limits<double>::max() );
279 return tile;
280}
281long long QgsQuantizedMeshIndex::parentTileId( long long id ) const
282{
283 if ( id == ROOT_TILE_ID )
284 return -1;
285 auto tile = decodeTileId( id );
286 if ( tile.zoomLevel() == 0 )
287 return ROOT_TILE_ID;
288 return encodeTileId( {tile.zoomLevel() - 1, tile.column() / 2, tile.row() / 2} );
289}
290QVector<long long> QgsQuantizedMeshIndex::childTileIds( long long id ) const
291{
292 auto tile = decodeTileId( id );
293 QVector<long long> children;
294 auto x = tile.column(), y = tile.row(), zoom = tile.zoomLevel();
295
296 if ( mMetadata.containsTile( {x * 2, y * 2, zoom + 1} ) )
297 children.push_back( encodeTileId( {x * 2, y * 2, zoom + 1} ) );
298 if ( mMetadata.containsTile( {x * 2 + 1, y * 2, zoom + 1} ) )
299 children.push_back( encodeTileId( {x * 2 + 1, y * 2, zoom + 1} ) );
300 if ( mMetadata.containsTile( {x * 2, y * 2 + 1, zoom + 1} ) )
301 children.push_back( encodeTileId( {x * 2, y * 2 + 1, zoom + 1} ) );
302 if ( mMetadata.containsTile( {x * 2 + 1, y * 2 + 1, zoom + 1} ) )
303 children.push_back( encodeTileId( {x * 2 + 1, y * 2 + 1, zoom + 1} ) );
304
305 return children;
306}
307QgsTiledSceneTile QgsQuantizedMeshIndex::getTile( long long id )
308{
309 auto xyzTile = decodeTileId( id );
310 QgsTiledSceneTile sceneTile( id );
311
312 auto zoomedMatrix = QgsTileMatrix::fromTileMatrix( xyzTile.zoomLevel(), mMetadata.mTileMatrix );
313 auto tileExtent = zoomedMatrix.tileExtent( xyzTile );
314
315 sceneTile.setBoundingVolume(
317 QgsBox3D( tileExtent, mMetadata.dummyZRange.lower(), mMetadata.dummyZRange.upper() ) ) );
318 sceneTile.setGeometricError( mMetadata.geometricErrorAtZoom( xyzTile.zoomLevel() ) );
319
320 if ( id == ROOT_TILE_ID )
321 // The root tile is fictitious and has no content, don't bother pointing to any.
322 return sceneTile;
323
324 if ( mMetadata.mTileScheme == QLatin1String( "tms" ) )
325 xyzTile = tileToTms( xyzTile );
326
327 if ( mMetadata.mTileUrls.size() == 0 )
328 {
329 QgsDebugError( "Quantized Mesh metadata has no URLs for tiles" );
330 }
331 else
332 {
333 // TODO: Intelligently choose from alternatives. Round robin?
335 mMetadata.mTileUrls[0], xyzTile, zoomedMatrix );
336 sceneTile.setResources( {{"content", tileUri}} );
337 sceneTile.setMetadata(
338 {
339 {QStringLiteral( "gltfUpAxis" ), static_cast<int>( Qgis::Axis::Z )},
340 {QStringLiteral( "contentFormat" ), QStringLiteral( "quantizedmesh" )},
341 } );
342 }
343
344 // Tile meshes have 0.0 -- 1.0 coordinates. Rescale them to the tile's real
345 // width and height in our CRS and move the tile to its position.
346 QgsMatrix4x4 transform(
347 tileExtent.width(), 0, 0, tileExtent.xMinimum(),
348 0, tileExtent.height(), 0, tileExtent.yMinimum(),
349 0, 0, 1, 0,
350 0, 0, 0, 1 );
351 sceneTile.setTransform( transform );
352
353 return sceneTile;
354}
355QVector<long long>
356QgsQuantizedMeshIndex::getTiles( const QgsTiledSceneRequest &request )
357{
358 uint8_t zoomLevel = mMetadata.mMinZoom;
359 if ( request.requiredGeometricError() != 0 )
360 {
361 while ( zoomLevel < mMetadata.mMaxZoom &&
362 mMetadata.geometricErrorAtZoom( zoomLevel ) > request.requiredGeometricError() )
363 zoomLevel++;
364 }
365 auto tileMatrix = QgsTileMatrix::fromTileMatrix( zoomLevel, mMetadata.mTileMatrix );
366
367 QVector<long long> ids;
368 // We can only filter on X and Y
369 auto extent = request.filterBox().extent().toRectangle();
370 if ( request.parentTileId() != -1 )
371 {
372 auto parentTile = decodeTileId( request.parentTileId() );
373 extent.intersect( tileMatrix.tileExtent( parentTile ) );
374 }
375
376 auto tileRange = tileMatrix.tileRangeFromExtent( extent );
377 if ( !tileRange.isValid() )
378 return {};
379
380 for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); col++ )
381 for ( int row = tileRange.startRow(); row <= tileRange.endRow(); row++ )
382 {
383 auto xyzTile = QgsTileXYZ( col, row, zoomLevel );
384 if ( mMetadata.containsTile( xyzTile ) )
385 ids.push_back( encodeTileId( xyzTile ) );
386 }
387
388 return ids;
389}
391QgsQuantizedMeshIndex::childAvailability( long long id ) const
392{
393 auto childIds = childTileIds( id );
394 if ( childIds.count() == 0 )
397}
398bool QgsQuantizedMeshIndex::fetchHierarchy( long long id, QgsFeedback *feedback )
399{
400 // The API was built for Cesium 3D tiles, which have tiles (actual files with
401 // metadata) as nodes of a hierarchy tree, with the actual data in children
402 // of those tiles. For us, tiles are represented by int IDs, so they don't
403 // need to be fetched.
404 Q_UNUSED( id );
405 Q_UNUSED( feedback );
406 return true;
407}
408
409QByteArray QgsQuantizedMeshIndex::fetchContent( const QString &uri,
410 QgsFeedback *feedback )
411{
412 QNetworkRequest requestData( uri );
413 requestData.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
414 requestData.setRawHeader( "Accept", "application/vnd.quantized-mesh,application/octet-stream;q=0.9" );
415 mMetadata.mHeaders.updateNetworkRequest( requestData );
416 QgsApplication::authManager()->updateNetworkRequest( requestData, mMetadata.mAuthCfg );
417 QgsSetRequestInitiatorClass( requestData,
418 QStringLiteral( "QgsQuantizedMeshIndex" ) );
419
420 std::unique_ptr<QgsTileDownloadManagerReply> reply( QgsApplication::tileDownloadManager()->get( requestData ) );
421
422 QEventLoop loop;
423 if ( feedback )
424 QObject::connect( feedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );
425 QObject::connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit );
426 loop.exec();
427
428 if ( reply->error() != QNetworkReply::NoError )
429 {
430 QgsDebugError( QStringLiteral( "Request failed: %1" ).arg( uri ) );
431 return {};
432 }
433 return reply->data();
434}
435
436QgsQuantizedMeshDataProvider::QgsQuantizedMeshDataProvider(
437 const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions,
439 : QgsTiledSceneDataProvider( uri, providerOptions, flags ), mUri( uri ),
440 mProviderOptions( providerOptions )
441{
442 mMetadata = QgsQuantizedMeshMetadata( uri, transformContext(), mError );
443 if ( mError.isEmpty() )
444 {
445 QgsCoordinateReferenceSystem wgs84( QStringLiteral( "EPSG:4326" ) );
446 QgsCoordinateTransform wgs84ToCrs( wgs84, mMetadata->mCrs, transformContext() );
447 mIndex.emplace( new QgsQuantizedMeshIndex( *mMetadata, wgs84ToCrs ) );
448 mIsValid = true;
449 }
450}
451
453QgsQuantizedMeshDataProvider::capabilities() const
454{
456}
457QgsTiledSceneDataProvider *QgsQuantizedMeshDataProvider::clone() const
458{
459 return new QgsQuantizedMeshDataProvider( mUri, mProviderOptions );
460}
462QgsQuantizedMeshDataProvider::sceneCrs() const
463{
464 return mMetadata->mCrs;
465}
467QgsQuantizedMeshDataProvider::boundingVolume() const
468{
469 return mMetadata->mBoundingVolume;
470}
471
472QgsTiledSceneIndex QgsQuantizedMeshDataProvider::index() const
473{
474 if ( !mIndex )
475 // Return dummy index, this provider is likely not valid
476 return QgsTiledSceneIndex( nullptr );
477 return *mIndex;
478}
479
480QgsDoubleRange QgsQuantizedMeshDataProvider::zRange() const
481{
482 return mMetadata->dummyZRange;
483}
484QgsCoordinateReferenceSystem QgsQuantizedMeshDataProvider::crs() const
485{
486 return mMetadata->mCrs;
487}
488QgsRectangle QgsQuantizedMeshDataProvider::extent() const
489{
490 return mMetadata->mExtent;
491}
492bool QgsQuantizedMeshDataProvider::isValid() const { return mIsValid; }
493QString QgsQuantizedMeshDataProvider::name() const { return providerName; }
494QString QgsQuantizedMeshDataProvider::description() const { return providerDescription; }
495
496const QgsQuantizedMeshMetadata &QgsQuantizedMeshDataProvider::quantizedMeshMetadata() const
497{
498 return *mMetadata;
499}
500
501QgsQuantizedMeshProviderMetadata::QgsQuantizedMeshProviderMetadata()
502 : QgsProviderMetadata( QgsQuantizedMeshDataProvider::providerName,
503 QgsQuantizedMeshDataProvider::providerDescription ) {}
504
505QgsDataProvider *QgsQuantizedMeshProviderMetadata::createProvider(
506 const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions,
508{
509 return new QgsQuantizedMeshDataProvider( uri, providerOptions, flags );
510}
511
QFlags< TiledSceneProviderCapability > TiledSceneProviderCapabilities
Tiled scene data provider capabilities.
Definition qgis.h:5164
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:5201
@ 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.
@ NoError
No error was encountered.
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:361
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Abstract base class for spatial data provider implementations.
Class for storing the component parts of a RDBMS data source URI (e.g.
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.
QString authConfigId() const
Returns any associated authentication configuration ID stored in the URI.
QgsRange which stores a range of double values.
Definition qgsrange.h:231
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.
A simple 4x4 matrix implementation useful for transformation in 3D space.
QByteArray content() const
Returns the reply content.
QgsBox3D extent() const
Returns the overall bounding box of the object.
static QgsOrientedBox3D fromBox3D(const QgsBox3D &box)
Constructs an oriented box from an axis-aligned bounding box.
A class to represent a 2D point.
Definition qgspointxy.h:60
Holds data provider key, description, and associated shared library file or function pointer informat...
A rectangle specified with double values.
void finished()
Emitted when the reply has finished (either with a success or with a failure)
static QgsTileMatrix fromTileMatrix(int zoomLevel, const QgsTileMatrix &tileMatrix)
Returns a tile matrix based on another one.
Definition qgstiles.cpp:61
static QgsTileMatrix fromCustomDef(int zoomLevel, const QgsCoordinateReferenceSystem &crs, const QgsPointXY &z0TopLeftPoint, double z0Dimension, int z0MatrixWidth=1, int z0MatrixHeight=1)
Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units.
Definition qgstiles.cpp:31
Range of tiles in a tile matrix to be rendered.
Definition qgstiles.h:99
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.
static QString formatXYZUrlTemplate(const QString &url, QgsTileXYZ tile, const QgsTileMatrix &tileMatrix)
Returns formatted tile URL string replacing {x}, {y}, {z} placeholders (or {-y} instead of {y} for TM...
#define QgsDebugError(str)
Definition qgslogger.h:38
#define QgsSetRequestInitiatorClass(request, _class)
Setting options for creating vector data providers.