41#include <QNetworkRequest> 
   42#include <QJsonDocument> 
   45#include <QRegularExpression> 
   46#include <QRecursiveMutex> 
   48#include <QApplication> 
   49#include <nlohmann/json.hpp> 
   53#define PROVIDER_KEY QStringLiteral( "cesiumtiles" ) 
   54#define PROVIDER_DESCRIPTION QStringLiteral( "Cesium 3D Tiles data provider" ) 
   65static QString appendQueryFromBaseUrl( 
const QString &contentUri, 
const QUrl &baseUrl )
 
   67  QUrlQuery contentQuery( QUrl( contentUri ).query() );
 
   68  const QList<QPair<QString, QString>> baseUrlQueryItems = QUrlQuery( baseUrl.query() ).queryItems();
 
   69  for ( 
const QPair<QString, QString> &kv : baseUrlQueryItems )
 
   71    contentQuery.addQueryItem( kv.first, kv.second );
 
   73  QUrl newContentUrl( contentUri );
 
   74  newContentUrl.setQuery( contentQuery );
 
   75  return newContentUrl.toString();
 
   83    QgsCesiumTiledSceneIndex(
 
   86      const QString &authCfg,
 
   90    std::unique_ptr< QgsTiledSceneTile > tileFromJson( 
const json &node, 
const QUrl &baseUrl, 
const QgsTiledSceneTile *parent, 
Qgis::Axis gltfUpAxis );
 
   92    void refineNodeFromJson( 
QgsTiledSceneNode *node, 
const QUrl &baseUrl, 
const json &json );
 
   96    long long parentTileId( 
long long id ) const final;
 
   97    QVector< 
long long > childTileIds( 
long long id ) const final;
 
   99    Qgis::TileChildrenAvailability childAvailability( 
long long id ) const final;
 
  100    bool fetchHierarchy( 
long long id, 
QgsFeedback *feedback = 
nullptr ) final;
 
  104    QByteArray fetchContent( const QString &uri, 
QgsFeedback *feedback = 
nullptr ) final;
 
  108    enum class TileContentFormat
 
  114    mutable QRecursiveMutex mLock;
 
  116    std::unique_ptr< QgsTiledSceneNode > mRootNode;
 
  117    QMap< long long, QgsTiledSceneNode * > mNodeMap;
 
  118    QMap< long long, TileContentFormat > mTileContentFormats;
 
  121    long long mNextTileId = 0;
 
  125class QgsCesiumTilesDataProviderSharedData
 
  128    QgsCesiumTilesDataProviderSharedData();
 
  129    void initialize( 
const QString &tileset,
 
  132                     const QString &authCfg,
 
  140    nlohmann::json mTileset;
 
  147    QReadWriteLock mReadWriteLock;
 
  158  const std::string gltfUpAxisString = json.get<std::string>();
 
  159  if ( gltfUpAxisString == 
"z" || gltfUpAxisString == 
"Z" )
 
  163  else if ( gltfUpAxisString == 
"y" || gltfUpAxisString == 
"Y" )
 
  167  else if ( gltfUpAxisString == 
"x" || gltfUpAxisString == 
"X" )
 
  171  QgsDebugError( QStringLiteral( 
"Unsupported gltfUpAxis value: %1" ).arg( QString::fromStdString( gltfUpAxisString ) ) );
 
  176  : mTransformContext( transformContext )
 
  177  , mAuthCfg( authCfg )
 
  178  , mHeaders( headers )
 
  181  if ( tileset.contains( 
"asset" ) )
 
  183    const auto &assetJson = tileset[
"asset"];
 
  184    if ( assetJson.contains( 
"gltfUpAxis" ) )
 
  186      gltfUpAxis = axisFromJson( assetJson[
"gltfUpAxis"] );
 
  190  mRootNode.reset( nodeFromJson( tileset[ 
"root" ], rootUrl, 
nullptr, gltfUpAxis ) );
 
  193std::unique_ptr< QgsTiledSceneTile > QgsCesiumTiledSceneIndex::tileFromJson( 
const json &json, 
const QUrl &baseUrl, 
const QgsTiledSceneTile *parent, 
Qgis::Axis gltfUpAxis )
 
  195  std::unique_ptr< QgsTiledSceneTile > tile = std::make_unique< QgsTiledSceneTile >( mNextTileId++ );
 
  197  tile->setBaseUrl( baseUrl );
 
  198  tile->setMetadata( {{ QStringLiteral( 
"gltfUpAxis" ), 
static_cast< int >( gltfUpAxis ) }} );
 
  201  if ( json.contains( 
"transform" ) && !json[
"transform"].is_null() )
 
  203    const auto &transformJson = json[
"transform"];
 
  204    double *ptr = transform.
data();
 
  205    for ( 
int i = 0; i < 16; ++i )
 
  206      ptr[i] = transformJson[i].get<double>();
 
  210      transform = *parent->
transform() * transform;
 
  213  else if ( parent && parent->
transform() )
 
  218    tile->setTransform( transform );
 
  220  const auto &boundingVolume = json[ 
"boundingVolume" ];
 
  222  if ( boundingVolume.contains( 
"region" ) )
 
  225    if ( !rootRegion.
isNull() )
 
  227      if ( rootRegion.
width() > 20 || rootRegion.
height() > 20 )
 
  234        QVector< QgsVector3D > corners = rootRegion.
corners();
 
  242        for ( 
int i = 0; i < 8; ++i )
 
  245          x.append( corner.
x() );
 
  246          y.append( corner.
y() );
 
  247          z.append( corner.
z() );
 
  250        ct.setBallparkTransformsAreAppropriate( 
true );
 
  253          ct.transformInPlace( x, y, z );
 
  257          QgsDebugError( QStringLiteral( 
"Cannot transform region bounding volume" ) );
 
  260        const auto minMaxX = std::minmax_element( x.constBegin(), x.constEnd() );
 
  261        const auto minMaxY = std::minmax_element( y.constBegin(), y.constEnd() );
 
  262        const auto minMaxZ = std::minmax_element( z.constBegin(), z.constEnd() );
 
  269  else if ( boundingVolume.contains( 
"box" ) )
 
  279  else if ( boundingVolume.contains( 
"sphere" ) )
 
  290    QgsDebugError( QStringLiteral( 
"unsupported boundingVolume format" ) );
 
  293  tile->setBoundingVolume( volume );
 
  295  if ( json.contains( 
"geometricError" ) )
 
  296    tile->setGeometricError( json[
"geometricError"].get< double >() );
 
  297  if ( json.contains( 
"refine" ) )
 
  299    if ( json[
"refine"] == 
"ADD" )
 
  301    else if ( json[
"refine"] == 
"REPLACE" )
 
  310  if ( json.contains( 
"content" ) && !json[
"content"].is_null() )
 
  312    const auto &contentJson = json[
"content"];
 
  316    if ( contentJson.contains( 
"uri" ) && !contentJson[
"uri"].is_null() )
 
  318      QString relativeUri = QString::fromStdString( contentJson[
"uri"].get<std::string>() );
 
  319      contentUri = baseUrl.resolved( QUrl( relativeUri ) ).toString();
 
  321      if ( baseUrl.hasQuery() && QUrl( relativeUri ).isRelative() )
 
  322        contentUri = appendQueryFromBaseUrl( contentUri, baseUrl );
 
  324    else if ( contentJson.contains( 
"url" ) && !contentJson[
"url"].is_null() )
 
  326      QString relativeUri = QString::fromStdString( contentJson[
"url"].get<std::string>() );
 
  327      contentUri = baseUrl.resolved( QUrl( relativeUri ) ).toString();
 
  329      if ( baseUrl.hasQuery() && QUrl( relativeUri ).isRelative() )
 
  330        contentUri = appendQueryFromBaseUrl( contentUri, baseUrl );
 
  332    if ( !contentUri.isEmpty() )
 
  334      tile->setResources( {{ QStringLiteral( 
"content" ), contentUri } } );
 
  343  std::unique_ptr< QgsTiledSceneTile > tile = tileFromJson( json, baseUrl, parent ? parent->
tile() : nullptr, gltfUpAxis );
 
  344  std::unique_ptr< QgsTiledSceneNode > newNode = std::make_unique< QgsTiledSceneNode >( tile.release() );
 
  345  mNodeMap.insert( newNode->tile()->id(), newNode.get() );
 
  350  if ( json.contains( 
"children" ) )
 
  352    for ( 
const auto &childJson : json[
"children"] )
 
  354      nodeFromJson( childJson, baseUrl, newNode.get(), gltfUpAxis );
 
  358  return newNode.release();
 
  361void QgsCesiumTiledSceneIndex::refineNodeFromJson( 
QgsTiledSceneNode *node, 
const QUrl &baseUrl, 
const json &json )
 
  363  const auto &rootTileJson = json[
"root"];
 
  366  if ( json.contains( 
"asset" ) )
 
  368    const auto &assetJson = json[
"asset"];
 
  369    if ( assetJson.contains( 
"gltfUpAxis" ) )
 
  371      gltfUpAxis = axisFromJson( assetJson[
"gltfUpAxis"] );
 
  375  std::unique_ptr< QgsTiledSceneTile > newTile = tileFromJson( rootTileJson, baseUrl, node->
tile(), gltfUpAxis );
 
  385  if ( newTile->transform() )
 
  388  if ( rootTileJson.contains( 
"children" ) )
 
  390    for ( 
const auto &childJson : rootTileJson[
"children"] )
 
  392      nodeFromJson( childJson, baseUrl, node, gltfUpAxis );
 
  399  QMutexLocker locker( &mLock );
 
  405  QMutexLocker locker( &mLock );
 
  406  auto it = mNodeMap.constFind( 
id );
 
  407  if ( it != mNodeMap.constEnd() )
 
  409    return *( it.value()->tile() );
 
  415long long QgsCesiumTiledSceneIndex::parentTileId( 
long long id )
 const 
  417  QMutexLocker locker( &mLock );
 
  418  auto it = mNodeMap.constFind( 
id );
 
  419  if ( it != mNodeMap.constEnd() )
 
  423      return parent->
tile()->
id();
 
  430QVector< long long > QgsCesiumTiledSceneIndex::childTileIds( 
long long id )
 const 
  432  QMutexLocker locker( &mLock );
 
  433  auto it = mNodeMap.constFind( 
id );
 
  434  if ( it != mNodeMap.constEnd() )
 
  436    QVector< long long > childIds;
 
  437    const QList< QgsTiledSceneNode * > children = it.value()->children();
 
  438    childIds.reserve( children.size() );
 
  441      childIds << child->tile()->id();
 
  451  QVector< long long > results;
 
  454  traverseNode = [&request, &traverseNode, &results, 
this]( 
QgsTiledSceneNode * node )
 
  470      QList< QgsTiledSceneNode * > children = node->
children();
 
  471      if ( children.empty() )
 
  473        switch ( childAvailability( tile->
id() ) )
 
  483              if ( fetchHierarchy( tile->
id() ), request.
feedback() )
 
  498        traverseNode( child );
 
  505          results << tile->
id();
 
  510          if ( children.empty() )
 
  511            results << tile->
id();
 
  517      results << tile->
id();
 
  522  QMutexLocker locker( &mLock );
 
  526      traverseNode( mRootNode.get() );
 
  531    if ( it != mNodeMap.constEnd() )
 
  533      traverseNode( it.value() );
 
  543  QMutexLocker locker( &mLock );
 
  545    auto it = mNodeMap.constFind( 
id );
 
  546    if ( it == mNodeMap.constEnd() )
 
  549    if ( !it.value()->children().isEmpty() )
 
  552    contentUri = it.value()->tile()->resources().value( QStringLiteral( 
"content" ) ).toString();
 
  556    auto it = mTileContentFormats.constFind( 
id );
 
  557    if ( it != mTileContentFormats.constEnd() )
 
  559      switch ( it.value() )
 
  561        case TileContentFormat::NotJson:
 
  563        case TileContentFormat::Json:
 
  570  if ( contentUri.isEmpty() )
 
  580  const thread_local QRegularExpression isJsonRx( QStringLiteral( 
".*\\.json(?:\\?.*)?$" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
 
  581  if ( isJsonRx.match( contentUri ).hasMatch() )
 
  585  const thread_local QRegularExpression antiCandidateRx( QStringLiteral( 
".*\\.(gltf|glb|b3dm|i3dm|pnts|cmpt|bin|glbin|glbuf|png|jpeg|jpg)(?:\\?.*)?$" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
 
  586  if ( antiCandidateRx.match( contentUri ).hasMatch() )
 
  594bool QgsCesiumTiledSceneIndex::fetchHierarchy( 
long long id, 
QgsFeedback *feedback )
 
  596  QMutexLocker locker( &mLock );
 
  597  auto it = mNodeMap.constFind( 
id );
 
  598  if ( it == mNodeMap.constEnd() )
 
  604    auto it = mTileContentFormats.constFind( 
id );
 
  605    if ( it != mTileContentFormats.constEnd() )
 
  607      switch ( it.value() )
 
  609        case TileContentFormat::NotJson:
 
  611        case TileContentFormat::Json:
 
  617  const QString contentUri = it.value()->tile()->resources().value( QStringLiteral( 
"content" ) ).toString();
 
  620  if ( contentUri.isEmpty() )
 
  624  const QByteArray subTile = retrieveContent( contentUri, feedback );
 
  625  if ( !subTile.isEmpty() )
 
  632      const auto subTileJson = json::parse( subTile.toStdString() );
 
  633      QMutexLocker locker( &mLock );
 
  634      refineNodeFromJson( it.value(), QUrl( contentUri ), subTileJson );
 
  635      mTileContentFormats.insert( 
id, TileContentFormat::Json );
 
  638    catch ( json::parse_error & )
 
  640      QMutexLocker locker( &mLock );
 
  641      mTileContentFormats.insert( 
id, TileContentFormat::NotJson );
 
  649    mTileContentFormats.insert( 
id, TileContentFormat::NotJson );
 
  654QByteArray QgsCesiumTiledSceneIndex::fetchContent( 
const QString &uri, 
QgsFeedback *feedback )
 
  658  if ( uri.startsWith( 
"http" ) )
 
  660    QNetworkRequest networkRequest = QNetworkRequest( url );
 
  662    networkRequest.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
 
  663    networkRequest.setAttribute( QNetworkRequest::CacheSaveControlAttribute, 
true );
 
  665    mHeaders.updateNetworkRequest( networkRequest );
 
  667    if ( QThread::currentThread() == QApplication::instance()->thread() )
 
  671                                             networkRequest, mAuthCfg, 
false, feedback );
 
  691      return reply->data();
 
  694  else if ( url.isLocalFile() && QFile::exists( url.toLocalFile() ) )
 
  696    QFile file( url.toLocalFile() );
 
  697    if ( file.open( QIODevice::ReadOnly ) )
 
  699      return file.readAll();
 
  710QgsCesiumTilesDataProviderSharedData::QgsCesiumTilesDataProviderSharedData()
 
  718  mTileset = json::parse( tileset.toStdString() );
 
  719  if ( !mTileset.contains( 
"root" ) )
 
  721    mError = QObject::tr( 
"JSON is not a valid Cesium 3D Tiles source (does not contain \"root\" value)" );
 
  725  mLayerMetadata.setType( QStringLiteral( 
"dataset" ) );
 
  727  if ( mTileset.contains( 
"asset" ) )
 
  729    const auto &asset = mTileset[ 
"asset" ];
 
  730    if ( asset.contains( 
"tilesetVersion" ) )
 
  734        const QString tilesetVersion = QString::fromStdString( asset[
"tilesetVersion"].get<std::string>() );
 
  735        mLayerMetadata.setIdentifier( tilesetVersion );
 
  737      catch ( json::type_error & )
 
  739        QgsDebugError( QStringLiteral( 
"Error when parsing tilesetVersion value" ) );
 
  745             new QgsCesiumTiledSceneIndex(
 
  756    const auto &root = mTileset[ 
"root" ];
 
  768      const auto &rootBoundingVolume = root[ 
"boundingVolume" ];
 
  771      if ( root.contains( 
"transform" ) && !root[
"transform"].is_null() )
 
  773        const auto &transformJson = root[
"transform"];
 
  774        double *ptr = rootTransform.
data();
 
  775        for ( 
int i = 0; i < 16; ++i )
 
  776          ptr[i] = transformJson[i].get<double>();
 
  779      if ( rootBoundingVolume.contains( 
"region" ) )
 
  782        if ( !rootRegion.
isNull() )
 
  787          if ( !mIndex.rootTile().boundingVolume().box().isNull() )
 
  794          mLayerMetadata.setCrs( mSceneCrs );
 
  797          spatialExtent.
bounds = rootRegion;
 
  800      else if ( rootBoundingVolume.contains( 
"box" ) )
 
  811          mLayerMetadata.setCrs( mSceneCrs );
 
  814          mBoundingVolume.transform( rootTransform );
 
  818            ct.setBallparkTransformsAreAppropriate( 
true );
 
  819            const QgsBox3D rootRegion = mBoundingVolume.bounds( ct );
 
  821            if ( !mIndex.rootTile().boundingVolume().box().isNull() )
 
  826            std::unique_ptr< QgsAbstractGeometry > extent2D( mBoundingVolume.as2DGeometry( ct ) );
 
  827            mExtent = extent2D->boundingBox();
 
  831            QgsDebugError( QStringLiteral( 
"Caught transform exception when transforming boundingVolume" ) );
 
  835          spatialExtent.
bounds = mBoundingVolume.bounds();
 
  838      else if ( rootBoundingVolume.contains( 
"sphere" ) )
 
  849          mLayerMetadata.setCrs( mSceneCrs );
 
  857            ct.setBallparkTransformsAreAppropriate( 
true );
 
  858            const QgsBox3D rootRegion = mBoundingVolume.bounds( ct );
 
  860            if ( !mIndex.rootTile().boundingVolume().box().isNull() )
 
  865            std::unique_ptr< QgsAbstractGeometry > extent2D( mBoundingVolume.as2DGeometry( ct ) );
 
  866            mExtent = extent2D->boundingBox();
 
  870            QgsDebugError( QStringLiteral( 
"Caught transform exception when transforming boundingVolume" ) );
 
  874          spatialExtent.
bounds = mBoundingVolume.bounds();
 
  879        mError = QObject::tr( 
"JSON is not a valid Cesium 3D Tiles source (unsupported boundingVolume format)" );
 
  885      mLayerMetadata.setExtent( layerExtent );
 
  895QgsCesiumTilesDataProvider::QgsCesiumTilesDataProvider( 
const QString &uri, 
const ProviderOptions &providerOptions, ReadFlags flags )
 
  897  , mShared( std::make_shared< QgsCesiumTilesDataProviderSharedData >() )
 
  907QgsCesiumTilesDataProvider::QgsCesiumTilesDataProvider( 
const QgsCesiumTilesDataProvider &other )
 
  909  , mIsValid( other.mIsValid )
 
  910  , mAuthCfg( other.mAuthCfg )
 
  911  , mHeaders( other.mHeaders )
 
  914  mShared = other.mShared;
 
  917Qgis::TiledSceneProviderCapabilities QgsCesiumTilesDataProvider::capabilities()
 const 
  922QgsCesiumTilesDataProvider::~QgsCesiumTilesDataProvider() = 
default;
 
  924QgsCesiumTilesDataProvider *QgsCesiumTilesDataProvider::clone()
 const 
  927  return new QgsCesiumTilesDataProvider( *
this );
 
  930bool QgsCesiumTilesDataProvider::init()
 
  935  const QString uri = dataSourceUri();
 
  937  if ( uri.startsWith( QLatin1String( 
"ion://" ) ) )
 
  940    const QString assetId = QUrlQuery( url ).queryItemValue( QStringLiteral( 
"assetId" ) );
 
  941    const QString accessToken = QUrlQuery( url ).queryItemValue( QStringLiteral( 
"accessToken" ) );
 
  943    const QString CESIUM_ION_URL = QStringLiteral( 
"https://api.cesium.com/" );
 
  947      const QString assetInfoEndpoint = CESIUM_ION_URL + QStringLiteral( 
"v1/assets/%1" ).arg( assetId );
 
  948      QNetworkRequest request = QNetworkRequest( assetInfoEndpoint );
 
  950      mHeaders.updateNetworkRequest( request );
 
  951      if ( !accessToken.isEmpty() )
 
  952        request.setRawHeader( "Authorization", QStringLiteral( "Bearer %1" ).arg( accessToken ).toLocal8Bit() );
 
  955      if ( accessToken.isEmpty() )
 
  956        networkRequest.setAuthCfg( mAuthCfg );
 
  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      mShared->mLayerMetadata.setTitle( QString::fromStdString( assetInfoJson[
"name"].get<std::string>() ) );
 
  979      mShared->mLayerMetadata.setAbstract( QString::fromStdString( assetInfoJson[
"description"].get<std::string>() ) );
 
  980      const QString attribution = QString::fromStdString( assetInfoJson[
"attribution"].get<std::string>() );
 
  981      if ( !attribution.isEmpty() )
 
  982        mShared->mLayerMetadata.setRights( { attribution } );
 
  984      mShared->mLayerMetadata.setDateTime( 
Qgis::MetadataDateType::Created, QDateTime::fromString( QString::fromStdString( assetInfoJson[
"dateAdded"].get<std::string>() ), Qt::DateFormat::ISODate ) );
 
  989      const QString tileAccessEndpoint = CESIUM_ION_URL + QStringLiteral( 
"v1/assets/%1/endpoint" ).arg( assetId );
 
  990      QNetworkRequest request = QNetworkRequest( tileAccessEndpoint );
 
  992      mHeaders.updateNetworkRequest( request );
 
  993      if ( !accessToken.isEmpty() )
 
  994        request.setRawHeader( "Authorization", QStringLiteral( "Bearer %1" ).arg( accessToken ).toLocal8Bit() );
 
  997      if ( accessToken.isEmpty() )
 
  998        networkRequest.setAuthCfg( mAuthCfg );
 
 1000      switch ( networkRequest.get( request ) )
 
 1013      const json tileAccessJson = json::parse( content.
content().toStdString() );
 
 1015      if ( tileAccessJson.contains( 
"url" ) )
 
 1017        tileSetUri = QString::fromStdString( tileAccessJson[
"url"].get<std::string>() );
 
 1019      else if ( tileAccessJson.contains( 
"options" ) )
 
 1021        const auto &optionsJson = tileAccessJson[
"options"];
 
 1022        if ( optionsJson.contains( 
"url" ) )
 
 1024          tileSetUri = QString::fromStdString( optionsJson[
"url"].get<std::string>() );
 
 1028      if ( tileAccessJson.contains( 
"accessToken" ) )
 
 1032        mHeaders.insert( QStringLiteral( 
"Authorization" ),
 
 1033                         QStringLiteral( 
"Bearer %1" ).arg( QString::fromStdString( tileAccessJson[
"accessToken"].get<std::string>() ) ) );
 
 1042    tileSetUri = dsUri.
param( QStringLiteral( 
"url" ) );
 
 1045  if ( !tileSetUri.isEmpty() )
 
 1047    const QUrl url( tileSetUri );
 
 1049    QNetworkRequest request = QNetworkRequest( url );
 
 1051    mHeaders.updateNetworkRequest( request );
 
 1054    networkRequest.setAuthCfg( mAuthCfg );
 
 1056    switch ( networkRequest.get( request ) )
 
 1070    mShared->initialize( content.
content(), tileSetUri, transformContext(), mAuthCfg, mHeaders );
 
 1077    const QFileInfo fi( dataSourceUri() );
 
 1080      QFile file( dataSourceUri( ) );
 
 1081      if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) )
 
 1083        const QByteArray raw = file.readAll();
 
 1084        mShared->initialize( raw, QUrl::fromLocalFile( dataSourceUri() ), transformContext(), mAuthCfg, mHeaders );
 
 1097  if ( !mShared->mIndex.isValid() )
 
 1099    appendError( mShared->mError );
 
 1110  return mShared->mLayerCrs;
 
 1118  return mShared->mExtent;
 
 1121bool QgsCesiumTilesDataProvider::isValid()
 const 
 1128QString QgsCesiumTilesDataProvider::name()
 const 
 1132  return PROVIDER_KEY;
 
 1135QString QgsCesiumTilesDataProvider::description()
 const 
 1139  return QObject::tr( 
"Cesium 3D Tiles" );
 
 1142QString QgsCesiumTilesDataProvider::htmlMetadata()
 const 
 1149  if ( mShared->mTileset.contains( 
"asset" ) )
 
 1151    const auto &asset = mShared->mTileset[ 
"asset" ];
 
 1152    if ( asset.contains( 
"version" ) )
 
 1154      const QString version = QString::fromStdString( asset[
"version"].get<std::string>() );
 
 1155      metadata += QStringLiteral( 
"<tr><td class=\"highlight\">" ) % tr( 
"3D Tiles Version" ) % QStringLiteral( 
"</td><td>%1</a>" ).arg( version ) % QStringLiteral( 
"</td></tr>\n" );
 
 1158    if ( asset.contains( 
"tilesetVersion" ) )
 
 1162        const QString tilesetVersion = QString::fromStdString( asset[
"tilesetVersion"].get<std::string>() );
 
 1163        metadata += QStringLiteral( 
"<tr><td class=\"highlight\">" ) % tr( 
"Tileset Version" ) % QStringLiteral( 
"</td><td>%1</a>" ).arg( tilesetVersion ) % QStringLiteral( 
"</td></tr>\n" );
 
 1165      catch ( json::type_error & )
 
 1167        QgsDebugError( QStringLiteral( 
"Error when parsing tilesetVersion value" ) );
 
 1171    if ( asset.contains( 
"generator" ) )
 
 1173      const QString generator = QString::fromStdString( asset[
"generator"].get<std::string>() );
 
 1174      metadata += QStringLiteral( 
"<tr><td class=\"highlight\">" ) % tr( 
"Tileset Generator" ) % QStringLiteral( 
"</td><td>%1</a>" ).arg( generator ) % QStringLiteral( 
"</td></tr>\n" );
 
 1177  if ( mShared->mTileset.contains( 
"extensionsRequired" ) )
 
 1179    QStringList extensions;
 
 1180    for ( 
const auto &item : mShared->mTileset[
"extensionsRequired"] )
 
 1182      extensions << QString::fromStdString( item.get<std::string>() );
 
 1184    if ( !extensions.isEmpty() )
 
 1186      metadata += QStringLiteral( 
"<tr><td class=\"highlight\">" ) % tr( 
"Extensions Required" ) % QStringLiteral( 
"</td><td><ul><li>%1</li></ul></a>" ).arg( extensions.join( QLatin1String( 
"</li><li>" ) ) ) % QStringLiteral( 
"</td></tr>\n" );
 
 1189  if ( mShared->mTileset.contains( 
"extensionsUsed" ) )
 
 1191    QStringList extensions;
 
 1192    for ( 
const auto &item : mShared->mTileset[
"extensionsUsed"] )
 
 1194      extensions << QString::fromStdString( item.get<std::string>() );
 
 1196    if ( !extensions.isEmpty() )
 
 1198      metadata += QStringLiteral( 
"<tr><td class=\"highlight\">" ) % tr( 
"Extensions Used" ) % QStringLiteral( 
"</td><td><ul><li>%1</li></ul></a>" ).arg( extensions.join( QLatin1String( 
"</li><li>" ) ) ) % QStringLiteral( 
"</td></tr>\n" );
 
 1202  if ( !mShared->mZRange.isInfinite() )
 
 1204    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" );
 
 1217  return mShared->mLayerMetadata;
 
 1227  return mShared->mSceneCrs ;
 
 1238  return mShared ? mShared->mBoundingVolume : nullVolume;
 
 1248  return mShared->mIndex;
 
 1258  return mShared->mZRange;
 
 1266QgsCesiumTilesProviderMetadata::QgsCesiumTilesProviderMetadata():
 
 1271QIcon QgsCesiumTilesProviderMetadata::icon()
 const 
 1276QgsCesiumTilesDataProvider *QgsCesiumTilesProviderMetadata::createProvider( 
const QString &uri, 
const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
 
 1278  return new QgsCesiumTilesDataProvider( uri, options, flags );
 
 1281QList<QgsProviderSublayerDetails> QgsCesiumTilesProviderMetadata::querySublayers( 
const QString &uri, Qgis::SublayerQueryFlags, 
QgsFeedback * )
 const 
 1283  const QVariantMap parts = decodeUri( uri );
 
 1284  if ( parts.value( QStringLiteral( 
"file-name" ) ).toString().compare( QLatin1String( 
"tileset.json" ), Qt::CaseInsensitive ) == 0 )
 
 1299int QgsCesiumTilesProviderMetadata::priorityForUri( 
const QString &uri )
 const 
 1301  const QVariantMap parts = decodeUri( uri );
 
 1302  if ( parts.value( QStringLiteral( 
"file-name" ) ).toString().compare( QLatin1String( 
"tileset.json" ), Qt::CaseInsensitive ) == 0 )
 
 1308QList<Qgis::LayerType> QgsCesiumTilesProviderMetadata::validLayerTypesForUri( 
const QString &uri )
 const 
 1310  const QVariantMap parts = decodeUri( uri );
 
 1311  if ( parts.value( QStringLiteral( 
"file-name" ) ).toString().compare( QLatin1String( 
"tileset.json" ), Qt::CaseInsensitive ) == 0 )
 
 1314  return QList< Qgis::LayerType>();
 
 1317QVariantMap QgsCesiumTilesProviderMetadata::decodeUri( 
const QString &uri )
 const 
 1319  QVariantMap uriComponents;
 
 1320  QUrl url = QUrl::fromUserInput( uri );
 
 1321  uriComponents.insert( QStringLiteral( 
"file-name" ), url.fileName() );
 
 1322  uriComponents.insert( QStringLiteral( 
"path" ), uri );
 
 1323  return uriComponents;
 
 1339      return QObject::tr( 
"Cesium 3D Tiles" ) + QStringLiteral( 
" (tileset.json TILESET.JSON)" );
 
 1344QgsProviderMetadata::ProviderCapabilities QgsCesiumTilesProviderMetadata::providerCapabilities()
 const 
 1346  return FileBasedUris;
 
 1349QList<Qgis::LayerType> QgsCesiumTilesProviderMetadata::supportedLayerTypes()
 const 
 1354QString QgsCesiumTilesProviderMetadata::encodeUri( 
const QVariantMap &parts )
 const 
 1356  const QString path = parts.value( QStringLiteral( 
"path" ) ).toString();
 
 1360QgsProviderMetadata::ProviderMetadataCapabilities QgsCesiumTilesProviderMetadata::capabilities()
 const 
 1362  return ProviderMetadataCapability::LayerTypesForUri
 
 1363         | ProviderMetadataCapability::PriorityForUri
 
 1364         | ProviderMetadataCapability::QuerySublayers;
 
The Qgis class provides global constants for use throughout the application.
FileFilterType
Type of file filters.
@ TiledScene
Tiled scene layers (since QGIS 3.34)
@ VectorTile
Vector tile layers (since QGIS 3.32)
@ MeshDataset
Mesh datasets.
@ PointCloud
Point clouds (since QGIS 3.18)
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata()
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.
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
@ NetworkError
A network error occurred.
@ ServerExceptionError
An exception was raised by the server.
@ NoError
No error was encountered.
@ TimeoutError
Timeout was reached before a reply was received.
A 3-dimensional box composed of x, y, z coordinates.
double zMaximum() const
Returns the maximum z value.
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
QVector< QgsVector3D > corners() const
Returns an array of all box corners as 3D vectors.
double width() const
Returns the width of the box.
double zMinimum() const
Returns the minimum z value.
double height() const
Returns the height of the box.
bool isNull() const
Test if the box is null (holding no spatial information).
static QgsSphere parseSphere(const json &sphere)
Parses a sphere object from a Cesium JSON document.
static QgsOrientedBox3D parseBox(const json &box)
Parses a box object from a Cesium JSON document to an oriented bounding box.
static QgsBox3D parseRegion(const json ®ion)
Parses a region object from a Cesium JSON object to a 3D box.
static QgsSphere transformSphere(const QgsSphere &sphere, const QgsMatrix4x4 &transform)
Applies a transform to a sphere.
This class 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.
Class for storing the component parts of a RDBMS data source URI (e.g.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
QgsHttpHeaders httpHeaders() const
Returns http headers.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
QString authConfigId() const
Returns any associated authentication configuration ID stored in the URI.
QgsRange which stores a range of double values.
QgsErrorMessage represents 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)
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.
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
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 scene CRS units.
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 scene CRS units, of the tile's simplified ...
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
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.