20#include <nlohmann/json.hpp>
44#include <QApplication>
47#include <QJsonDocument>
49#include <QNetworkRequest>
50#include <QRecursiveMutex>
51#include <QRegularExpression>
55#include <qstringliteral.h>
57#include "moc_qgscesiumtilesdataprovider.cpp"
59using namespace Qt::StringLiterals;
63#define PROVIDER_KEY u"cesiumtiles"_s
64#define PROVIDER_DESCRIPTION u"Cesium 3D Tiles data provider"_s
75static QString appendQueryFromBaseUrl(
const QString &contentUri,
const QUrl &baseUrl )
77 QUrlQuery contentQuery( QUrl( contentUri ).query() );
78 const QList<QPair<QString, QString>> baseUrlQueryItems = QUrlQuery( baseUrl.query() ).queryItems();
79 for (
const QPair<QString, QString> &kv : baseUrlQueryItems )
81 contentQuery.addQueryItem( kv.first, kv.second );
83 QUrl newContentUrl( contentUri );
84 newContentUrl.setQuery( contentQuery );
85 return newContentUrl.toString();
92 QgsCesiumTiledSceneIndex(
const json &tileset,
const QUrl &rootUrl,
const QString &authCfg,
const QgsHttpHeaders &headers,
const QgsCoordinateTransformContext &transformContext );
94 std::unique_ptr< QgsTiledSceneTile > tileFromJson(
const json &node,
const QUrl &baseUrl,
const QgsTiledSceneTile *parent,
Qgis::Axis gltfUpAxis );
100 std::unique_ptr< QgsTiledSceneNode > nodeFromJson(
const json &node,
const QUrl &baseUrl, QgsTiledSceneNode *parent,
Qgis::Axis gltfUpAxis );
101 void refineNodeFromJson( QgsTiledSceneNode *node,
const QUrl &baseUrl,
const json &json );
103 QgsTiledSceneTile
rootTile() const final;
104 QgsTiledSceneTile getTile(
long long id ) final;
105 long long parentTileId(
long long id ) const final;
106 QVector<
long long > childTileIds(
long long id ) const final;
107 QVector<
long long > getTiles( const QgsTiledSceneRequest &request ) final;
108 Qgis::TileChildrenAvailability childAvailability(
long long id ) const final;
109 bool fetchHierarchy(
long long id, QgsFeedback *feedback =
nullptr ) final;
112 QByteArray fetchContent( const QString &uri, QgsFeedback *feedback =
nullptr ) final;
115 enum class TileContentFormat
121 mutable QRecursiveMutex mLock;
122 QgsCoordinateTransformContext mTransformContext;
123 std::unique_ptr< QgsTiledSceneNode > mRootNode;
124 QMap< long long, QgsTiledSceneNode * > mNodeMap;
125 QMap< long long, TileContentFormat > mTileContentFormats;
127 QgsHttpHeaders mHeaders;
128 long long mNextTileId = 0;
131class QgsCesiumTilesDataProviderSharedData
134 QgsCesiumTilesDataProviderSharedData();
135 void initialize(
const QString &tileset,
const QUrl &rootUrl,
const QgsCoordinateTransformContext &transformContext,
const QString &authCfg,
const QgsHttpHeaders &headers );
137 QgsCoordinateReferenceSystem mLayerCrs;
138 QgsCoordinateReferenceSystem mSceneCrs;
139 QgsTiledSceneBoundingVolume mBoundingVolume;
141 QgsRectangle mExtent;
142 nlohmann::json mTileset;
143 QgsDoubleRange mZRange;
145 QgsTiledSceneIndex mIndex;
147 QgsLayerMetadata mLayerMetadata;
149 QReadWriteLock mReadWriteLock;
159 const std::string gltfUpAxisString = json.get<std::string>();
160 if ( gltfUpAxisString ==
"z" || gltfUpAxisString ==
"Z" )
164 else if ( gltfUpAxisString ==
"y" || gltfUpAxisString ==
"Y" )
168 else if ( gltfUpAxisString ==
"x" || gltfUpAxisString ==
"X" )
172 QgsDebugError( u
"Unsupported gltfUpAxis value: %1"_s.arg( QString::fromStdString( gltfUpAxisString ) ) );
177 : mTransformContext( transformContext )
178 , mAuthCfg( authCfg )
179 , mHeaders( headers )
182 if ( tileset.contains(
"asset" ) )
184 const auto &assetJson = tileset[
"asset"];
185 if ( assetJson.contains(
"gltfUpAxis" ) )
187 gltfUpAxis = axisFromJson( assetJson[
"gltfUpAxis"] );
191 mRootNode = nodeFromJson( tileset[
"root"], rootUrl,
nullptr, gltfUpAxis );
194std::unique_ptr< QgsTiledSceneTile > QgsCesiumTiledSceneIndex::tileFromJson(
const json &json,
const QUrl &baseUrl,
const QgsTiledSceneTile *parent,
Qgis::Axis gltfUpAxis )
196 auto tile = std::make_unique< QgsTiledSceneTile >( mNextTileId++ );
198 tile->setBaseUrl( baseUrl );
200 { u
"gltfUpAxis"_s,
static_cast< int >( gltfUpAxis ) },
201 { u
"contentFormat"_s, u
"cesiumtiles"_s },
205 if ( json.contains(
"transform" ) && !json[
"transform"].is_null() )
207 const auto &transformJson = json[
"transform"];
208 double *ptr = transform.
data();
209 for (
int i = 0; i < 16; ++i )
210 ptr[i] = transformJson[i].get<double>();
214 transform = *parent->
transform() * transform;
217 else if ( parent && parent->
transform() )
222 tile->setTransform( transform );
224 const auto &boundingVolume = json[
"boundingVolume"];
226 if ( boundingVolume.contains(
"region" ) )
229 if ( !rootRegion.
isNull() )
231 if ( rootRegion.
width() > 20 || rootRegion.
height() > 20 )
238 QVector< QgsVector3D > corners = rootRegion.
corners();
246 for (
int i = 0; i < 8; ++i )
249 x.append( corner.
x() );
250 y.append( corner.
y() );
251 z.append( corner.
z() );
254 ct.setBallparkTransformsAreAppropriate(
true );
257 ct.transformInPlace( x, y, z );
261 QgsDebugError( u
"Cannot transform region bounding volume"_s );
264 const auto minMaxX = std::minmax_element( x.constBegin(), x.constEnd() );
265 const auto minMaxY = std::minmax_element( y.constBegin(), y.constEnd() );
266 const auto minMaxZ = std::minmax_element( z.constBegin(), z.constEnd() );
273 else if ( boundingVolume.contains(
"box" ) )
283 else if ( boundingVolume.contains(
"sphere" ) )
297 tile->setBoundingVolume( volume );
299 if ( json.contains(
"geometricError" ) )
300 tile->setGeometricError( json[
"geometricError"].get< double >() );
301 if ( json.contains(
"refine" ) )
303 if ( json[
"refine"] ==
"ADD" )
305 else if ( json[
"refine"] ==
"REPLACE" )
314 if ( json.contains(
"content" ) && !json[
"content"].is_null() )
316 const auto &contentJson = json[
"content"];
320 if ( contentJson.contains(
"uri" ) && !contentJson[
"uri"].is_null() )
322 QString relativeUri = QString::fromStdString( contentJson[
"uri"].get<std::string>() );
323 contentUri = baseUrl.resolved( QUrl( relativeUri ) ).toString();
325 if ( baseUrl.hasQuery() && QUrl( relativeUri ).isRelative() )
326 contentUri = appendQueryFromBaseUrl( contentUri, baseUrl );
328 else if ( contentJson.contains(
"url" ) && !contentJson[
"url"].is_null() )
330 QString relativeUri = QString::fromStdString( contentJson[
"url"].get<std::string>() );
331 contentUri = baseUrl.resolved( QUrl( relativeUri ) ).toString();
333 if ( baseUrl.hasQuery() && QUrl( relativeUri ).isRelative() )
334 contentUri = appendQueryFromBaseUrl( contentUri, baseUrl );
336 if ( !contentUri.isEmpty() )
338 tile->setResources( { { u
"content"_s, contentUri } } );
345std::unique_ptr<QgsTiledSceneNode> QgsCesiumTiledSceneIndex::nodeFromJson(
const json &json,
const QUrl &baseUrl,
QgsTiledSceneNode *parent,
Qgis::Axis gltfUpAxis )
347 std::unique_ptr< QgsTiledSceneTile > tile = tileFromJson( json, baseUrl, parent ? parent->
tile() :
nullptr, gltfUpAxis );
348 auto newNode = std::make_unique< QgsTiledSceneNode >( tile.release() );
349 mNodeMap.insert( newNode->tile()->id(), newNode.get() );
351 if ( json.contains(
"children" ) )
353 for (
const auto &childJson : json[
"children"] )
355 nodeFromJson( childJson, baseUrl, newNode.get(), gltfUpAxis );
361 parent->
addChild( newNode.release() );
370void QgsCesiumTiledSceneIndex::refineNodeFromJson(
QgsTiledSceneNode *node,
const QUrl &baseUrl,
const json &json )
372 const auto &rootTileJson = json[
"root"];
375 if ( json.contains(
"asset" ) )
377 const auto &assetJson = json[
"asset"];
378 if ( assetJson.contains(
"gltfUpAxis" ) )
380 gltfUpAxis = axisFromJson( assetJson[
"gltfUpAxis"] );
384 std::unique_ptr< QgsTiledSceneTile > newTile = tileFromJson( rootTileJson, baseUrl, node->
tile(), gltfUpAxis );
394 if ( newTile->transform() )
397 if ( rootTileJson.contains(
"children" ) )
399 for (
const auto &childJson : rootTileJson[
"children"] )
401 nodeFromJson( childJson, baseUrl, node, gltfUpAxis );
408 QMutexLocker locker( &mLock );
414 QMutexLocker locker( &mLock );
415 auto it = mNodeMap.constFind(
id );
416 if ( it != mNodeMap.constEnd() )
418 return *( it.value()->tile() );
424long long QgsCesiumTiledSceneIndex::parentTileId(
long long id )
const
426 QMutexLocker locker( &mLock );
427 auto it = mNodeMap.constFind(
id );
428 if ( it != mNodeMap.constEnd() )
432 return parent->
tile()->
id();
439QVector< long long > QgsCesiumTiledSceneIndex::childTileIds(
long long id )
const
441 QMutexLocker locker( &mLock );
442 auto it = mNodeMap.constFind(
id );
443 if ( it != mNodeMap.constEnd() )
445 QVector< long long > childIds;
446 const QList< QgsTiledSceneNode * > children = it.value()->children();
447 childIds.reserve( children.size() );
450 childIds << child->tile()->id();
460 QVector< long long > results;
463 traverseNode = [&request, &traverseNode, &results,
this](
QgsTiledSceneNode *node ) {
478 QList< QgsTiledSceneNode * > children = node->
children();
479 if ( children.empty() )
481 switch ( childAvailability( tile->
id() ) )
491 if ( fetchHierarchy( tile->
id() ), request.
feedback() )
506 traverseNode( child );
513 results << tile->
id();
518 if ( children.empty() )
519 results << tile->
id();
525 results << tile->
id();
529 QMutexLocker locker( &mLock );
533 traverseNode( mRootNode.get() );
538 if ( it != mNodeMap.constEnd() )
540 traverseNode( it.value() );
550 QMutexLocker locker( &mLock );
552 auto it = mNodeMap.constFind(
id );
553 if ( it == mNodeMap.constEnd() )
556 if ( !it.value()->children().isEmpty() )
559 contentUri = it.value()->tile()->resources().value( u
"content"_s ).toString();
563 auto it = mTileContentFormats.constFind(
id );
564 if ( it != mTileContentFormats.constEnd() )
566 switch ( it.value() )
568 case TileContentFormat::NotJson:
570 case TileContentFormat::Json:
577 if ( contentUri.isEmpty() )
587 const thread_local QRegularExpression isJsonRx( u
".*\\.json(?:\\?.*)?$"_s, QRegularExpression::PatternOption::CaseInsensitiveOption );
588 if ( isJsonRx.match( contentUri ).hasMatch() )
592 const thread_local QRegularExpression antiCandidateRx( u
".*\\.(gltf|glb|b3dm|i3dm|pnts|cmpt|bin|glbin|glbuf|png|jpeg|jpg)(?:\\?.*)?$"_s, QRegularExpression::PatternOption::CaseInsensitiveOption );
593 if ( antiCandidateRx.match( contentUri ).hasMatch() )
601bool QgsCesiumTiledSceneIndex::fetchHierarchy(
long long id,
QgsFeedback *feedback )
603 QMutexLocker locker( &mLock );
604 auto it = mNodeMap.constFind(
id );
605 if ( it == mNodeMap.constEnd() )
611 auto it = mTileContentFormats.constFind(
id );
612 if ( it != mTileContentFormats.constEnd() )
614 switch ( it.value() )
616 case TileContentFormat::NotJson:
618 case TileContentFormat::Json:
624 const QString contentUri = it.value()->tile()->resources().value( u
"content"_s ).toString();
627 if ( contentUri.isEmpty() )
631 const QByteArray subTile = retrieveContent( contentUri, feedback );
632 if ( !subTile.isEmpty() )
639 const auto subTileJson = json::parse( subTile.toStdString() );
640 QMutexLocker locker( &mLock );
641 refineNodeFromJson( it.value(), QUrl( contentUri ), subTileJson );
642 mTileContentFormats.insert(
id, TileContentFormat::Json );
645 catch ( json::parse_error & )
647 QMutexLocker locker( &mLock );
648 mTileContentFormats.insert(
id, TileContentFormat::NotJson );
656 mTileContentFormats.insert(
id, TileContentFormat::NotJson );
661QByteArray QgsCesiumTiledSceneIndex::fetchContent(
const QString &uri,
QgsFeedback *feedback )
665 if ( uri.startsWith(
"http" ) )
667 QNetworkRequest networkRequest = QNetworkRequest( url );
669 networkRequest.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
670 networkRequest.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
672 mHeaders.updateNetworkRequest( networkRequest );
674 if ( QThread::currentThread() == QApplication::instance()->thread() )
697 return reply->data();
700 else if ( url.isLocalFile() && QFile::exists( url.toLocalFile() ) )
702 QFile file( url.toLocalFile() );
703 if ( file.open( QIODevice::ReadOnly ) )
705 return file.readAll();
716QgsCesiumTilesDataProviderSharedData::QgsCesiumTilesDataProviderSharedData()
722 mTileset = json::parse( tileset.toStdString() );
723 if ( !mTileset.contains(
"root" ) )
725 mError = QObject::tr(
"JSON is not a valid Cesium 3D Tiles source (does not contain \"root\" value)" );
729 mLayerMetadata.setType( u
"dataset"_s );
731 if ( mTileset.contains(
"asset" ) )
733 const auto &asset = mTileset[
"asset"];
734 if ( asset.contains(
"tilesetVersion" ) )
738 const QString tilesetVersion = QString::fromStdString( asset[
"tilesetVersion"].get<std::string>() );
739 mLayerMetadata.setIdentifier( tilesetVersion );
741 catch ( json::type_error & )
743 QgsDebugError( u
"Error when parsing tilesetVersion value"_s );
748 mIndex =
QgsTiledSceneIndex(
new QgsCesiumTiledSceneIndex( mTileset, rootUrl, authCfg, headers, transformContext ) );
752 const auto &root = mTileset[
"root"];
764 const auto &rootBoundingVolume = root[
"boundingVolume"];
767 if ( root.contains(
"transform" ) && !root[
"transform"].is_null() )
769 const auto &transformJson = root[
"transform"];
770 double *ptr = rootTransform.
data();
771 for (
int i = 0; i < 16; ++i )
772 ptr[i] = transformJson[i].get<double>();
775 if ( rootBoundingVolume.contains(
"region" ) )
778 if ( !rootRegion.
isNull() )
783 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
790 mLayerMetadata.setCrs( mSceneCrs );
793 spatialExtent.
bounds = rootRegion;
796 else if ( rootBoundingVolume.contains(
"box" ) )
807 mLayerMetadata.setCrs( mSceneCrs );
810 mBoundingVolume.transform( rootTransform );
814 ct.setBallparkTransformsAreAppropriate(
true );
815 const QgsBox3D rootRegion = mBoundingVolume.bounds( ct );
817 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
822 std::unique_ptr< QgsAbstractGeometry > extent2D( mBoundingVolume.as2DGeometry( ct ) );
823 mExtent = extent2D->boundingBox();
827 QgsDebugError( u
"Caught transform exception when transforming boundingVolume"_s );
831 spatialExtent.
bounds = mBoundingVolume.bounds();
834 else if ( rootBoundingVolume.contains(
"sphere" ) )
845 mLayerMetadata.setCrs( mSceneCrs );
853 ct.setBallparkTransformsAreAppropriate(
true );
854 const QgsBox3D rootRegion = mBoundingVolume.bounds( ct );
856 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
861 std::unique_ptr< QgsAbstractGeometry > extent2D( mBoundingVolume.as2DGeometry( ct ) );
862 mExtent = extent2D->boundingBox();
866 QgsDebugError( u
"Caught transform exception when transforming boundingVolume"_s );
870 spatialExtent.
bounds = mBoundingVolume.bounds();
875 mError = QObject::tr(
"JSON is not a valid Cesium 3D Tiles source (unsupported boundingVolume format)" );
881 mLayerMetadata.setExtent( layerExtent );
891QgsCesiumTilesDataProvider::QgsCesiumTilesDataProvider(
const QString &uri,
const ProviderOptions &providerOptions,
Qgis::DataProviderReadFlags flags )
893 , mShared( std::make_shared< QgsCesiumTilesDataProviderSharedData >() )
903QgsCesiumTilesDataProvider::QgsCesiumTilesDataProvider(
const QgsCesiumTilesDataProvider &other )
905 , mIsValid( other.mIsValid )
906 , mAuthCfg( other.mAuthCfg )
907 , mHeaders( other.mHeaders )
910 mShared = other.mShared;
915 return mProviderFlags;
923QgsCesiumTilesDataProvider::~QgsCesiumTilesDataProvider() =
default;
925QgsCesiumTilesDataProvider *QgsCesiumTilesDataProvider::clone()
const
928 return new QgsCesiumTilesDataProvider( *
this );
931bool QgsCesiumTilesDataProvider::init()
936 const QString uri = dataSourceUri();
938 if ( uri.startsWith(
"ion://"_L1 ) )
941 const QString assetId = QUrlQuery( url ).queryItemValue( u
"assetId"_s );
942 const QString accessToken = QUrlQuery( url ).queryItemValue( u
"accessToken"_s );
944 const QString CESIUM_ION_URL = u
"https://api.cesium.com/"_s;
948 const QString assetInfoEndpoint = CESIUM_ION_URL + u
"v1/assets/%1"_s.arg( assetId );
949 QNetworkRequest request = QNetworkRequest( assetInfoEndpoint );
951 if ( !accessToken.isEmpty() )
952 request.setRawHeader(
"Authorization", u
"Bearer %1"_s.arg( accessToken ).toLocal8Bit() );
955 if ( accessToken.isEmpty() )
958 switch ( networkRequest.
get( request ) )
971 const json assetInfoJson = json::parse( content.
content().toStdString() );
972 if ( assetInfoJson[
"type"] !=
"3DTILES" )
974 appendError(
QgsErrorMessage( tr(
"Only ion 3D Tiles content can be accessed, not %1" ).arg( QString::fromStdString( assetInfoJson[
"type"].get<std::string>() ) ) ) );
978 const QString name = QString::fromStdString( assetInfoJson[
"name"].get<std::string>() );
979 if ( name.compare(
"Google Photorealistic 3D Tiles"_L1, Qt::CaseInsensitive ) == 0 )
987 mShared->mLayerMetadata.setTitle( name );
988 mShared->mLayerMetadata.setAbstract( QString::fromStdString( assetInfoJson[
"description"].get<std::string>() ) );
989 const QString attribution = QString::fromStdString( assetInfoJson[
"attribution"].get<std::string>() );
990 if ( !attribution.isEmpty() )
991 mShared->mLayerMetadata.setRights( { attribution } );
993 mShared->mLayerMetadata.setDateTime(
Qgis::MetadataDateType::Created, QDateTime::fromString( QString::fromStdString( assetInfoJson[
"dateAdded"].get<std::string>() ), Qt::DateFormat::ISODate ) );
998 const QString tileAccessEndpoint = CESIUM_ION_URL + u
"v1/assets/%1/endpoint"_s.arg( assetId );
999 QNetworkRequest request = QNetworkRequest( tileAccessEndpoint );
1001 if ( !accessToken.isEmpty() )
1002 request.setRawHeader(
"Authorization", u
"Bearer %1"_s.arg( accessToken ).toLocal8Bit() );
1005 if ( accessToken.isEmpty() )
1008 switch ( networkRequest.
get( request ) )
1021 const json tileAccessJson = json::parse( content.
content().toStdString() );
1023 if ( tileAccessJson.contains(
"url" ) )
1025 tileSetUri = QString::fromStdString( tileAccessJson[
"url"].get<std::string>() );
1027 else if ( tileAccessJson.contains(
"options" ) )
1029 const auto &optionsJson = tileAccessJson[
"options"];
1030 if ( optionsJson.contains(
"url" ) )
1032 tileSetUri = QString::fromStdString( optionsJson[
"url"].get<std::string>() );
1036 if ( tileAccessJson.contains(
"accessToken" ) )
1040 mHeaders.insert( u
"Authorization"_s, u
"Bearer %1"_s.arg( QString::fromStdString( tileAccessJson[
"accessToken"].get<std::string>() ) ) );
1049 tileSetUri = dsUri.
param( u
"url"_s );
1052 if ( !tileSetUri.isEmpty() )
1054 const QUrl url( tileSetUri );
1056 QNetworkRequest request = QNetworkRequest( url );
1062 switch ( networkRequest.
get( request ) )
1076 mShared->initialize( content.
content(), tileSetUri, transformContext(), mAuthCfg, mHeaders );
1083 const QFileInfo fi( dataSourceUri() );
1086 QFile file( dataSourceUri() );
1087 if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) )
1089 const QByteArray raw = file.readAll();
1090 mShared->initialize( raw, QUrl::fromLocalFile( dataSourceUri() ), transformContext(), mAuthCfg, mHeaders );
1103 if ( !mShared->mIndex.isValid() )
1105 appendError( mShared->mError );
1116 return mShared->mLayerCrs;
1124 return mShared->mExtent;
1127bool QgsCesiumTilesDataProvider::isValid()
const
1134QString QgsCesiumTilesDataProvider::name()
const
1138 return PROVIDER_KEY;
1141QString QgsCesiumTilesDataProvider::description()
const
1145 return QObject::tr(
"Cesium 3D Tiles" );
1148QString QgsCesiumTilesDataProvider::htmlMetadata()
const
1155 if ( mShared->mTileset.contains(
"asset" ) )
1157 const auto &asset = mShared->mTileset[
"asset"];
1158 if ( asset.contains(
"version" ) )
1160 const QString version = QString::fromStdString( asset[
"version"].get<std::string>() );
1161 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"3D Tiles Version" ) % u
"</td><td>%1</a>"_s.arg( version ) % u
"</td></tr>\n"_s;
1164 if ( asset.contains(
"tilesetVersion" ) )
1168 const QString tilesetVersion = QString::fromStdString( asset[
"tilesetVersion"].get<std::string>() );
1169 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Tileset Version" ) % u
"</td><td>%1</a>"_s.arg( tilesetVersion ) % u
"</td></tr>\n"_s;
1171 catch ( json::type_error & )
1173 QgsDebugError( u
"Error when parsing tilesetVersion value"_s );
1177 if ( asset.contains(
"generator" ) )
1179 const QString generator = QString::fromStdString( asset[
"generator"].get<std::string>() );
1180 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Tileset Generator" ) % u
"</td><td>%1</a>"_s.arg( generator ) % u
"</td></tr>\n"_s;
1183 if ( mShared->mTileset.contains(
"extensionsRequired" ) )
1185 QStringList extensions;
1186 for (
const auto &item : mShared->mTileset[
"extensionsRequired"] )
1188 extensions << QString::fromStdString( item.get<std::string>() );
1190 if ( !extensions.isEmpty() )
1192 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Extensions Required" ) % u
"</td><td><ul><li>%1</li></ul></a>"_s.arg( extensions.join(
"</li><li>"_L1 ) ) % u
"</td></tr>\n"_s;
1195 if ( mShared->mTileset.contains(
"extensionsUsed" ) )
1197 QStringList extensions;
1198 for (
const auto &item : mShared->mTileset[
"extensionsUsed"] )
1200 extensions << QString::fromStdString( item.get<std::string>() );
1202 if ( !extensions.isEmpty() )
1204 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Extensions Used" ) % u
"</td><td><ul><li>%1</li></ul></a>"_s.arg( extensions.join(
"</li><li>"_L1 ) ) % u
"</td></tr>\n"_s;
1208 if ( !mShared->mZRange.isInfinite() )
1210 metadata += u
"<tr><td class=\"highlight\">"_s
1212 % u
"</td><td>%1 - %2</a>"_s.arg( QLocale().toString( mShared->mZRange.lower() ), QLocale().toString( mShared->mZRange.upper() ) )
1213 % u
"</td></tr>\n"_s;
1226 return mShared->mLayerMetadata;
1236 return mShared->mSceneCrs;
1247 return mShared ? mShared->mBoundingVolume : nullVolume;
1257 return mShared->mIndex;
1267 return mShared->mZRange;
1275QgsCesiumTilesProviderMetadata::QgsCesiumTilesProviderMetadata()
1279QIcon QgsCesiumTilesProviderMetadata::icon()
const
1286 return new QgsCesiumTilesDataProvider( uri, options, flags );
1291 const QVariantMap parts = decodeUri( uri );
1292 if ( parts.value( u
"file-name"_s ).toString().compare(
"tileset.json"_L1, Qt::CaseInsensitive ) == 0 )
1307int QgsCesiumTilesProviderMetadata::priorityForUri(
const QString &uri )
const
1309 const QVariantMap parts = decodeUri( uri );
1310 if ( parts.value( u
"file-name"_s ).toString().compare(
"tileset.json"_L1, Qt::CaseInsensitive ) == 0 )
1316QList<Qgis::LayerType> QgsCesiumTilesProviderMetadata::validLayerTypesForUri(
const QString &uri )
const
1318 const QVariantMap parts = decodeUri( uri );
1319 if ( parts.value( u
"file-name"_s ).toString().compare(
"tileset.json"_L1, Qt::CaseInsensitive ) == 0 )
1322 return QList< Qgis::LayerType>();
1325QVariantMap QgsCesiumTilesProviderMetadata::decodeUri(
const QString &uri )
const
1327 QVariantMap uriComponents;
1328 QUrl url = QUrl::fromUserInput( uri );
1329 uriComponents.insert( u
"file-name"_s, url.fileName() );
1330 uriComponents.insert( u
"path"_s, uri );
1331 return uriComponents;
1347 return QObject::tr(
"Cesium 3D Tiles" ) + u
" (tileset.json TILESET.JSON)"_s;
1354 return FileBasedUris;
1357QList<Qgis::LayerType> QgsCesiumTilesProviderMetadata::supportedLayerTypes()
const
1362QString QgsCesiumTilesProviderMetadata::encodeUri(
const QVariantMap &parts )
const
1364 const QString path = parts.value( u
"path"_s ).toString();
1370 return ProviderMetadataCapability::LayerTypesForUri | ProviderMetadataCapability::PriorityForUri | 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...
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
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.
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.