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();
93 QgsCesiumTiledSceneIndex(
96 const QString &authCfg,
97 const QgsHttpHeaders &headers,
98 const QgsCoordinateTransformContext &transformContext );
100 std::unique_ptr< QgsTiledSceneTile > tileFromJson(
const json &node,
const QUrl &baseUrl,
const QgsTiledSceneTile *parent,
Qgis::Axis gltfUpAxis );
106 std::unique_ptr< QgsTiledSceneNode > nodeFromJson(
const json &node,
const QUrl &baseUrl, QgsTiledSceneNode *parent,
Qgis::Axis gltfUpAxis );
107 void refineNodeFromJson( QgsTiledSceneNode *node,
const QUrl &baseUrl,
const json &json );
109 QgsTiledSceneTile
rootTile() const final;
110 QgsTiledSceneTile getTile(
long long id ) final;
111 long long parentTileId(
long long id ) const final;
112 QVector<
long long > childTileIds(
long long id ) const final;
113 QVector<
long long > getTiles( const QgsTiledSceneRequest &request ) final;
114 Qgis::TileChildrenAvailability childAvailability(
long long id ) const final;
115 bool fetchHierarchy(
long long id, QgsFeedback *feedback =
nullptr ) final;
119 QByteArray fetchContent( const QString &uri, QgsFeedback *feedback =
nullptr ) final;
123 enum class TileContentFormat
129 mutable QRecursiveMutex mLock;
130 QgsCoordinateTransformContext mTransformContext;
131 std::unique_ptr< QgsTiledSceneNode > mRootNode;
132 QMap< long long, QgsTiledSceneNode * > mNodeMap;
133 QMap< long long, TileContentFormat > mTileContentFormats;
135 QgsHttpHeaders mHeaders;
136 long long mNextTileId = 0;
140class QgsCesiumTilesDataProviderSharedData
143 QgsCesiumTilesDataProviderSharedData();
144 void initialize(
const QString &tileset,
146 const QgsCoordinateTransformContext &transformContext,
147 const QString &authCfg,
148 const QgsHttpHeaders &headers );
150 QgsCoordinateReferenceSystem mLayerCrs;
151 QgsCoordinateReferenceSystem mSceneCrs;
152 QgsTiledSceneBoundingVolume mBoundingVolume;
154 QgsRectangle mExtent;
155 nlohmann::json mTileset;
156 QgsDoubleRange mZRange;
158 QgsTiledSceneIndex mIndex;
160 QgsLayerMetadata mLayerMetadata;
162 QReadWriteLock mReadWriteLock;
173 const std::string gltfUpAxisString = json.get<std::string>();
174 if ( gltfUpAxisString ==
"z" || gltfUpAxisString ==
"Z" )
178 else if ( gltfUpAxisString ==
"y" || gltfUpAxisString ==
"Y" )
182 else if ( gltfUpAxisString ==
"x" || gltfUpAxisString ==
"X" )
186 QgsDebugError( u
"Unsupported gltfUpAxis value: %1"_s.arg( QString::fromStdString( gltfUpAxisString ) ) );
191 : mTransformContext( transformContext )
192 , mAuthCfg( authCfg )
193 , mHeaders( headers )
196 if ( tileset.contains(
"asset" ) )
198 const auto &assetJson = tileset[
"asset"];
199 if ( assetJson.contains(
"gltfUpAxis" ) )
201 gltfUpAxis = axisFromJson( assetJson[
"gltfUpAxis"] );
205 mRootNode = nodeFromJson( tileset[
"root" ], rootUrl,
nullptr, gltfUpAxis );
208std::unique_ptr< QgsTiledSceneTile > QgsCesiumTiledSceneIndex::tileFromJson(
const json &json,
const QUrl &baseUrl,
const QgsTiledSceneTile *parent,
Qgis::Axis gltfUpAxis )
210 auto tile = std::make_unique< QgsTiledSceneTile >( mNextTileId++ );
212 tile->setBaseUrl( baseUrl );
215 { u
"gltfUpAxis"_s,
static_cast< int >( gltfUpAxis ) },
216 { u
"contentFormat"_s, u
"cesiumtiles"_s },
220 if ( json.contains(
"transform" ) && !json[
"transform"].is_null() )
222 const auto &transformJson = json[
"transform"];
223 double *ptr = transform.
data();
224 for (
int i = 0; i < 16; ++i )
225 ptr[i] = transformJson[i].get<double>();
229 transform = *parent->
transform() * transform;
232 else if ( parent && parent->
transform() )
237 tile->setTransform( transform );
239 const auto &boundingVolume = json[
"boundingVolume" ];
241 if ( boundingVolume.contains(
"region" ) )
244 if ( !rootRegion.
isNull() )
246 if ( rootRegion.
width() > 20 || rootRegion.
height() > 20 )
253 QVector< QgsVector3D > corners = rootRegion.
corners();
261 for (
int i = 0; i < 8; ++i )
264 x.append( corner.
x() );
265 y.append( corner.
y() );
266 z.append( corner.
z() );
269 ct.setBallparkTransformsAreAppropriate(
true );
272 ct.transformInPlace( x, y, z );
276 QgsDebugError( u
"Cannot transform region bounding volume"_s );
279 const auto minMaxX = std::minmax_element( x.constBegin(), x.constEnd() );
280 const auto minMaxY = std::minmax_element( y.constBegin(), y.constEnd() );
281 const auto minMaxZ = std::minmax_element( z.constBegin(), z.constEnd() );
288 else if ( boundingVolume.contains(
"box" ) )
298 else if ( boundingVolume.contains(
"sphere" ) )
312 tile->setBoundingVolume( volume );
314 if ( json.contains(
"geometricError" ) )
315 tile->setGeometricError( json[
"geometricError"].get< double >() );
316 if ( json.contains(
"refine" ) )
318 if ( json[
"refine"] ==
"ADD" )
320 else if ( json[
"refine"] ==
"REPLACE" )
329 if ( json.contains(
"content" ) && !json[
"content"].is_null() )
331 const auto &contentJson = json[
"content"];
335 if ( contentJson.contains(
"uri" ) && !contentJson[
"uri"].is_null() )
337 QString relativeUri = QString::fromStdString( contentJson[
"uri"].get<std::string>() );
338 contentUri = baseUrl.resolved( QUrl( relativeUri ) ).toString();
340 if ( baseUrl.hasQuery() && QUrl( relativeUri ).isRelative() )
341 contentUri = appendQueryFromBaseUrl( contentUri, baseUrl );
343 else if ( contentJson.contains(
"url" ) && !contentJson[
"url"].is_null() )
345 QString relativeUri = QString::fromStdString( contentJson[
"url"].get<std::string>() );
346 contentUri = baseUrl.resolved( QUrl( relativeUri ) ).toString();
348 if ( baseUrl.hasQuery() && QUrl( relativeUri ).isRelative() )
349 contentUri = appendQueryFromBaseUrl( contentUri, baseUrl );
351 if ( !contentUri.isEmpty() )
353 tile->setResources( {{ u
"content"_s, contentUri } } );
360std::unique_ptr<QgsTiledSceneNode> QgsCesiumTiledSceneIndex::nodeFromJson(
const json &json,
const QUrl &baseUrl,
QgsTiledSceneNode *parent,
Qgis::Axis gltfUpAxis )
362 std::unique_ptr< QgsTiledSceneTile > tile = tileFromJson( json, baseUrl, parent ? parent->
tile() :
nullptr, gltfUpAxis );
363 auto newNode = std::make_unique< QgsTiledSceneNode >( tile.release() );
364 mNodeMap.insert( newNode->tile()->id(), newNode.get() );
366 if ( json.contains(
"children" ) )
368 for (
const auto &childJson : json[
"children"] )
370 nodeFromJson( childJson, baseUrl, newNode.get(), gltfUpAxis );
376 parent->
addChild( newNode.release() );
385void QgsCesiumTiledSceneIndex::refineNodeFromJson(
QgsTiledSceneNode *node,
const QUrl &baseUrl,
const json &json )
387 const auto &rootTileJson = json[
"root"];
390 if ( json.contains(
"asset" ) )
392 const auto &assetJson = json[
"asset"];
393 if ( assetJson.contains(
"gltfUpAxis" ) )
395 gltfUpAxis = axisFromJson( assetJson[
"gltfUpAxis"] );
399 std::unique_ptr< QgsTiledSceneTile > newTile = tileFromJson( rootTileJson, baseUrl, node->
tile(), gltfUpAxis );
409 if ( newTile->transform() )
412 if ( rootTileJson.contains(
"children" ) )
414 for (
const auto &childJson : rootTileJson[
"children"] )
416 nodeFromJson( childJson, baseUrl, node, gltfUpAxis );
423 QMutexLocker locker( &mLock );
429 QMutexLocker locker( &mLock );
430 auto it = mNodeMap.constFind(
id );
431 if ( it != mNodeMap.constEnd() )
433 return *( it.value()->tile() );
439long long QgsCesiumTiledSceneIndex::parentTileId(
long long id )
const
441 QMutexLocker locker( &mLock );
442 auto it = mNodeMap.constFind(
id );
443 if ( it != mNodeMap.constEnd() )
447 return parent->
tile()->
id();
454QVector< long long > QgsCesiumTiledSceneIndex::childTileIds(
long long id )
const
456 QMutexLocker locker( &mLock );
457 auto it = mNodeMap.constFind(
id );
458 if ( it != mNodeMap.constEnd() )
460 QVector< long long > childIds;
461 const QList< QgsTiledSceneNode * > children = it.value()->children();
462 childIds.reserve( children.size() );
465 childIds << child->tile()->id();
475 QVector< long long > results;
478 traverseNode = [&request, &traverseNode, &results,
this](
QgsTiledSceneNode * node )
494 QList< QgsTiledSceneNode * > children = node->
children();
495 if ( children.empty() )
497 switch ( childAvailability( tile->
id() ) )
507 if ( fetchHierarchy( tile->
id() ), request.
feedback() )
522 traverseNode( child );
529 results << tile->
id();
534 if ( children.empty() )
535 results << tile->
id();
541 results << tile->
id();
546 QMutexLocker locker( &mLock );
550 traverseNode( mRootNode.get() );
555 if ( it != mNodeMap.constEnd() )
557 traverseNode( it.value() );
567 QMutexLocker locker( &mLock );
569 auto it = mNodeMap.constFind(
id );
570 if ( it == mNodeMap.constEnd() )
573 if ( !it.value()->children().isEmpty() )
576 contentUri = it.value()->tile()->resources().value( u
"content"_s ).toString();
580 auto it = mTileContentFormats.constFind(
id );
581 if ( it != mTileContentFormats.constEnd() )
583 switch ( it.value() )
585 case TileContentFormat::NotJson:
587 case TileContentFormat::Json:
594 if ( contentUri.isEmpty() )
604 const thread_local QRegularExpression isJsonRx( u
".*\\.json(?:\\?.*)?$"_s, QRegularExpression::PatternOption::CaseInsensitiveOption );
605 if ( isJsonRx.match( contentUri ).hasMatch() )
609 const thread_local QRegularExpression antiCandidateRx( u
".*\\.(gltf|glb|b3dm|i3dm|pnts|cmpt|bin|glbin|glbuf|png|jpeg|jpg)(?:\\?.*)?$"_s, QRegularExpression::PatternOption::CaseInsensitiveOption );
610 if ( antiCandidateRx.match( contentUri ).hasMatch() )
618bool QgsCesiumTiledSceneIndex::fetchHierarchy(
long long id,
QgsFeedback *feedback )
620 QMutexLocker locker( &mLock );
621 auto it = mNodeMap.constFind(
id );
622 if ( it == mNodeMap.constEnd() )
628 auto it = mTileContentFormats.constFind(
id );
629 if ( it != mTileContentFormats.constEnd() )
631 switch ( it.value() )
633 case TileContentFormat::NotJson:
635 case TileContentFormat::Json:
641 const QString contentUri = it.value()->tile()->resources().value( u
"content"_s ).toString();
644 if ( contentUri.isEmpty() )
648 const QByteArray subTile = retrieveContent( contentUri, feedback );
649 if ( !subTile.isEmpty() )
656 const auto subTileJson = json::parse( subTile.toStdString() );
657 QMutexLocker locker( &mLock );
658 refineNodeFromJson( it.value(), QUrl( contentUri ), subTileJson );
659 mTileContentFormats.insert(
id, TileContentFormat::Json );
662 catch ( json::parse_error & )
664 QMutexLocker locker( &mLock );
665 mTileContentFormats.insert(
id, TileContentFormat::NotJson );
673 mTileContentFormats.insert(
id, TileContentFormat::NotJson );
678QByteArray QgsCesiumTiledSceneIndex::fetchContent(
const QString &uri,
QgsFeedback *feedback )
682 if ( uri.startsWith(
"http" ) )
684 QNetworkRequest networkRequest = QNetworkRequest( url );
686 networkRequest.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
687 networkRequest.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
689 mHeaders.updateNetworkRequest( networkRequest );
691 if ( QThread::currentThread() == QApplication::instance()->thread() )
695 networkRequest, mAuthCfg,
false, feedback );
715 return reply->data();
718 else if ( url.isLocalFile() && QFile::exists( url.toLocalFile() ) )
720 QFile file( url.toLocalFile() );
721 if ( file.open( QIODevice::ReadOnly ) )
723 return file.readAll();
734QgsCesiumTilesDataProviderSharedData::QgsCesiumTilesDataProviderSharedData()
742 mTileset = json::parse( tileset.toStdString() );
743 if ( !mTileset.contains(
"root" ) )
745 mError = QObject::tr(
"JSON is not a valid Cesium 3D Tiles source (does not contain \"root\" value)" );
749 mLayerMetadata.setType( u
"dataset"_s );
751 if ( mTileset.contains(
"asset" ) )
753 const auto &asset = mTileset[
"asset" ];
754 if ( asset.contains(
"tilesetVersion" ) )
758 const QString tilesetVersion = QString::fromStdString( asset[
"tilesetVersion"].get<std::string>() );
759 mLayerMetadata.setIdentifier( tilesetVersion );
761 catch ( json::type_error & )
763 QgsDebugError( u
"Error when parsing tilesetVersion value"_s );
769 new QgsCesiumTiledSceneIndex(
780 const auto &root = mTileset[
"root" ];
792 const auto &rootBoundingVolume = root[
"boundingVolume" ];
795 if ( root.contains(
"transform" ) && !root[
"transform"].is_null() )
797 const auto &transformJson = root[
"transform"];
798 double *ptr = rootTransform.
data();
799 for (
int i = 0; i < 16; ++i )
800 ptr[i] = transformJson[i].get<double>();
803 if ( rootBoundingVolume.contains(
"region" ) )
806 if ( !rootRegion.
isNull() )
811 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
818 mLayerMetadata.setCrs( mSceneCrs );
821 spatialExtent.
bounds = rootRegion;
824 else if ( rootBoundingVolume.contains(
"box" ) )
835 mLayerMetadata.setCrs( mSceneCrs );
838 mBoundingVolume.transform( rootTransform );
842 ct.setBallparkTransformsAreAppropriate(
true );
843 const QgsBox3D rootRegion = mBoundingVolume.bounds( ct );
845 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
850 std::unique_ptr< QgsAbstractGeometry > extent2D( mBoundingVolume.as2DGeometry( ct ) );
851 mExtent = extent2D->boundingBox();
855 QgsDebugError( u
"Caught transform exception when transforming boundingVolume"_s );
859 spatialExtent.
bounds = mBoundingVolume.bounds();
862 else if ( rootBoundingVolume.contains(
"sphere" ) )
873 mLayerMetadata.setCrs( mSceneCrs );
881 ct.setBallparkTransformsAreAppropriate(
true );
882 const QgsBox3D rootRegion = mBoundingVolume.bounds( ct );
884 if ( !mIndex.rootTile().boundingVolume().box().isNull() )
889 std::unique_ptr< QgsAbstractGeometry > extent2D( mBoundingVolume.as2DGeometry( ct ) );
890 mExtent = extent2D->boundingBox();
894 QgsDebugError( u
"Caught transform exception when transforming boundingVolume"_s );
898 spatialExtent.
bounds = mBoundingVolume.bounds();
903 mError = QObject::tr(
"JSON is not a valid Cesium 3D Tiles source (unsupported boundingVolume format)" );
909 mLayerMetadata.setExtent( layerExtent );
919QgsCesiumTilesDataProvider::QgsCesiumTilesDataProvider(
const QString &uri,
const ProviderOptions &providerOptions,
Qgis::DataProviderReadFlags flags )
921 , mShared( std::make_shared< QgsCesiumTilesDataProviderSharedData >() )
931QgsCesiumTilesDataProvider::QgsCesiumTilesDataProvider(
const QgsCesiumTilesDataProvider &other )
933 , mIsValid( other.mIsValid )
934 , mAuthCfg( other.mAuthCfg )
935 , mHeaders( other.mHeaders )
938 mShared = other.mShared;
943 return mProviderFlags;
951QgsCesiumTilesDataProvider::~QgsCesiumTilesDataProvider() =
default;
953QgsCesiumTilesDataProvider *QgsCesiumTilesDataProvider::clone()
const
956 return new QgsCesiumTilesDataProvider( *
this );
959bool QgsCesiumTilesDataProvider::init()
964 const QString uri = dataSourceUri();
966 if ( uri.startsWith(
"ion://"_L1 ) )
969 const QString assetId = QUrlQuery( url ).queryItemValue( u
"assetId"_s );
970 const QString accessToken = QUrlQuery( url ).queryItemValue( u
"accessToken"_s );
972 const QString CESIUM_ION_URL = u
"https://api.cesium.com/"_s;
976 const QString assetInfoEndpoint = CESIUM_ION_URL + u
"v1/assets/%1"_s.arg( assetId );
977 QNetworkRequest request = QNetworkRequest( assetInfoEndpoint );
979 mHeaders.updateNetworkRequest( request );
980 if ( !accessToken.isEmpty() )
981 request.setRawHeader(
"Authorization", u
"Bearer %1"_s.arg( accessToken ).toLocal8Bit() );
984 if ( accessToken.isEmpty() )
987 switch ( networkRequest.
get( request ) )
1000 const json assetInfoJson = json::parse( content.
content().toStdString() );
1001 if ( assetInfoJson[
"type"] !=
"3DTILES" )
1003 appendError(
QgsErrorMessage( tr(
"Only ion 3D Tiles content can be accessed, not %1" ).arg( QString::fromStdString( assetInfoJson[
"type"].get<std::string>() ) ) ) );
1007 const QString name = QString::fromStdString( assetInfoJson[
"name"].get<std::string>() );
1008 if ( name.compare(
"Google Photorealistic 3D Tiles"_L1, Qt::CaseInsensitive ) == 0 )
1016 mShared->mLayerMetadata.setTitle( name );
1017 mShared->mLayerMetadata.setAbstract( QString::fromStdString( assetInfoJson[
"description"].get<std::string>() ) );
1018 const QString attribution = QString::fromStdString( assetInfoJson[
"attribution"].get<std::string>() );
1019 if ( !attribution.isEmpty() )
1020 mShared->mLayerMetadata.setRights( { attribution } );
1022 mShared->mLayerMetadata.setDateTime(
Qgis::MetadataDateType::Created, QDateTime::fromString( QString::fromStdString( assetInfoJson[
"dateAdded"].get<std::string>() ), Qt::DateFormat::ISODate ) );
1027 const QString tileAccessEndpoint = CESIUM_ION_URL + u
"v1/assets/%1/endpoint"_s.arg( assetId );
1028 QNetworkRequest request = QNetworkRequest( tileAccessEndpoint );
1030 mHeaders.updateNetworkRequest( request );
1031 if ( !accessToken.isEmpty() )
1032 request.setRawHeader(
"Authorization", u
"Bearer %1"_s.arg( accessToken ).toLocal8Bit() );
1035 if ( accessToken.isEmpty() )
1038 switch ( networkRequest.
get( request ) )
1051 const json tileAccessJson = json::parse( content.
content().toStdString() );
1053 if ( tileAccessJson.contains(
"url" ) )
1055 tileSetUri = QString::fromStdString( tileAccessJson[
"url"].get<std::string>() );
1057 else if ( tileAccessJson.contains(
"options" ) )
1059 const auto &optionsJson = tileAccessJson[
"options"];
1060 if ( optionsJson.contains(
"url" ) )
1062 tileSetUri = QString::fromStdString( optionsJson[
"url"].get<std::string>() );
1066 if ( tileAccessJson.contains(
"accessToken" ) )
1070 mHeaders.insert( u
"Authorization"_s,
1071 u
"Bearer %1"_s.arg( QString::fromStdString( tileAccessJson[
"accessToken"].get<std::string>() ) ) );
1080 tileSetUri = dsUri.
param( u
"url"_s );
1083 if ( !tileSetUri.isEmpty() )
1085 const QUrl url( tileSetUri );
1087 QNetworkRequest request = QNetworkRequest( url );
1089 mHeaders.updateNetworkRequest( request );
1094 switch ( networkRequest.
get( request ) )
1108 mShared->initialize( content.
content(), tileSetUri, transformContext(), mAuthCfg, mHeaders );
1115 const QFileInfo fi( dataSourceUri() );
1118 QFile file( dataSourceUri( ) );
1119 if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) )
1121 const QByteArray raw = file.readAll();
1122 mShared->initialize( raw, QUrl::fromLocalFile( dataSourceUri() ), transformContext(), mAuthCfg, mHeaders );
1135 if ( !mShared->mIndex.isValid() )
1137 appendError( mShared->mError );
1148 return mShared->mLayerCrs;
1156 return mShared->mExtent;
1159bool QgsCesiumTilesDataProvider::isValid()
const
1166QString QgsCesiumTilesDataProvider::name()
const
1170 return PROVIDER_KEY;
1173QString QgsCesiumTilesDataProvider::description()
const
1177 return QObject::tr(
"Cesium 3D Tiles" );
1180QString QgsCesiumTilesDataProvider::htmlMetadata()
const
1187 if ( mShared->mTileset.contains(
"asset" ) )
1189 const auto &asset = mShared->mTileset[
"asset" ];
1190 if ( asset.contains(
"version" ) )
1192 const QString version = QString::fromStdString( asset[
"version"].get<std::string>() );
1193 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"3D Tiles Version" ) % u
"</td><td>%1</a>"_s.arg( version ) % u
"</td></tr>\n"_s;
1196 if ( asset.contains(
"tilesetVersion" ) )
1200 const QString tilesetVersion = QString::fromStdString( asset[
"tilesetVersion"].get<std::string>() );
1201 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Tileset Version" ) % u
"</td><td>%1</a>"_s.arg( tilesetVersion ) % u
"</td></tr>\n"_s;
1203 catch ( json::type_error & )
1205 QgsDebugError( u
"Error when parsing tilesetVersion value"_s );
1209 if ( asset.contains(
"generator" ) )
1211 const QString generator = QString::fromStdString( asset[
"generator"].get<std::string>() );
1212 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Tileset Generator" ) % u
"</td><td>%1</a>"_s.arg( generator ) % u
"</td></tr>\n"_s;
1215 if ( mShared->mTileset.contains(
"extensionsRequired" ) )
1217 QStringList extensions;
1218 for (
const auto &item : mShared->mTileset[
"extensionsRequired"] )
1220 extensions << QString::fromStdString( item.get<std::string>() );
1222 if ( !extensions.isEmpty() )
1224 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;
1227 if ( mShared->mTileset.contains(
"extensionsUsed" ) )
1229 QStringList extensions;
1230 for (
const auto &item : mShared->mTileset[
"extensionsUsed"] )
1232 extensions << QString::fromStdString( item.get<std::string>() );
1234 if ( !extensions.isEmpty() )
1236 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;
1240 if ( !mShared->mZRange.isInfinite() )
1242 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Z Range" ) % u
"</td><td>%1 - %2</a>"_s.arg( QLocale().toString( mShared->mZRange.lower() ), QLocale().toString( mShared->mZRange.upper() ) ) % u
"</td></tr>\n"_s;
1255 return mShared->mLayerMetadata;
1265 return mShared->mSceneCrs ;
1276 return mShared ? mShared->mBoundingVolume : nullVolume;
1286 return mShared->mIndex;
1296 return mShared->mZRange;
1304QgsCesiumTilesProviderMetadata::QgsCesiumTilesProviderMetadata():
1309QIcon QgsCesiumTilesProviderMetadata::icon()
const
1316 return new QgsCesiumTilesDataProvider( uri, options, flags );
1321 const QVariantMap parts = decodeUri( uri );
1322 if ( parts.value( u
"file-name"_s ).toString().compare(
"tileset.json"_L1, Qt::CaseInsensitive ) == 0 )
1337int QgsCesiumTilesProviderMetadata::priorityForUri(
const QString &uri )
const
1339 const QVariantMap parts = decodeUri( uri );
1340 if ( parts.value( u
"file-name"_s ).toString().compare(
"tileset.json"_L1, Qt::CaseInsensitive ) == 0 )
1346QList<Qgis::LayerType> QgsCesiumTilesProviderMetadata::validLayerTypesForUri(
const QString &uri )
const
1348 const QVariantMap parts = decodeUri( uri );
1349 if ( parts.value( u
"file-name"_s ).toString().compare(
"tileset.json"_L1, Qt::CaseInsensitive ) == 0 )
1352 return QList< Qgis::LayerType>();
1355QVariantMap QgsCesiumTilesProviderMetadata::decodeUri(
const QString &uri )
const
1357 QVariantMap uriComponents;
1358 QUrl url = QUrl::fromUserInput( uri );
1359 uriComponents.insert( u
"file-name"_s, url.fileName() );
1360 uriComponents.insert( u
"path"_s, uri );
1361 return uriComponents;
1377 return QObject::tr(
"Cesium 3D Tiles" ) + u
" (tileset.json TILESET.JSON)"_s;
1384 return FileBasedUris;
1387QList<Qgis::LayerType> QgsCesiumTilesProviderMetadata::supportedLayerTypes()
const
1392QString QgsCesiumTilesProviderMetadata::encodeUri(
const QVariantMap &parts )
const
1394 const QString path = parts.value( u
"path"_s ).toString();
1400 return ProviderMetadataCapability::LayerTypesForUri
1401 | ProviderMetadataCapability::PriorityForUri
1402 | 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.