18#include <nlohmann/json.hpp>
40#include "moc_qgsesrii3sdataprovider.cpp"
42#define I3S_PROVIDER_KEY QStringLiteral( "esrii3s" )
43#define I3S_PROVIDER_DESCRIPTION QStringLiteral( "ESRI I3S data provider" )
57 QgsEsriI3STiledSceneIndex(
65 QVector< long long >
childTileIds(
long long id )
const final;
76 bool fetchNodePage(
int nodePage,
QgsFeedback *feedback =
nullptr );
77 void parseNodePage(
const QByteArray &nodePageContent );
79 QVariantMap parseMaterialDefinition(
const json &materialDefinitionJson );
83 long long parentNodeIndex;
84 QVector<long long> childNodeIndexes;
88 QVector<QString> mTextureSetFormats;
89 QVector<QVariantMap> mMaterialDefinitions;
91 mutable QRecursiveMutex mLock;
94 long long mRootNodeIndex;
96 bool mGlobalMode =
false;
97 QMap< long long, NodeDetails > mNodeMap;
98 QSet<int> mCachedNodePages;
111class QgsEsriI3SDataProviderSharedData
114 QgsEsriI3SDataProviderSharedData();
115 void initialize(
const QString &i3sVersion,
116 const json &layerJson,
118 const QgsCoordinateTransformContext &transformContext );
123 QgsCoordinateReferenceSystem mLayerCrs;
124 QgsCoordinateReferenceSystem mSceneCrs;
125 QgsTiledSceneBoundingVolume mBoundingVolume;
127 QgsRectangle mExtent;
128 QgsDoubleRange mZRange;
130 QgsTiledSceneIndex mIndex;
133 QReadWriteLock mReadWriteLock;
141QgsEsriI3STiledSceneIndex::QgsEsriI3STiledSceneIndex(
142 const json &layerJson,
145 : mRootUrl( rootUrl )
146 , mTransformContext( transformContext )
151 if ( layerJson.contains(
"spatialReference" ) && layerJson[
"spatialReference"].is_object() )
153 const json spatialReferenceJson = layerJson[
"spatialReference"];
154 if ( spatialReferenceJson.contains(
"latestWkid" ) && spatialReferenceJson[
"latestWkid"].is_number() )
156 int epsgCode = spatialReferenceJson[
"latestWkid"].get<int>();
157 mGlobalMode = epsgCode == 4326;
159 else if ( spatialReferenceJson.contains(
"wkid" ) && spatialReferenceJson[
"wkid"].is_number() )
161 int epsgCode = spatialReferenceJson[
"wkid"].get<int>();
162 mGlobalMode = epsgCode == 4326;
166 if ( layerJson.contains(
"textureSetDefinitions" ) )
168 for ( auto textureSetDefinitionJson : layerJson[
"textureSetDefinitions"] )
171 for ( const json &formatJson : textureSetDefinitionJson[
"formats"] )
173 if ( formatJson[
"name"].get<std::string>() ==
"0" )
175 formatType = QString::fromStdString( formatJson[
"format"].get<std::string>() );
179 mTextureSetFormats.append( formatType );
183 if ( layerJson.contains(
"materialDefinitions" ) )
185 for ( const json &materialDefinitionJson : layerJson[
"materialDefinitions"] )
187 QVariantMap materialDef = parseMaterialDefinition( materialDefinitionJson );
188 mMaterialDefinitions.append( materialDef );
192 json nodePagesJson = layerJson[
"nodePages"];
193 mNodesPerPage = nodePagesJson[
"nodesPerPage"].get<
int>();
194 mRootNodeIndex = nodePagesJson.contains(
"rootIndex" ) ? nodePagesJson[
"rootIndex"].get<long long>() : 0;
196 int rootNodePage =
static_cast<int>( mRootNodeIndex / mNodesPerPage );
197 fetchNodePage( rootNodePage );
200QVariantMap QgsEsriI3STiledSceneIndex::parseMaterialDefinition(
const json &materialDefinitionJson )
202 QVariantMap materialDef;
204 if ( materialDefinitionJson.contains(
"pbrMetallicRoughness" ) )
206 const json pbrJson = materialDefinitionJson[
"pbrMetallicRoughness"];
207 if ( pbrJson.contains(
"baseColorFactor" ) )
209 const json pbrBaseColorFactorJson = pbrJson[
"baseColorFactor"];
210 materialDef[QStringLiteral(
"pbrBaseColorFactor" )] = QVariantList
212 pbrBaseColorFactorJson[0].get<
double>(),
213 pbrBaseColorFactorJson[1].get<double>(),
214 pbrBaseColorFactorJson[2].get<
double>(),
215 pbrBaseColorFactorJson[3].get<double>()
220 materialDef[QStringLiteral(
"pbrBaseColorFactor" )] = QVariantList{ 1.0, 1.0, 1.0, 1.0 };
222 if ( pbrJson.contains(
"baseColorTexture" ) )
227 const int textureSetDefinitionId = pbrJson[
"baseColorTexture"][
"textureSetDefinitionId"].get<
int>();
228 if ( textureSetDefinitionId < mTextureSetFormats.count() )
230 materialDef[QStringLiteral(
"pbrBaseColorTextureName" )] = QStringLiteral(
"0" );
231 materialDef[QStringLiteral(
"pbrBaseColorTextureFormat" )] = mTextureSetFormats[textureSetDefinitionId];
235 QgsDebugError( QString(
"referencing textureSetDefinition that does not exist! %1 " ).arg( textureSetDefinitionId ) );
241 materialDef[QStringLiteral(
"pbrBaseColorFactor" )] = QVariantList{ 1.0, 1.0, 1.0, 1.0 };
244 if ( materialDefinitionJson.contains(
"doubleSided" ) )
246 materialDef[QStringLiteral(
"doubleSided" )] = materialDefinitionJson[
"doubleSided"].get<
bool>();
258 QMutexLocker locker( &mLock );
259 if ( !mNodeMap.contains( mRootNodeIndex ) )
261 QgsDebugError( QStringLiteral(
"Unable to access the root tile!" ) );
264 return mNodeMap[mRootNodeIndex].tile;
269 QMutexLocker locker( &mLock );
270 auto it = mNodeMap.constFind(
id );
271 if ( it != mNodeMap.constEnd() )
273 return it.value().tile;
279long long QgsEsriI3STiledSceneIndex::parentTileId(
long long id )
const
281 QMutexLocker locker( &mLock );
282 auto it = mNodeMap.constFind(
id );
283 if ( it != mNodeMap.constEnd() )
285 return it.value().parentNodeIndex;
291QVector< long long > QgsEsriI3STiledSceneIndex::childTileIds(
long long id )
const
293 QMutexLocker locker( &mLock );
294 auto it = mNodeMap.constFind(
id );
295 if ( it != mNodeMap.constEnd() )
297 return it.value().childNodeIndexes;
305 QVector< long long > results;
307 std::function< void(
long long )> traverseNode;
308 traverseNode = [&request, &traverseNode, &results,
this](
long long nodeId )
321 fetchHierarchy( t.
id() );
325 auto it = mNodeMap.constFind( t.
id() );
326 for (
long long childId : it.value().childNodeIndexes )
328 traverseNode( childId );
331 if ( it.value().childNodeIndexes.isEmpty() )
345 QMutexLocker locker( &mLock );
347 traverseNode( startNodeId );
354 QMutexLocker locker( &mLock );
355 auto it = mNodeMap.constFind(
id );
356 if ( it == mNodeMap.constEnd() )
362 if ( it.value().childNodeIndexes.isEmpty() )
367 for (
long long childId : it.value().childNodeIndexes )
369 if ( !mNodeMap.contains( childId ) )
378bool QgsEsriI3STiledSceneIndex::fetchHierarchy(
long long id,
QgsFeedback *feedback )
380 QMutexLocker locker( &mLock );
381 auto it = mNodeMap.constFind(
id );
382 if ( it == mNodeMap.constEnd() )
386 QSet<int> nodePagesToFetch;
387 for (
long long childId : it.value().childNodeIndexes )
389 int nodePageIndex =
static_cast<int>( childId / mNodesPerPage );
390 if ( !mCachedNodePages.contains( nodePageIndex ) )
391 nodePagesToFetch.insert( nodePageIndex );
395 for (
int nodePage : nodePagesToFetch )
397 if ( !fetchNodePage( nodePage, feedback ) )
406QByteArray QgsEsriI3STiledSceneIndex::fetchContent(
const QString &uri,
QgsFeedback *feedback )
409 if ( url.isLocalFile() )
411 const QString slpkPath = mRootUrl.toLocalFile();
412 if ( QFileInfo( slpkPath ).suffix().compare( QLatin1String(
"slpk" ), Qt::CaseInsensitive ) == 0 )
414 const QString fileInSlpk = uri.mid( mRootUrl.toString().length() + 1 );
424 QFile file( url.toLocalFile() );
425 if ( file.open( QIODevice::ReadOnly ) )
427 return file.readAll();
433 QNetworkRequest networkRequest = QNetworkRequest( url );
435 networkRequest.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
436 networkRequest.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
439 networkRequest, QString(),
false, feedback );
446bool QgsEsriI3STiledSceneIndex::fetchNodePage(
int nodePage,
QgsFeedback *feedback )
448 QByteArray nodePageContent;
449 if ( !mRootUrl.isLocalFile() )
451 const QString uri = QStringLiteral(
"%1/layers/0/nodepages/%2" ).arg( mRootUrl.toString() ).arg( nodePage );
452 nodePageContent = retrieveContent( uri, feedback );
456 const QString uri = QStringLiteral(
"%1/nodepages/%2.json.gz" ).arg( mRootUrl.toString() ).arg( nodePage );
457 const QByteArray nodePageContentGzipped = retrieveContent( uri, feedback );
461 QgsDebugError( QStringLiteral(
"Failed to decompress node page content: " ) + uri );
465 if ( nodePageContent.isEmpty() )
467 QgsDebugError( QStringLiteral(
"Failed to read node page content: " ) + uri );
474 parseNodePage( nodePageContent );
476 catch ( json::exception &error )
478 QgsDebugError( QStringLiteral(
"Error reading node page %1: %2" ).arg( nodePage ).arg( error.what() ) );
482 mCachedNodePages.insert( nodePage );
490 json center = box[
"center"];
491 json halfSize = box[
"halfSize"];
492 json quaternion = box[
"quaternion"];
496 center[1].get<double>(),
497 center[2].get<double>() ),
499 halfSize[1].get<double>(),
500 halfSize[2].get<double>() ),
501 QQuaternion(
static_cast<float>( quaternion[3].get<double>() ),
502 static_cast<float>( quaternion[0].get<double>() ),
503 static_cast<float>( quaternion[1].get<double>() ),
504 static_cast<float>( quaternion[2].get<double>() ) ) );
506 catch ( nlohmann::json::exception & )
512void QgsEsriI3STiledSceneIndex::parseMesh(
QgsTiledSceneTile &t,
const json &meshJson )
514 if ( !meshJson.contains(
"geometry" ) || !meshJson.contains(
"material" ) )
517 int geometryResource = meshJson[
"geometry"][
"resource"].get<
int>();
519 if ( mRootUrl.isLocalFile() )
520 geometryUri = QStringLiteral(
"%1/nodes/%2/geometries/1.bin.gz" ).arg( mRootUrl.toString() ).arg( geometryResource );
522 geometryUri = QStringLiteral(
"%1/layers/0/nodes/%2/geometries/1" ).arg( mRootUrl.toString() ).arg( geometryResource );
525 const json materialJson = meshJson[
"material"];
526 int materialIndex = materialJson[
"definition"].get<
int>();
527 QVariantMap materialInfo;
528 if ( materialIndex >= 0 && materialIndex < mMaterialDefinitions.count() )
530 materialInfo = mMaterialDefinitions[materialIndex];
531 if ( materialInfo.contains( QStringLiteral(
"pbrBaseColorTextureName" ) ) )
533 const QString textureName = materialInfo[QStringLiteral(
"pbrBaseColorTextureName" )].toString();
534 const QString textureFormat = materialInfo[QStringLiteral(
"pbrBaseColorTextureFormat" )].toString();
535 materialInfo.remove( QStringLiteral(
"pbrBaseColorTextureName" ) );
536 materialInfo.remove( QStringLiteral(
"pbrBaseColorTextureFormat" ) );
538 const int textureResource = materialJson[
"resource"].get<
int>();
540 if ( mRootUrl.isLocalFile() )
541 textureUri = QStringLiteral(
"%1/nodes/%2/textures/%3.%4" ).arg( mRootUrl.toString() ).arg( textureResource ).arg( textureName, textureFormat );
543 textureUri = QStringLiteral(
"%1/layers/0/nodes/%2/textures/%3" ).arg( mRootUrl.toString() ).arg( textureResource ).arg( textureName );
544 materialInfo[QStringLiteral(
"pbrBaseColorTexture" )] = textureUri;
548 t.
setResources( { { QStringLiteral(
"content" ), geometryUri } } );
550 QVariantMap metadata =
552 { QStringLiteral(
"gltfUpAxis" ),
static_cast< int >(
Qgis::Axis::Z ) },
553 { QStringLiteral(
"contentFormat" ), QStringLiteral(
"draco" ) },
554 { QStringLiteral(
"material" ), materialInfo }
559void QgsEsriI3STiledSceneIndex::parseNodePage(
const QByteArray &nodePageContent )
561 const json nodePageJson = json::parse( nodePageContent.toStdString() );
562 for (
const json &nodeJson : nodePageJson[
"nodes"] )
564 long long nodeIndex = nodeJson[
"index"].get<
long long>();
565 long long parentNodeIndex = nodeJson.contains(
"parentIndex" ) ? nodeJson[
"parentIndex"].get<
long long>() : -1;
587 double threshold = -1;
588 if ( nodeJson.contains(
"lodThreshold" ) )
590 double maxScreenThresholdSquared = nodeJson[
"lodThreshold"].get<
double>();
594 threshold = obb.
longestSide() / sqrt( maxScreenThresholdSquared / ( M_PI / 4 ) ) * 16;
596 QVector<long long> childNodeIds;
597 if ( nodeJson.contains(
"children" ) )
599 for (
const json &childJson : nodeJson[
"children"] )
601 childNodeIds << childJson.get<
long long>();
613 if ( nodeJson.contains(
"mesh" ) )
616 parseMesh( t, nodeJson[
"mesh"] );
619 mNodeMap.insert( nodeIndex, { parentNodeIndex, childNodeIds, t } );
628QgsEsriI3SDataProviderSharedData::QgsEsriI3SDataProviderSharedData()
633void QgsEsriI3SDataProviderSharedData::initialize(
634 const QString &i3sVersion,
635 const json &layerJson,
639 mI3sVersion = i3sVersion;
640 mLayerJson = layerJson;
647 if ( layerJson.contains(
"spatialReference" ) && layerJson[
"spatialReference"].is_object() )
649 const json spatialReferenceJson = layerJson[
"spatialReference"];
650 if ( spatialReferenceJson.contains(
"latestWkid" ) && spatialReferenceJson[
"latestWkid"].is_number() )
652 epsgCode = spatialReferenceJson[
"latestWkid"].get<
int>();
654 else if ( spatialReferenceJson.contains(
"wkid" ) && spatialReferenceJson[
"wkid"].is_number() )
656 epsgCode = spatialReferenceJson[
"wkid"].get<
int>();
660 if ( epsgCode == 4326 )
672 mSceneCrs = mLayerCrs;
676 new QgsEsriI3STiledSceneIndex(
683 if ( layerJson.contains(
"fullExtent" ) )
685 const json fullExtentJson = layerJson[
"fullExtent"];
687 fullExtentJson[
"xmin"].get<double>(),
688 fullExtentJson[
"ymin"].get<double>(),
689 fullExtentJson[
"xmax"].get<double>(),
690 fullExtentJson[
"ymax"].get<double>() );
692 fullExtentJson[
"zmin"].get<double>(),
693 fullExtentJson[
"zmax"].get<double>() );
702 mBoundingVolume = mIndex.rootTile().boundingVolume();
710QgsEsriI3SDataProvider::QgsEsriI3SDataProvider(
const QString &uri,
714 , mShared( std::make_shared< QgsEsriI3SDataProviderSharedData >() )
717 QString sourcePath = dataSource.param( QStringLiteral(
"url" ) );
719 if ( sourcePath.isEmpty() )
725 if ( sourcePath.startsWith( QLatin1String(
"http" ) ) || sourcePath.startsWith( QLatin1String(
"file" ) ) )
727 rootUrl = sourcePath;
732 rootUrl = QUrl::fromLocalFile( sourcePath );
737 if ( sourcePath.startsWith( QLatin1String(
"http" ) ) )
739 if ( !loadFromRestService( rootUrl.toString(), layerJson, i3sVersion ) )
744 if ( !loadFromSlpk( rootUrl.toLocalFile(), layerJson, i3sVersion ) )
748 if ( !layerJson.contains(
"layerType" ) )
750 appendError(
QgsErrorMessage( tr(
"Invalid I3S source: missing layer type." ), QStringLiteral(
"I3S" ) ) );
754 if ( !layerJson.contains(
"nodePages" ) )
756 appendError(
QgsErrorMessage( tr(
"Missing 'nodePages' attribute (should be available in I3S >= 1.7)" ), QStringLiteral(
"I3S" ) ) );
760 QString layerType = QString::fromStdString( layerJson[
"layerType"].get<std::string>() );
761 if ( layerType != QLatin1String(
"3DObject" ) && layerType != QLatin1String(
"IntegratedMesh" ) )
763 appendError(
QgsErrorMessage( tr(
"Unsupported layer type: " ) + layerType, QStringLiteral(
"I3S" ) ) );
767 mShared->initialize( i3sVersion, layerJson, rootUrl, transformContext() );
769 if ( !mShared->mIndex.isValid() )
771 appendError( mShared->mError );
779bool QgsEsriI3SDataProvider::loadFromRestService(
const QString &uri, json &layerJson, QString &i3sVersion )
781 QNetworkRequest networkRequest = QNetworkRequest( QUrl( uri ) );
783 networkRequest.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
784 networkRequest.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
787 if ( reply.
error() != QNetworkReply::NoError )
789 appendError(
QgsErrorMessage( tr(
"Failed to fetch layer metadata: " ) + networkRequest.url().toString(), QStringLiteral(
"I3S" ) ) );
792 QByteArray sceneLayerContent = reply.
content();
797 serviceJson = json::parse( sceneLayerContent.toStdString() );
799 catch (
const json::parse_error & )
801 appendError(
QgsErrorMessage( tr(
"Unable to parse JSON: " ) + uri, QStringLiteral(
"I3S" ) ) );
805 if ( !serviceJson.contains(
"serviceVersion" ) )
807 appendError(
QgsErrorMessage( tr(
"Missing I3S version: " ) + uri, QStringLiteral(
"I3S" ) ) );
810 i3sVersion = QString::fromStdString( serviceJson[
"serviceVersion"].get<std::string>() );
811 if ( !checkI3SVersion( i3sVersion ) )
814 if ( !serviceJson.contains(
"layers" ) || !serviceJson[
"layers"].is_array() || serviceJson[
"layers"].size() < 1 )
816 appendError(
QgsErrorMessage( tr(
"Unable to get layer info: " ) + uri, QStringLiteral(
"I3S" ) ) );
820 layerJson = serviceJson[
"layers"][0];
824bool QgsEsriI3SDataProvider::loadFromSlpk(
const QString &uri, json &layerJson, QString &i3sVersion )
827 if ( QFileInfo( uri ).suffix().compare( QLatin1String(
"slpk" ), Qt::CaseInsensitive ) == 0 )
836 QByteArray metadataContent;
837 QString metadataFileName = QStringLiteral(
"metadata.json" );
840 const QString metadataDirPath = QDir( uri ).filePath( metadataFileName );
841 QFile fMetadata( metadataDirPath );
842 if ( !fMetadata.open( QIODevice::ReadOnly ) )
844 appendError(
QgsErrorMessage( tr(
"Failed to read layer metadata: %1" ).arg( metadataDirPath ), QStringLiteral(
"I3S" ) ) );
847 metadataContent = fMetadata.readAll();
853 appendError(
QgsErrorMessage( tr(
"Failed to read %1 in file: %2" ).arg( metadataFileName ).arg( uri ), QStringLiteral(
"I3S" ) ) );
861 metadataJson = json::parse( metadataContent.toStdString() );
863 catch (
const json::parse_error & )
865 appendError(
QgsErrorMessage( tr(
"Unable to parse %1 in: %2" ).arg( metadataFileName ).arg( uri ), QStringLiteral(
"I3S" ) ) );
869 if ( !metadataJson.contains(
"I3SVersion" ) )
871 appendError(
QgsErrorMessage( tr(
"Missing I3S version in %1 in: %2" ).arg( metadataFileName ).arg( uri ), QStringLiteral(
"I3S" ) ) );
874 i3sVersion = QString::fromStdString( metadataJson[
"I3SVersion"].get<std::string>() );
875 if ( !checkI3SVersion( i3sVersion ) )
878 QByteArray sceneLayerContentGzipped;
879 const QString sceneLayerContentFileName = QStringLiteral(
"3dSceneLayer.json.gz" );
882 const QString sceneLayerContentDirPath = QDir( uri ).filePath( sceneLayerContentFileName );
883 QFile fSceneLayerContent( sceneLayerContentDirPath );
884 if ( !fSceneLayerContent.open( QIODevice::ReadOnly ) )
886 appendError(
QgsErrorMessage( tr(
"Failed to read layer metadata: %1" ).arg( sceneLayerContentDirPath ), QStringLiteral(
"I3S" ) ) );
889 sceneLayerContentGzipped = fSceneLayerContent.readAll();
895 appendError(
QgsErrorMessage( tr(
"Failed to read %1 in file: %2" ).arg( sceneLayerContentFileName ).arg( uri ), QStringLiteral(
"I3S" ) ) );
900 QByteArray sceneLayerContent;
903 appendError(
QgsErrorMessage( tr(
"Failed to decompress %1 in: %2" ).arg( sceneLayerContentFileName ).arg( uri ), QStringLiteral(
"I3S" ) ) );
909 layerJson = json::parse( sceneLayerContent.toStdString() );
911 catch (
const json::parse_error & )
913 appendError(
QgsErrorMessage( tr(
"Unable to parse %1 in: %2" ).arg( sceneLayerContentFileName ).arg( uri ), QStringLiteral(
"I3S" ) ) );
920bool QgsEsriI3SDataProvider::checkI3SVersion(
const QString &i3sVersion )
931 QStringList i3sVersionComponents = i3sVersion.split(
'.' );
932 if ( i3sVersionComponents.size() != 2 )
934 appendError(
QgsErrorMessage( tr(
"Unexpected I3S version format: " ) + i3sVersion, QStringLiteral(
"I3S" ) ) );
937 int i3sVersionMajor = i3sVersionComponents[0].toInt();
938 int i3sVersionMinor = i3sVersionComponents[1].toInt();
939 if ( i3sVersionMajor != 1 || ( i3sVersionMajor == 1 && i3sVersionMinor < 7 ) )
941 appendError(
QgsErrorMessage( tr(
"Unsupported I3S version: " ) + i3sVersion, QStringLiteral(
"I3S" ) ) );
948QgsEsriI3SDataProvider::QgsEsriI3SDataProvider(
const QgsEsriI3SDataProvider &other )
950 , mIsValid( other.mIsValid )
953 mShared = other.mShared;
956QgsEsriI3SDataProvider::~QgsEsriI3SDataProvider() =
default;
968QgsEsriI3SDataProvider *QgsEsriI3SDataProvider::clone()
const
971 return new QgsEsriI3SDataProvider( *
this );
979 return mShared->mLayerCrs;
987 return mShared->mExtent;
990bool QgsEsriI3SDataProvider::isValid()
const
997QString QgsEsriI3SDataProvider::name()
const
1004QString QgsEsriI3SDataProvider::description()
const
1008 return QObject::tr(
"ESRI I3S" );
1011QString QgsEsriI3SDataProvider::htmlMetadata()
const
1019 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"I3S Version" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( mShared->mI3sVersion ) % QStringLiteral(
"</td></tr>\n" );
1021 QString layerType = QString::fromStdString( mShared->mLayerJson[
"layerType"].get<std::string>() );
1022 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Layer Type" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( layerType ) % QStringLiteral(
"</td></tr>\n" );
1024 if ( mShared->mLayerJson.contains(
"version" ) )
1028 QString version = QString::fromStdString( mShared->mLayerJson[
"version"].get<std::string>() );
1029 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Version" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( version ) % QStringLiteral(
"</td></tr>\n" );
1033 if ( mShared->mLayerJson.contains(
"name" ) )
1035 QString name = QString::fromStdString( mShared->mLayerJson[
"name"].get<std::string>() );
1036 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Name" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( name ) % QStringLiteral(
"</td></tr>\n" );
1039 if ( mShared->mLayerJson.contains(
"alias" ) )
1041 QString alias = QString::fromStdString( mShared->mLayerJson[
"alias"].get<std::string>() );
1042 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Alias" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( alias ) % QStringLiteral(
"</td></tr>\n" );
1045 if ( mShared->mLayerJson.contains(
"description" ) )
1047 QString description = QString::fromStdString( mShared->mLayerJson[
"description"].get<std::string>() );
1048 metadata += QStringLiteral(
"<tr><td class=\"highlight\">" ) % tr(
"Description" ) % QStringLiteral(
"</td><td>%1</a>" ).arg( description ) % QStringLiteral(
"</td></tr>\n" );
1051 if ( !mShared->mZRange.isInfinite() )
1053 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" );
1066 return mShared->mSceneCrs;
1077 return mShared ? mShared->mBoundingVolume : nullVolume;
1087 return mShared->mIndex;
1097 return mShared->mZRange;
1107QgsEsriI3SProviderMetadata::QgsEsriI3SProviderMetadata():
1112QIcon QgsEsriI3SProviderMetadata::icon()
const
1119 return new QgsEsriI3SDataProvider( uri, options, flags );
1125 const QFileInfo fi( uri );
1132 const QVariantMap parts = decodeUri( uri );
1133 fileName = parts.value( QStringLiteral(
"path" ) ).toString();
1136 if ( fileName.isEmpty() )
1139 if ( QFileInfo( fileName ).suffix().compare( QLatin1String(
"slpk" ), Qt::CaseInsensitive ) == 0 )
1142 parts.insert( QStringLiteral(
"path" ), fileName );
1145 details.
setUri( encodeUri( parts ) );
1154QVariantMap QgsEsriI3SProviderMetadata::decodeUri(
const QString &uri )
const
1158 QVariantMap uriComponents;
1159 QString path = dsUri.param( QStringLiteral(
"url" ) );
1160 if ( path.isEmpty() && !uri.isEmpty() )
1164 uriComponents.insert( QStringLiteral(
"path" ), path );
1166 return uriComponents;
1169QList< Qgis::LayerType > QgsEsriI3SProviderMetadata::validLayerTypesForUri(
const QString &uri )
const
1171 const QVariantMap parts = decodeUri( uri );
1172 QString filePath = parts.value( QStringLiteral(
"path" ) ).toString();
1174 if ( filePath.endsWith( QStringLiteral(
".slpk" ), Qt::CaseSensitivity::CaseInsensitive ) )
1180QString QgsEsriI3SProviderMetadata::encodeUri(
const QVariantMap &parts )
const
1183 const QString partsKey = parts.contains( QStringLiteral(
"path" ) ) ? QStringLiteral(
"path" ) : QStringLiteral(
"url" );
1184 dsUri.
setParam( QStringLiteral(
"url" ), parts.value( partsKey ).toString() );
1201 return QObject::tr(
"ESRI Scene layer package" ) + QStringLiteral(
" (*.slpk *.SLPK)" );
1208 return FileBasedUris;
1211QList<Qgis::LayerType> QgsEsriI3SProviderMetadata::supportedLayerTypes()
const
1218 return ProviderMetadataCapability::PriorityForUri;
1221int QgsEsriI3SProviderMetadata::priorityForUri(
const QString &uri )
const
1223 const QVariantMap parts = decodeUri( uri );
1224 QString filePath = parts.value( QStringLiteral(
"path" ) ).toString();
1226 if ( filePath.endsWith( QStringLiteral(
".slpk" ), Qt::CaseSensitivity::CaseInsensitive ) )
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.
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
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,...
An abstract base class for tiled scene data provider indices.
virtual QVector< long long > childTileIds(long long id) const =0
Returns a list of the tile IDs of any children for the tile with matching id.
virtual long long parentTileId(long long id) const =0
Returns the tile ID of the parent tile of the tile with matching id, or -1 if the tile has no parent.
virtual QgsTiledSceneTile rootTile() const =0
Returns the root tile for the index.
virtual QByteArray fetchContent(const QString &uri, QgsFeedback *feedback=nullptr)=0
Fetches index content for the specified uri.
virtual bool fetchHierarchy(long long id, QgsFeedback *feedback=nullptr)=0
Populates the tile with the given id by fetching any sub datasets attached to the tile.
virtual QgsTiledSceneTile getTile(long long id)=0
Returns the tile with matching id, or an invalid tile if the matching tile is not available.
virtual Qgis::TileChildrenAvailability childAvailability(long long id) const =0
Returns the availability for a tile's children.
virtual QVector< long long > getTiles(const QgsTiledSceneRequest &request)=0
Returns the tile IDs which match the given request.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
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.
double zMinimum() const
Returns the minimum z value.
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Stores the component parts of a data source URI (e.g.
QByteArray encodedUri() const
Returns the complete encoded URI as a byte array.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on 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.
A simple 4x4 matrix implementation useful for transformation in 3D space.
void translate(const QgsVector3D &vector)
Multiplies this matrix by another that translates coordinates by the components of a vector.
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.
QNetworkReply::NetworkError error() const
Returns the reply's error message, or QNetworkReply::NoError if no error was encountered.
Represents a oriented (rotated) box in 3 dimensions.
double longestSide() const
Returns size of the longest side of the box.
bool isNull() const
Returns true if the box is a null box.
QList< double > halfAxesList() const
Returns the half axes matrix;.
QgsVector3D center() const
Returns the vector to the center of the 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.
Represents a bounding volume for a tiled scene.
bool intersects(const QgsOrientedBox3D &box) const
Returns true if this bounds intersects the specified box.
Base class for data providers for QgsTiledSceneLayer.
An index for tiled scene data providers.
Tiled scene data request.
QgsOrientedBox3D filterBox() const
Returns the box from which data will be taken.
long long parentTileId() const
Returns the parent tile ID, if filtering is limited to children of a specific tile.
double requiredGeometricError() const
Returns the required geometric error threshold for the returned tiles, in meters.
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.
void setMetadata(const QVariantMap &metadata)
Sets additional metadata attached to the tile.
void setGeometricError(double error)
Sets the tile's geometric error, which is the error, in meters, of the tile's simplified representati...
const QgsTiledSceneBoundingVolume & boundingVolume() const
Returns the bounding volume for the tile.
void setBoundingVolume(const QgsTiledSceneBoundingVolume &volume)
Sets the bounding volume for the tile.
long long id() const
Returns the tile's unique ID.
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.
static bool extractFileFromZip(const QString &zipFilename, const QString &filenameInZip, QByteArray &bytesOut)
Extracts a file from a zip archive, returns true on success.
static bool decodeGzip(const QByteArray &bytesIn, QByteArray &bytesOut)
Decodes gzip byte stream, returns true on success.
#define I3S_PROVIDER_DESCRIPTION
#define QgsDebugError(str)
#define QgsSetRequestInitiatorClass(request, _class)
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.