20#include <nlohmann/json.hpp>
44#include <QApplication>
47#include <QJsonDocument>
49#include <QNetworkRequest>
50#include <QRecursiveMutex>
51#include <QRegularExpression>
54#include <qstringliteral.h>
56#include "moc_qgscesiumtilesdataprovider.cpp"
60#define PROVIDER_KEY QStringLiteral( "cesiumtiles" )
61#define PROVIDER_DESCRIPTION QStringLiteral( "Cesium 3D Tiles data provider" )
72static QString appendQueryFromBaseUrl(
const QString &contentUri,
const QUrl &baseUrl )
74 QUrlQuery contentQuery( QUrl( contentUri ).query() );
75 const QList<QPair<QString, QString>> baseUrlQueryItems = QUrlQuery( baseUrl.query() ).queryItems();
76 for (
const QPair<QString, QString> &kv : baseUrlQueryItems )
78 contentQuery.addQueryItem( kv.first, kv.second );
80 QUrl newContentUrl( contentUri );
81 newContentUrl.setQuery( contentQuery );
82 return newContentUrl.toString();
90 QgsCesiumTiledSceneIndex(
93 const QString &authCfg,
94 const QgsHttpHeaders &headers,
95 const QgsCoordinateTransformContext &transformContext );
97 std::unique_ptr< QgsTiledSceneTile > tileFromJson(
const json &node,
const QUrl &baseUrl,
const QgsTiledSceneTile *parent,
Qgis::Axis gltfUpAxis );
103 std::unique_ptr< QgsTiledSceneNode > nodeFromJson(
const json &node,
const QUrl &baseUrl, QgsTiledSceneNode *parent,
Qgis::Axis gltfUpAxis );
104 void refineNodeFromJson( QgsTiledSceneNode *node,
const QUrl &baseUrl,
const json &json );
106 QgsTiledSceneTile
rootTile() const final;
107 QgsTiledSceneTile getTile(
long long id ) final;
108 long long parentTileId(
long long id ) const final;
109 QVector<
long long > childTileIds(
long long id ) const final;
110 QVector<
long long > getTiles( const QgsTiledSceneRequest &request ) final;
111 Qgis::TileChildrenAvailability childAvailability(
long long id ) const final;
112 bool fetchHierarchy(
long long id, QgsFeedback *feedback =
nullptr ) final;
116 QByteArray fetchContent( const QString &uri, QgsFeedback *feedback =
nullptr ) final;
120 enum class TileContentFormat
126 mutable QRecursiveMutex mLock;
127 QgsCoordinateTransformContext mTransformContext;
128 std::unique_ptr< QgsTiledSceneNode > mRootNode;
129 QMap< long long, QgsTiledSceneNode * > mNodeMap;
130 QMap< long long, TileContentFormat > mTileContentFormats;
132 QgsHttpHeaders mHeaders;
133 long long mNextTileId = 0;
137class QgsCesiumTilesDataProviderSharedData
140 QgsCesiumTilesDataProviderSharedData();
141 void initialize(
const QString &tileset,
143 const QgsCoordinateTransformContext &transformContext,
144 const QString &authCfg,
145 const QgsHttpHeaders &headers );
147 QgsCoordinateReferenceSystem mLayerCrs;
148 QgsCoordinateReferenceSystem mSceneCrs;
149 QgsTiledSceneBoundingVolume mBoundingVolume;
151 QgsRectangle mExtent;
152 nlohmann::json mTileset;
153 QgsDoubleRange mZRange;
155 QgsTiledSceneIndex mIndex;
157 QgsLayerMetadata mLayerMetadata;
159 QReadWriteLock mReadWriteLock;
170 const std::string gltfUpAxisString = json.get<std::string>();
171 if ( gltfUpAxisString ==
"z" || gltfUpAxisString ==
"Z" )
175 else if ( gltfUpAxisString ==
"y" || gltfUpAxisString ==
"Y" )
179 else if ( gltfUpAxisString ==
"x" || gltfUpAxisString ==
"X" )
183 QgsDebugError( QStringLiteral(
"Unsupported gltfUpAxis value: %1" ).arg( QString::fromStdString( gltfUpAxisString ) ) );
188 : mTransformContext( transformContext )
189 , mAuthCfg( authCfg )
190 , mHeaders( headers )
193 if ( tileset.contains(
"asset" ) )
195 const auto &assetJson = tileset[
"asset"];
196 if ( assetJson.contains(
"gltfUpAxis" ) )
198 gltfUpAxis = axisFromJson( assetJson[
"gltfUpAxis"] );
202 mRootNode = nodeFromJson( tileset[
"root" ], rootUrl,
nullptr, gltfUpAxis );
205std::unique_ptr< QgsTiledSceneTile > QgsCesiumTiledSceneIndex::tileFromJson(
const json &json,
const QUrl &baseUrl,
const QgsTiledSceneTile *parent,
Qgis::Axis gltfUpAxis )
207 auto tile = std::make_unique< QgsTiledSceneTile >( mNextTileId++ );
209 tile->setBaseUrl( baseUrl );
212 { QStringLiteral(
"gltfUpAxis" ),
static_cast< int >( gltfUpAxis ) },
213 { QStringLiteral(
"contentFormat" ), QStringLiteral(
"cesiumtiles" ) },
217 if ( json.contains(
"transform" ) && !json[
"transform"].is_null() )
219 const auto &transformJson = json[
"transform"];
220 double *ptr = transform.
data();
221 for (
int i = 0; i < 16; ++i )
222 ptr[i] = transformJson[i].get<double>();
226 transform = *parent->
transform() * transform;
229 else if ( parent && parent->
transform() )
234 tile->setTransform( transform );
236 const auto &boundingVolume = json[
"boundingVolume" ];
238 if ( boundingVolume.contains(
"region" ) )
241 if ( !rootRegion.
isNull() )
243 if ( rootRegion.
width() > 20 || rootRegion.
height() > 20 )
250 QVector< QgsVector3D > corners = rootRegion.
corners();
258 for (
int i = 0; i < 8; ++i )
261 x.append( corner.
x() );
262 y.append( corner.
y() );
263 z.append( corner.
z() );
266 ct.setBallparkTransformsAreAppropriate(
true );
269 ct.transformInPlace( x, y, z );
273 QgsDebugError( QStringLiteral(
"Cannot transform region bounding volume" ) );
276 const auto minMaxX = std::minmax_element( x.constBegin(), x.constEnd() );
277 const auto minMaxY = std::minmax_element( y.constBegin(), y.constEnd() );
278 const auto minMaxZ = std::minmax_element( z.constBegin(), z.constEnd() );
285 else if ( boundingVolume.contains(
"box" ) )
295 else if ( boundingVolume.contains(
"sphere" ) )
306 QgsDebugError( QStringLiteral(
"unsupported boundingVolume format" ) );
309 tile->setBoundingVolume( volume );
311 if ( json.contains(
"geometricError" ) )
312 tile->setGeometricError( json[
"geometricError"].get< double >() );
313 if ( json.contains(
"refine" ) )
315 if ( json[
"refine"] ==
"ADD" )
317 else if ( json[
"refine"] ==
"REPLACE" )
326 if ( json.contains(
"content" ) && !json[
"content"].is_null() )
328 const auto &contentJson = json[
"content"];
332 if ( contentJson.contains(
"uri" ) && !contentJson[
"uri"].is_null() )
334 QString relativeUri = QString::fromStdString( contentJson[
"uri"].get<std::string>() );
335 contentUri = baseUrl.resolved( QUrl( relativeUri ) ).toString();
337 if ( baseUrl.hasQuery() && QUrl( relativeUri ).isRelative() )
338 contentUri = appendQueryFromBaseUrl( contentUri, baseUrl );
340 else if ( contentJson.contains(
"url" ) && !contentJson[
"url"].is_null() )
342 QString relativeUri = QString::fromStdString( contentJson[
"url"].get<std::string>() );
343 contentUri = baseUrl.resolved( QUrl( relativeUri ) ).toString();
345 if ( baseUrl.hasQuery() && QUrl( relativeUri ).isRelative() )
346 contentUri = appendQueryFromBaseUrl( contentUri, baseUrl );
348 if ( !contentUri.isEmpty() )
350 tile->setResources( {{ QStringLiteral(
"content" ), contentUri } } );
357std::unique_ptr<QgsTiledSceneNode> QgsCesiumTiledSceneIndex::nodeFromJson(
const json &json,
const QUrl &baseUrl,
QgsTiledSceneNode *parent,
Qgis::Axis gltfUpAxis )
359 std::unique_ptr< QgsTiledSceneTile > tile = tileFromJson( json, baseUrl, parent ? parent->
tile() :
nullptr, gltfUpAxis );
360 auto newNode = std::make_unique< QgsTiledSceneNode >( tile.release() );
361 mNodeMap.insert( newNode->tile()->id(), newNode.get() );
363 if ( json.contains(
"children" ) )
365 for (
const auto &childJson : json[
"children"] )
367 nodeFromJson( childJson, baseUrl, newNode.get(), gltfUpAxis );
373 parent->
addChild( newNode.release() );
382void QgsCesiumTiledSceneIndex::refineNodeFromJson(
QgsTiledSceneNode *node,
const QUrl &baseUrl,
const json &json )
384 const auto &rootTileJson = json[
"root"];
387 if ( json.contains(
"asset" ) )
389 const auto &assetJson = json[
"asset"];
390 if ( assetJson.contains(
"gltfUpAxis" ) )
392 gltfUpAxis = axisFromJson( assetJson[
"gltfUpAxis"] );
396 std::unique_ptr< QgsTiledSceneTile > newTile = tileFromJson( rootTileJson, baseUrl, node->
tile(), gltfUpAxis );
406 if ( newTile->transform() )
409 if ( rootTileJson.contains(
"children" ) )
411 for (
const auto &childJson : rootTileJson[
"children"] )
413 nodeFromJson( childJson, baseUrl, node, gltfUpAxis );
420 QMutexLocker locker( &mLock );
426 QMutexLocker locker( &mLock );
427 auto it = mNodeMap.constFind(
id );
428 if ( it != mNodeMap.constEnd() )
430 return *( it.value()->tile() );
436long long QgsCesiumTiledSceneIndex::parentTileId(
long long id )
const
438 QMutexLocker locker( &mLock );
439 auto it = mNodeMap.constFind(
id );
440 if ( it != mNodeMap.constEnd() )
444 return parent->
tile()->
id();
451QVector< long long > QgsCesiumTiledSceneIndex::childTileIds(
long long id )
const
453 QMutexLocker locker( &mLock );
454 auto it = mNodeMap.constFind(
id );
455 if ( it != mNodeMap.constEnd() )
457 QVector< long long > childIds;
458 const QList< QgsTiledSceneNode * > children = it.value()->children();
459 childIds.reserve( children.size() );
462 childIds << child->tile()->id();
472 QVector< long long > results;
475 traverseNode = [&request, &traverseNode, &results,
this](
QgsTiledSceneNode * node )
491 QList< QgsTiledSceneNode * > children = node->
children();
492 if ( children.empty() )
494 switch ( childAvailability( tile->
id() ) )
504 if ( fetchHierarchy( tile->
id() ), request.
feedback() )
519 traverseNode( child );
526 results << tile->
id();
531 if ( children.empty() )
532 results << tile->
id();
538 results << tile->
id();
543 QMutexLocker locker( &mLock );
547 traverseNode( mRootNode.get() );
552 if ( it != mNodeMap.constEnd() )
554 traverseNode( it.value() );
564 QMutexLocker locker( &mLock );
566 auto it = mNodeMap.constFind(
id );
567 if ( it == mNodeMap.constEnd() )
570 if ( !it.value()->children().isEmpty() )
573 contentUri = it.value()->tile()->resources().value( QStringLiteral(
"content" ) ).toString();
577 auto it = mTileContentFormats.constFind(
id );
578 if ( it != mTileContentFormats.constEnd() )
580 switch ( it.value() )
582 case TileContentFormat::NotJson:
584 case TileContentFormat::Json:
591 if ( contentUri.isEmpty() )
601 const thread_local QRegularExpression isJsonRx( QStringLiteral(
".*\\.json(?:\\?.*)?$" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
602 if ( isJsonRx.match( contentUri ).hasMatch() )
606 const thread_local QRegularExpression antiCandidateRx( QStringLiteral(
".*\\.(gltf|glb|b3dm|i3dm|pnts|cmpt|bin|glbin|glbuf|png|jpeg|jpg)(?:\\?.*)?$" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
607 if ( antiCandidateRx.match( contentUri ).hasMatch() )
615bool QgsCesiumTiledSceneIndex::fetchHierarchy(
long long id,
QgsFeedback *feedback )
617 QMutexLocker locker( &mLock );
618 auto it = mNodeMap.constFind(
id );
619 if ( it == mNodeMap.constEnd() )
625 auto it = mTileContentFormats.constFind(
id );
626 if ( it != mTileContentFormats.constEnd() )
628 switch ( it.value() )
630 case TileContentFormat::NotJson:
632 case TileContentFormat::Json:
638 const QString contentUri = it.value()->tile()->resources().value( QStringLiteral(
"content" ) ).toString();
641 if ( contentUri.isEmpty() )
645 const QByteArray subTile = retrieveContent( contentUri, feedback );
646 if ( !subTile.isEmpty() )
653 const auto subTileJson = json::parse( subTile.toStdString() );
654 QMutexLocker locker( &mLock );
655 refineNodeFromJson( it.value(), QUrl( contentUri ), subTileJson );
656 mTileContentFormats.insert(
id, TileContentFormat::Json );
659 catch ( json::parse_error & )
661 QMutexLocker locker( &mLock );
662 mTileContentFormats.insert(
id, TileContentFormat::NotJson );
670 mTileContentFormats.insert(
id, TileContentFormat::NotJson );
675QByteArray QgsCesiumTiledSceneIndex::fetchContent(
const QString &uri,
QgsFeedback *feedback )
679 if ( uri.startsWith(
"http" ) )
681 QNetworkRequest networkRequest = QNetworkRequest( url );
683 networkRequest.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
684 networkRequest.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
686 mHeaders.updateNetworkRequest( networkRequest );
688 if ( QThread::currentThread() == QApplication::instance()->thread() )
692 networkRequest, mAuthCfg,
false, feedback );
712 return reply->data();
715 else if ( url.isLocalFile() && QFile::exists( url.toLocalFile() ) )
717 QFile file( url.toLocalFile() );
718 if ( file.open( QIODevice::ReadOnly ) )
720 return file.readAll();
731QgsCesiumTilesDataProviderSharedData::QgsCesiumTilesDataProviderSharedData()
739 mTileset = json::parse( tileset.toStdString() );
740 if ( !mTileset.contains(
"root" ) )
742 mError = QObject::tr(
"JSON is not a valid Cesium 3D Tiles source (does not contain \"root\" value)" );
746 mLayerMetadata.setType( QStringLiteral(
"dataset" ) );
748 if ( mTileset.contains(
"asset" ) )
750 const auto &asset = mTileset[
"asset" ];
751 if ( asset.contains(
"tilesetVersion" ) )
755 const QString tilesetVersion = QString::fromStdString( asset[
"tilesetVersion"].get<std::string>() );
756 mLayerMetadata.setIdentifier( tilesetVersion );
758 catch ( json::type_error & )
760 QgsDebugError( QStringLiteral(
"Error when parsing tilesetVersion value" ) );
766 new QgsCesiumTiledSceneIndex(
777 const auto &root = mTileset[
"root" ];
789 const auto &rootBoundingVolume = root[
"boundingVolume" ];
792 if ( root.contains(
"transform" ) && !root[
"transform"].is_null() )
794 const auto &transformJson = root[
"transform"];
795 double *ptr = rootTransform.
data();
796 for (
int i = 0; i < 16; ++i )
797 ptr[i] = transformJson[i].get<double>();
800 if ( rootBoundingVolume.contains(
"region" ) )
803 if ( !rootRegion.
isNull() )
808 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
815 mLayerMetadata.setCrs( mSceneCrs );
818 spatialExtent.
bounds = rootRegion;
821 else if ( rootBoundingVolume.contains(
"box" ) )
832 mLayerMetadata.setCrs( mSceneCrs );
835 mBoundingVolume.transform( rootTransform );
839 ct.setBallparkTransformsAreAppropriate(
true );
840 const QgsBox3D rootRegion = mBoundingVolume.bounds( ct );
842 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
847 std::unique_ptr< QgsAbstractGeometry > extent2D( mBoundingVolume.as2DGeometry( ct ) );
848 mExtent = extent2D->boundingBox();
852 QgsDebugError( QStringLiteral(
"Caught transform exception when transforming boundingVolume" ) );
856 spatialExtent.
bounds = mBoundingVolume.bounds();
859 else if ( rootBoundingVolume.contains(
"sphere" ) )
870 mLayerMetadata.setCrs( mSceneCrs );
878 ct.setBallparkTransformsAreAppropriate(
true );
879 const QgsBox3D rootRegion = mBoundingVolume.bounds( ct );
881 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
886 std::unique_ptr< QgsAbstractGeometry > extent2D( mBoundingVolume.as2DGeometry( ct ) );
887 mExtent = extent2D->boundingBox();
891 QgsDebugError( QStringLiteral(
"Caught transform exception when transforming boundingVolume" ) );
895 spatialExtent.
bounds = mBoundingVolume.bounds();
900 mError = QObject::tr(
"JSON is not a valid Cesium 3D Tiles source (unsupported boundingVolume format)" );
906 mLayerMetadata.setExtent( layerExtent );
916QgsCesiumTilesDataProvider::QgsCesiumTilesDataProvider(
const QString &uri,
const ProviderOptions &providerOptions,
Qgis::DataProviderReadFlags flags )
918 , mShared( std::make_shared< QgsCesiumTilesDataProviderSharedData >() )
928QgsCesiumTilesDataProvider::QgsCesiumTilesDataProvider(
const QgsCesiumTilesDataProvider &other )
930 , mIsValid( other.mIsValid )
931 , mAuthCfg( other.mAuthCfg )
932 , mHeaders( other.mHeaders )
935 mShared = other.mShared;
940 return mProviderFlags;
948QgsCesiumTilesDataProvider::~QgsCesiumTilesDataProvider() =
default;
950QgsCesiumTilesDataProvider *QgsCesiumTilesDataProvider::clone()
const
953 return new QgsCesiumTilesDataProvider( *
this );
956bool QgsCesiumTilesDataProvider::init()
961 const QString uri = dataSourceUri();
963 if ( uri.startsWith( QLatin1String(
"ion://" ) ) )
966 const QString assetId = QUrlQuery( url ).queryItemValue( QStringLiteral(
"assetId" ) );
967 const QString accessToken = QUrlQuery( url ).queryItemValue( QStringLiteral(
"accessToken" ) );
969 const QString CESIUM_ION_URL = QStringLiteral(
"https://api.cesium.com/" );
973 const QString assetInfoEndpoint = CESIUM_ION_URL + QStringLiteral(
"v1/assets/%1" ).arg( assetId );
974 QNetworkRequest request = QNetworkRequest( assetInfoEndpoint );
976 mHeaders.updateNetworkRequest( request );
977 if ( !accessToken.isEmpty() )
978 request.setRawHeader(
"Authorization", QStringLiteral(
"Bearer %1" ).arg( accessToken ).toLocal8Bit() );
981 if ( accessToken.isEmpty() )
982 networkRequest.setAuthCfg( mAuthCfg );
984 switch ( networkRequest.get( request ) )
997 const json assetInfoJson = json::parse( content.
content().toStdString() );
998 if ( assetInfoJson[
"type"] !=
"3DTILES" )
1000 appendError(
QgsErrorMessage( tr(
"Only ion 3D Tiles content can be accessed, not %1" ).arg( QString::fromStdString( assetInfoJson[
"type"].get<std::string>() ) ) ) );
1004 const QString name = QString::fromStdString( assetInfoJson[
"name"].get<std::string>() );
1005 if ( name.compare( QLatin1String(
"Google Photorealistic 3D Tiles" ), Qt::CaseInsensitive ) == 0 )
1013 mShared->mLayerMetadata.setTitle( name );
1014 mShared->mLayerMetadata.setAbstract( QString::fromStdString( assetInfoJson[
"description"].get<std::string>() ) );
1015 const QString attribution = QString::fromStdString( assetInfoJson[
"attribution"].get<std::string>() );
1016 if ( !attribution.isEmpty() )
1017 mShared->mLayerMetadata.setRights( { attribution } );
1019 mShared->mLayerMetadata.setDateTime(
Qgis::MetadataDateType::Created, QDateTime::fromString( QString::fromStdString( assetInfoJson[
"dateAdded"].get<std::string>() ), Qt::DateFormat::ISODate ) );
1024 const QString tileAccessEndpoint = CESIUM_ION_URL + QStringLiteral(
"v1/assets/%1/endpoint" ).arg( assetId );
1025 QNetworkRequest request = QNetworkRequest( tileAccessEndpoint );
1027 mHeaders.updateNetworkRequest( request );
1028 if ( !accessToken.isEmpty() )
1029 request.setRawHeader(
"Authorization", QStringLiteral(
"Bearer %1" ).arg( accessToken ).toLocal8Bit() );
1032 if ( accessToken.isEmpty() )
1033 networkRequest.setAuthCfg( mAuthCfg );
1035 switch ( networkRequest.get( request ) )
1048 const json tileAccessJson = json::parse( content.
content().toStdString() );
1050 if ( tileAccessJson.contains(
"url" ) )
1052 tileSetUri = QString::fromStdString( tileAccessJson[
"url"].get<std::string>() );
1054 else if ( tileAccessJson.contains(
"options" ) )
1056 const auto &optionsJson = tileAccessJson[
"options"];
1057 if ( optionsJson.contains(
"url" ) )
1059 tileSetUri = QString::fromStdString( optionsJson[
"url"].get<std::string>() );
1063 if ( tileAccessJson.contains(
"accessToken" ) )
1067 mHeaders.insert( QStringLiteral(
"Authorization" ),
1068 QStringLiteral(
"Bearer %1" ).arg( QString::fromStdString( tileAccessJson[
"accessToken"].get<std::string>() ) ) );
1077 tileSetUri = dsUri.
param( QStringLiteral(
"url" ) );
1080 if ( !tileSetUri.isEmpty() )
1082 const QUrl url( tileSetUri );
1084 QNetworkRequest request = QNetworkRequest( url );
1086 mHeaders.updateNetworkRequest( request );
1089 networkRequest.setAuthCfg( mAuthCfg );
1091 switch ( networkRequest.get( request ) )
1105 mShared->initialize( content.
content(), tileSetUri, transformContext(), mAuthCfg, mHeaders );
1112 const QFileInfo fi( dataSourceUri() );
1115 QFile file( dataSourceUri( ) );
1116 if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) )
1118 const QByteArray raw = file.readAll();
1119 mShared->initialize( raw, QUrl::fromLocalFile( dataSourceUri() ), transformContext(), mAuthCfg, mHeaders );
1132 if ( !mShared->mIndex.isValid() )
1134 appendError( mShared->mError );
1145 return mShared->mLayerCrs;
1153 return mShared->mExtent;
1156bool QgsCesiumTilesDataProvider::isValid()
const
1163QString QgsCesiumTilesDataProvider::name()
const
1167 return PROVIDER_KEY;
1170QString QgsCesiumTilesDataProvider::description()
const
1174 return QObject::tr(
"Cesium 3D Tiles" );
1177QString QgsCesiumTilesDataProvider::htmlMetadata()
const
1184 if ( mShared->mTileset.contains(
"asset" ) )
1186 const auto &asset = mShared->mTileset[
"asset" ];
1187 if ( asset.contains(
"version" ) )
1189 const QString version = QString::fromStdString( asset[
"version"].get<std::string>() );
1190 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"3D Tiles Version" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( version ) % QStringLiteral(
"</td></tr>\n" );
1193 if ( asset.contains(
"tilesetVersion" ) )
1197 const QString tilesetVersion = QString::fromStdString( asset[
"tilesetVersion"].get<std::string>() );
1198 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Tileset Version" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( tilesetVersion ) % QStringLiteral(
"</td></tr>\n" );
1200 catch ( json::type_error & )
1202 QgsDebugError( QStringLiteral(
"Error when parsing tilesetVersion value" ) );
1206 if ( asset.contains(
"generator" ) )
1208 const QString generator = QString::fromStdString( asset[
"generator"].get<std::string>() );
1209 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Tileset Generator" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( generator ) % QStringLiteral(
"</td></tr>\n" );
1212 if ( mShared->mTileset.contains(
"extensionsRequired" ) )
1214 QStringList extensions;
1215 for (
const auto &item : mShared->mTileset[
"extensionsRequired"] )
1217 extensions << QString::fromStdString( item.get<std::string>() );
1219 if ( !extensions.isEmpty() )
1221 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Extensions Required" ) % QStringLiteral(
"</td><td><ul><li>%1</li></ul></a>" ).arg( extensions.join( QLatin1String(
"</li><li>" ) ) ) % QStringLiteral(
"</td></tr>\n" );
1224 if ( mShared->mTileset.contains(
"extensionsUsed" ) )
1226 QStringList extensions;
1227 for (
const auto &item : mShared->mTileset[
"extensionsUsed"] )
1229 extensions << QString::fromStdString( item.get<std::string>() );
1231 if ( !extensions.isEmpty() )
1233 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Extensions Used" ) % QStringLiteral(
"</td><td><ul><li>%1</li></ul></a>" ).arg( extensions.join( QLatin1String(
"</li><li>" ) ) ) % QStringLiteral(
"</td></tr>\n" );
1237 if ( !mShared->mZRange.isInfinite() )
1239 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Z Range" ) % QStringLiteral(
"</td><td>%1 - %2</a>" ).arg( QLocale().toString( mShared->mZRange.lower() ), QLocale().toString( mShared->mZRange.upper() ) ) % QStringLiteral(
"</td></tr>\n" );
1252 return mShared->mLayerMetadata;
1262 return mShared->mSceneCrs ;
1273 return mShared ? mShared->mBoundingVolume : nullVolume;
1283 return mShared->mIndex;
1293 return mShared->mZRange;
1301QgsCesiumTilesProviderMetadata::QgsCesiumTilesProviderMetadata():
1306QIcon QgsCesiumTilesProviderMetadata::icon()
const
1313 return new QgsCesiumTilesDataProvider( uri, options, flags );
1318 const QVariantMap parts = decodeUri( uri );
1319 if ( parts.value( QStringLiteral(
"file-name" ) ).toString().compare( QLatin1String(
"tileset.json" ), Qt::CaseInsensitive ) == 0 )
1334int QgsCesiumTilesProviderMetadata::priorityForUri(
const QString &uri )
const
1336 const QVariantMap parts = decodeUri( uri );
1337 if ( parts.value( QStringLiteral(
"file-name" ) ).toString().compare( QLatin1String(
"tileset.json" ), Qt::CaseInsensitive ) == 0 )
1343QList<Qgis::LayerType> QgsCesiumTilesProviderMetadata::validLayerTypesForUri(
const QString &uri )
const
1345 const QVariantMap parts = decodeUri( uri );
1346 if ( parts.value( QStringLiteral(
"file-name" ) ).toString().compare( QLatin1String(
"tileset.json" ), Qt::CaseInsensitive ) == 0 )
1349 return QList< Qgis::LayerType>();
1352QVariantMap QgsCesiumTilesProviderMetadata::decodeUri(
const QString &uri )
const
1354 QVariantMap uriComponents;
1355 QUrl url = QUrl::fromUserInput( uri );
1356 uriComponents.insert( QStringLiteral(
"file-name" ), url.fileName() );
1357 uriComponents.insert( QStringLiteral(
"path" ), uri );
1358 return uriComponents;
1374 return QObject::tr(
"Cesium 3D Tiles" ) + QStringLiteral(
" (tileset.json TILESET.JSON)" );
1381 return FileBasedUris;
1384QList<Qgis::LayerType> QgsCesiumTilesProviderMetadata::supportedLayerTypes()
const
1389QString QgsCesiumTilesProviderMetadata::encodeUri(
const QVariantMap &parts )
const
1391 const QString path = parts.value( QStringLiteral(
"path" ) ).toString();
1397 return ProviderMetadataCapability::LayerTypesForUri
1398 | ProviderMetadataCapability::PriorityForUri
1399 | ProviderMetadataCapability::QuerySublayers;
QFlags< TiledSceneProviderCapability > TiledSceneProviderCapabilities
Tiled scene data provider capabilities.
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
FileFilterType
Type of file filters.
@ TiledScene
Tiled scene layers.
@ VectorTile
Vector tile layers.
@ MeshDataset
Mesh datasets.
@ PointCloud
Point clouds.
@ Is3DBasemapSource
Associated source should be considered a '3D basemap' layer. See Qgis::MapLayerProperty::Is3DBasemapL...
@ IsBasemapSource
Associated source should be considered a 'basemap' layer. See Qgis::MapLayerProperty::IsBasemapLayer.
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata().
QFlags< SublayerQueryFlag > SublayerQueryFlags
Sublayer query flags.
TileChildrenAvailability
Possible availability states for a tile's children.
@ Available
Tile children are already available.
@ NeedFetching
Tile has children, but they are not yet available and must be fetched.
@ NoChildren
Tile is known to have no children.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ NoHierarchyFetch
Do not allow hierarchy fetching when hierarchy is not currently available. Avoids network requests,...
@ Additive
When tile is refined its content should be used alongside its children simultaneously.
@ Replacement
When tile is refined then its children should be used in place of itself.
An abstract base class for tiled scene data provider indices.
virtual QgsTiledSceneTile rootTile() const =0
Returns the root tile for the index.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
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.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
@ 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.
A 3-dimensional box composed of x, y, z coordinates.
double zMaximum() const
Returns the maximum z value.
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
QVector< QgsVector3D > corners() const
Returns an array of all box corners as 3D vectors.
double width() const
Returns the width of the box.
double zMinimum() const
Returns the minimum z value.
double height() const
Returns the height of the box.
bool isNull() const
Test if the box is null (holding no spatial information).
static QgsSphere parseSphere(const json &sphere)
Parses a sphere object from a Cesium JSON document.
static QgsOrientedBox3D parseBox(const json &box)
Parses a box object from a Cesium JSON document to an oriented bounding box.
static QgsBox3D parseRegion(const json ®ion)
Parses a region object from a Cesium JSON object to a 3D box.
static QgsSphere transformSphere(const QgsSphere &sphere, const QgsMatrix4x4 &transform)
Applies a transform to a sphere.
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Custom exception class for Coordinate Reference System related exceptions.
Stores the component parts of a 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.
Represents a single error message.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
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.
bool isIdentity() const
Returns whether this matrix is an identity matrix.
double * data()
Returns pointer to the matrix data (stored in column-major order).
static QgsNetworkReplyContent blockingGet(QNetworkRequest &request, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr, Qgis::NetworkRequestFlags flags=Qgis::NetworkRequestFlags())
Posts a GET request to obtain the contents of the target request and returns a new QgsNetworkReplyCon...
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
Represents a oriented (rotated) box in 3 dimensions.
bool isNull() const
Returns true if the box is a null box.
static QgsOrientedBox3D fromBox3D(const QgsBox3D &box)
Constructs an oriented box from an axis-aligned bounding box.
Contains details about a sub layer available from a dataset.
void setUri(const QString &uri)
Sets the layer's uri.
void setType(Qgis::LayerType type)
Sets the layer type.
void setName(const QString &name)
Sets the layer's name.
void setProviderKey(const QString &key)
Sets the associated data provider key.
static QString suggestLayerNameFromFilePath(const QString &path)
Suggests a suitable layer name given only a file path.
A convenience class that simplifies locking and unlocking QReadWriteLocks.
A rectangle specified with double values.
A spherical geometry object.
bool isNull() const
Returns true if the sphere is a null (default constructed) sphere.
QgsBox3D boundingBox() const
Returns the 3-dimensional bounding box containing the sphere.
void finished()
Emitted when the reply has finished (either with a success or with a failure).
Represents a bounding volume for a tiled scene.
QgsOrientedBox3D box() const
Returns the volume's oriented box.
bool intersects(const QgsOrientedBox3D &box) const
Returns true if this bounds intersects the specified box.
void transform(const QgsMatrix4x4 &transform)
Applies a transform to the bounding volume.
Base class for data providers for QgsTiledSceneLayer.
An index for tiled scene data providers.
Allows representing QgsTiledSceneTiles in a hierarchical tree.
void addChild(QgsTiledSceneNode *child)
Adds a child to this node.
QgsTiledSceneNode * parentNode() const
Returns the parent of this node.
QList< QgsTiledSceneNode * > children() const
Returns this node's children.
QgsTiledSceneTile * tile()
Returns the tile associated with the node.
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.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the request to check if it should be can...
Qgis::TiledSceneRequestFlags flags() const
Returns the flags which affect how tiles are fetched.
Represents an individual tile from a tiled scene data source.
void setTransform(const QgsMatrix4x4 &transform)
Sets the tile's transform.
Qgis::TileRefinementProcess refinementProcess() const
Returns the tile's refinement process.
const QgsTiledSceneBoundingVolume & boundingVolume() const
Returns the bounding volume for the tile.
long long id() const
Returns the tile's unique ID.
const QgsMatrix4x4 * transform() const
Returns the tile's transform.
void setResources(const QVariantMap &resources)
Sets the resources attached to the tile.
double geometricError() const
Returns the tile's geometric error, which is the error, in meters, of the tile's simplified represent...
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
#define QgsDebugError(str)
#define QgsSetRequestInitiatorClass(request, _class)
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.