From 648cbc9aef720fb3841387df96b65627d9da9796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ko=C5=88a=C5=99=C3=ADk?= Date: Tue, 12 Nov 2024 09:56:25 +0100 Subject: [PATCH 1/2] Merge remote and local QgsCopcPointCloudIndex classes --- src/core/CMakeLists.txt | 2 - .../pointcloud/qgscopcpointcloudindex.cpp | 198 +++++++++++++----- src/core/pointcloud/qgscopcpointcloudindex.h | 6 +- .../qgsremotecopcpointcloudindex.cpp | 170 --------------- .../pointcloud/qgsremotecopcpointcloudindex.h | 82 -------- src/core/providers/copc/qgscopcprovider.cpp | 7 +- .../vpc/qgsvirtualpointcloudprovider.cpp | 3 +- 7 files changed, 152 insertions(+), 316 deletions(-) delete mode 100644 src/core/pointcloud/qgsremotecopcpointcloudindex.cpp delete mode 100644 src/core/pointcloud/qgsremotecopcpointcloudindex.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 808540650d29..eaad00402b26 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2256,7 +2256,6 @@ if (WITH_COPC) providers/copc/qgscopcprovider.cpp providers/vpc/qgsvirtualpointcloudprovider.cpp pointcloud/qgscopcpointcloudindex.cpp - pointcloud/qgsremotecopcpointcloudindex.cpp pointcloud/qgscopcpointcloudblockrequest.cpp pointcloud/qgscachedpointcloudblockrequest.cpp ) @@ -2264,7 +2263,6 @@ if (WITH_COPC) providers/copc/qgscopcprovider.h providers/vpc/qgsvirtualpointcloudprovider.h pointcloud/qgscopcpointcloudindex.h - pointcloud/qgsremotecopcpointcloudindex.h pointcloud/qgscopcpointcloudblockrequest.h pointcloud/qgscachedpointcloudblockrequest.h ) diff --git a/src/core/pointcloud/qgscopcpointcloudindex.cpp b/src/core/pointcloud/qgscopcpointcloudindex.cpp index f25459299aaf..804f2f11e14c 100644 --- a/src/core/pointcloud/qgscopcpointcloudindex.cpp +++ b/src/core/pointcloud/qgscopcpointcloudindex.cpp @@ -26,19 +26,21 @@ #include #include +#include "qgsapplication.h" +#include "qgscachedpointcloudblockrequest.h" +#include "qgscopcpointcloudblockrequest.h" #include "qgseptdecoder.h" #include "qgslazdecoder.h" #include "qgscoordinatereferencesystem.h" +#include "qgspointcloudblockrequest.h" #include "qgspointcloudrequest.h" #include "qgspointcloudattribute.h" #include "qgslogger.h" -#include "qgsfeedback.h" #include "qgsmessagelog.h" #include "qgspointcloudexpression.h" -#include "lazperf/lazperf.hpp" -#include "lazperf/readers.hpp" #include "lazperf/vlr.hpp" +#include "qgssetrequestinitiator_p.h" ///@cond PRIVATE @@ -57,31 +59,42 @@ std::unique_ptr QgsCopcPointCloudIndex::clone() const return std::unique_ptr( clone ); } -void QgsCopcPointCloudIndex::load( const QString &fileName ) +void QgsCopcPointCloudIndex::load( const QString &urlString ) { - mUri = fileName; - mCopcFile.open( QgsLazDecoder::toNativePath( fileName ), std::ios::binary ); - - if ( !mCopcFile.is_open() || !mCopcFile.good() ) + QUrl url = urlString; + // Treat non-URLs as local files + if ( url.isValid() && ( url.scheme() == "http" || url.scheme() == "https" ) ) + mAccessType = Remote; + else { - mError = tr( "Unable to open %1 for reading" ).arg( fileName ); - mIsValid = false; - return; + mAccessType = Local; + mCopcFile.open( QgsLazDecoder::toNativePath( urlString ), std::ios::binary ); + if ( mCopcFile.fail() ) + { + mError = tr( "Unable to open %1 for reading" ).arg( urlString ); + mIsValid = false; + return; + } } + mUri = urlString; - mLazInfo.reset( new QgsLazInfo( QgsLazInfo::fromFile( mCopcFile ) ) ); + if ( mAccessType == Remote ) + mLazInfo.reset( new QgsLazInfo( QgsLazInfo::fromUrl( url ) ) ); + else + mLazInfo.reset( new QgsLazInfo( QgsLazInfo::fromFile( mCopcFile ) ) ); mIsValid = mLazInfo->isValid(); if ( mIsValid ) { mIsValid = loadSchema( *mLazInfo.get() ); + if ( mIsValid ) + { + loadHierarchy(); + } } if ( !mIsValid ) { - mError = tr( "Unable to recognize %1 as a LAZ file: \"%2\"" ).arg( fileName, mLazInfo->error() ); - return; + mError = tr( "Unable to recognize %1 as a LAZ file: \"%2\"" ).arg( urlString, mLazInfo->error() ); } - - loadHierarchy(); } bool QgsCopcPointCloudIndex::loadSchema( QgsLazInfo &lazInfo ) @@ -144,43 +157,84 @@ std::unique_ptr QgsCopcPointCloudIndex::nodeData( const Inde return std::unique_ptr( cached ); } - const bool found = fetchNodeHierarchy( n ); - if ( !found ) - return nullptr; - mHierarchyMutex.lock(); - int pointCount = mHierarchy.value( n ); - auto [blockOffset, blockSize] = mHierarchyNodePos.value( n ); - mHierarchyMutex.unlock(); - - // we need to create a copy of the expression to pass to the decoder - // as the same QgsPointCloudExpression object mighgt be concurrently - // used on another thread, for example in a 3d view - QgsPointCloudExpression filterExpression = mFilterExpression; - QgsPointCloudAttributeCollection requestAttributes = request.attributes(); - requestAttributes.extend( attributes(), filterExpression.referencedAttributes() ); + std::unique_ptr block; + if ( mAccessType == Local ) + { + const bool found = fetchNodeHierarchy( n ); + if ( !found ) + return nullptr; + mHierarchyMutex.lock(); + int pointCount = mHierarchy.value( n ); + auto [blockOffset, blockSize] = mHierarchyNodePos.value( n ); + mHierarchyMutex.unlock(); + + // we need to create a copy of the expression to pass to the decoder + // as the same QgsPointCloudExpression object mighgt be concurrently + // used on another thread, for example in a 3d view + QgsPointCloudExpression filterExpression = mFilterExpression; + QgsPointCloudAttributeCollection requestAttributes = request.attributes(); + requestAttributes.extend( attributes(), filterExpression.referencedAttributes() ); + + QByteArray rawBlockData( blockSize, Qt::Initialization::Uninitialized ); + std::ifstream file( QgsLazDecoder::toNativePath( mUri ), std::ios::binary ); + file.seekg( blockOffset ); + file.read( rawBlockData.data(), blockSize ); + if ( !file ) + { + QgsDebugError( QStringLiteral( "Could not read file %1" ).arg( mUri ) ); + return nullptr; + } + QgsRectangle filterRect = request.filterRect(); - QByteArray rawBlockData( blockSize, Qt::Initialization::Uninitialized ); - std::ifstream file( QgsLazDecoder::toNativePath( mUri ), std::ios::binary ); - file.seekg( blockOffset ); - file.read( rawBlockData.data(), blockSize ); - if ( !file ) + block = QgsLazDecoder::decompressCopc( rawBlockData, *mLazInfo.get(), pointCount, requestAttributes, filterExpression, filterRect ); + } + else { - QgsDebugError( QStringLiteral( "Could not read file %1" ).arg( mUri ) ); - return nullptr; + + std::unique_ptr blockRequest( asyncNodeData( n, request ) ); + if ( !blockRequest ) + return nullptr; + + QEventLoop loop; + connect( blockRequest.get(), &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit ); + loop.exec(); + + block = blockRequest->takeBlock(); + + if ( !block ) + QgsDebugError( QStringLiteral( "Error downloading node %1 data, error : %2 " ).arg( n.toString(), blockRequest->errorStr() ) ); } - QgsRectangle filterRect = request.filterRect(); - std::unique_ptr decoded = QgsLazDecoder::decompressCopc( rawBlockData, *mLazInfo.get(), pointCount, requestAttributes, filterExpression, filterRect ); - storeNodeDataToCache( decoded.get(), n, request ); - return decoded; + storeNodeDataToCache( block.get(), n, request ); + return block; } QgsPointCloudBlockRequest *QgsCopcPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request ) { - Q_UNUSED( n ) - Q_UNUSED( request ) - Q_ASSERT( false ); - return nullptr; // unsupported + if ( mAccessType == Local ) + return nullptr; // TODO + if ( QgsPointCloudBlock *cached = getNodeDataFromCache( n, request ) ) + { + return new QgsCachedPointCloudBlockRequest( cached, n, mUri, attributes(), request.attributes(), + scale(), offset(), mFilterExpression, request.filterRect() ); + } + + if ( !fetchNodeHierarchy( n ) ) + return nullptr; + QMutexLocker locker( &mHierarchyMutex ); + + // we need to create a copy of the expression to pass to the decoder + // as the same QgsPointCloudExpression object might be concurrently + // used on another thread, for example in a 3d view + QgsPointCloudExpression filterExpression = mFilterExpression; + QgsPointCloudAttributeCollection requestAttributes = request.attributes(); + requestAttributes.extend( attributes(), filterExpression.referencedAttributes() ); + auto [ blockOffset, blockSize ] = mHierarchyNodePos.value( n ); + int pointCount = mHierarchy.value( n ); + + return new QgsCopcPointCloudBlockRequest( n, mUri, attributes(), requestAttributes, + scale(), offset(), filterExpression, request.filterRect(), + blockOffset, blockSize, pointCount, *mLazInfo.get() ); } QgsCoordinateReferenceSystem QgsCopcPointCloudIndex::crs() const @@ -201,6 +255,12 @@ bool QgsCopcPointCloudIndex::loadHierarchy() bool QgsCopcPointCloudIndex::writeStatistics( QgsPointCloudStatistics &stats ) { + if ( mAccessType == Remote ) + { + QgsMessageLog::logMessage( tr( "Can't write statistics to remote file \"%1\"" ).arg( mUri ) ); + return false; + } + if ( mLazInfo->version() != qMakePair( 1, 4 ) ) { // EVLR isn't supported in the first place @@ -298,11 +358,46 @@ bool QgsCopcPointCloudIndex::fetchNodeHierarchy( const IndexedPointCloudNode &n void QgsCopcPointCloudIndex::fetchHierarchyPage( uint64_t offset, uint64_t byteSize ) const { - mCopcFile.seekg( offset ); - std::unique_ptr data( new char[ byteSize ] ); - mCopcFile.read( data.get(), byteSize ); + Q_ASSERT( byteSize > 0 ); + + switch ( mAccessType ) + { + case Local: + { + mCopcFile.seekg( offset ); + std::unique_ptr data( new char[ byteSize ] ); + mCopcFile.read( data.get(), byteSize ); + + populateHierarchy( data.get(), byteSize ); + return; + } + case Remote: + { + QNetworkRequest nr = QNetworkRequest( QUrl( mUri ) ); + QgsSetRequestInitiatorClass( nr, QStringLiteral( "QgsCopcPointCloudIndex" ) ); + nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); + nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); + QByteArray queryRange = QStringLiteral( "bytes=%1-%2" ).arg( offset ).arg( offset + byteSize - 1 ).toLocal8Bit(); + nr.setRawHeader( "Range", queryRange ); + + std::unique_ptr reply( QgsApplication::tileDownloadManager()->get( nr ) ); + + QEventLoop loop; + connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit ); + loop.exec(); + + if ( reply->error() != QNetworkReply::NoError ) + { + QgsDebugError( QStringLiteral( "Request failed: " ) + mUri ); + return; + } - populateHierarchy( data.get(), byteSize ); + QByteArray data = reply->data(); + + populateHierarchy( data.constData(), byteSize ); + return; + } + } } void QgsCopcPointCloudIndex::populateHierarchy( const char *hierarchyPageData, uint64_t byteSize ) const @@ -371,8 +466,10 @@ void QgsCopcPointCloudIndex::copyCommonProperties( QgsCopcPointCloudIndex *desti // QgsCopcPointCloudIndex specific fields destination->mIsValid = mIsValid; + destination->mAccessType = mAccessType; destination->mUri = mUri; - destination->mCopcFile.open( QgsLazDecoder::toNativePath( mUri ), std::ios::binary ); + if ( mAccessType == Local ) + destination->mCopcFile.open( QgsLazDecoder::toNativePath( mUri ), std::ios::binary ); destination->mCopcInfoVlr = mCopcInfoVlr; destination->mHierarchyNodePos = mHierarchyNodePos; destination->mOriginalMetadata = mOriginalMetadata; @@ -381,6 +478,7 @@ void QgsCopcPointCloudIndex::copyCommonProperties( QgsCopcPointCloudIndex *desti QByteArray QgsCopcPointCloudIndex::fetchCopcStatisticsEvlrData() { + Q_ASSERT( mAccessType == Local ); // TODO: Remote uint64_t offset = mLazInfo->firstEvlrOffset(); uint32_t evlrCount = mLazInfo->evlrCount(); diff --git a/src/core/pointcloud/qgscopcpointcloudindex.h b/src/core/pointcloud/qgscopcpointcloudindex.h index f203ef732df3..19824cee44f2 100644 --- a/src/core/pointcloud/qgscopcpointcloudindex.h +++ b/src/core/pointcloud/qgscopcpointcloudindex.h @@ -29,9 +29,6 @@ #include #include "qgspointcloudindex.h" -#include "qgspointcloudattribute.h" -#include "qgsstatisticalsummary.h" -#include "qgis_sip.h" #include "qgspointcloudstatistics.h" #include "qgslazinfo.h" @@ -66,7 +63,7 @@ class CORE_EXPORT QgsCopcPointCloudIndex: public QgsPointCloudIndex QVariantMap originalMetadata() const override { return mOriginalMetadata; } bool isValid() const override; - QgsPointCloudIndex::AccessType accessType() const override { return QgsPointCloudIndex::Local; }; + QgsPointCloudIndex::AccessType accessType() const override { return mAccessType; }; /** * Writes the statistics object \a stats into the COPC dataset as an Extended Variable Length Record (EVLR). @@ -105,6 +102,7 @@ class CORE_EXPORT QgsCopcPointCloudIndex: public QgsPointCloudIndex QByteArray fetchCopcStatisticsEvlrData(); bool mIsValid = false; + QgsPointCloudIndex::AccessType mAccessType = Local; mutable std::ifstream mCopcFile; mutable lazperf::copc_info_vlr mCopcInfoVlr; mutable QHash> mHierarchyNodePos; //!< Additional data hierarchy for COPC diff --git a/src/core/pointcloud/qgsremotecopcpointcloudindex.cpp b/src/core/pointcloud/qgsremotecopcpointcloudindex.cpp deleted file mode 100644 index 5c78a301881c..000000000000 --- a/src/core/pointcloud/qgsremotecopcpointcloudindex.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/*************************************************************************** - qgsremotecopcpointcloudindex.cpp - -------------------- - begin : March 2022 - copyright : (C) 2022 by Belgacem Nedjima - email : belgacem dot nedjima at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "qgsremotecopcpointcloudindex.h" -#include "moc_qgsremotecopcpointcloudindex.cpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qgspointcloudrequest.h" -#include "qgspointcloudattribute.h" -#include "qgslogger.h" -#include "qgstiledownloadmanager.h" -#include "qgsapplication.h" -#include "qgscopcpointcloudblockrequest.h" -#include "qgscachedpointcloudblockrequest.h" -#include "qgspointcloudexpression.h" -#include "qgsnetworkaccessmanager.h" -#include "qgssetrequestinitiator_p.h" - -///@cond PRIVATE - -QgsRemoteCopcPointCloudIndex::QgsRemoteCopcPointCloudIndex() = default; - -QgsRemoteCopcPointCloudIndex::~QgsRemoteCopcPointCloudIndex() = default; - -std::unique_ptr QgsRemoteCopcPointCloudIndex::clone() const -{ - QgsRemoteCopcPointCloudIndex *clone = new QgsRemoteCopcPointCloudIndex; - QMutexLocker locker( &mHierarchyMutex ); - copyCommonProperties( clone ); - return std::unique_ptr( clone ); -} - -void QgsRemoteCopcPointCloudIndex::load( const QString &uri ) -{ - mUri = uri; - QUrl url( uri ); - mLazInfo.reset( new QgsLazInfo( QgsLazInfo::fromUrl( url ) ) ); - mIsValid = mLazInfo->isValid(); - if ( mIsValid ) - { - mIsValid = loadSchema( *mLazInfo.get() ); - if ( mIsValid ) - { - loadHierarchy(); - } - } - if ( !mIsValid ) - { - mError = tr( "Unable to recognize %1 as a LAZ file: \"%2\"" ).arg( uri, mLazInfo->error() ); - } -} - -std::unique_ptr QgsRemoteCopcPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request ) -{ - if ( QgsPointCloudBlock *cached = getNodeDataFromCache( n, request ) ) - { - return std::unique_ptr( cached ); - } - - std::unique_ptr blockRequest( asyncNodeData( n, request ) ); - if ( !blockRequest ) - return nullptr; - - QEventLoop loop; - connect( blockRequest.get(), &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit ); - loop.exec(); - - std::unique_ptr block = blockRequest->takeBlock(); - - if ( !block ) - { - QgsDebugError( QStringLiteral( "Error downloading node %1 data, error : %2 " ).arg( n.toString(), blockRequest->errorStr() ) ); - } - - storeNodeDataToCache( block.get(), n, request ); - return block; -} - -QgsPointCloudBlockRequest *QgsRemoteCopcPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request ) -{ - if ( QgsPointCloudBlock *cached = getNodeDataFromCache( n, request ) ) - { - return new QgsCachedPointCloudBlockRequest( cached, n, mUri, attributes(), request.attributes(), - scale(), offset(), mFilterExpression, request.filterRect() ); - } - - if ( !fetchNodeHierarchy( n ) ) - return nullptr; - QMutexLocker locker( &mHierarchyMutex ); - - // we need to create a copy of the expression to pass to the decoder - // as the same QgsPointCloudExpression object might be concurrently - // used on another thread, for example in a 3d view - QgsPointCloudExpression filterExpression = mFilterExpression; - QgsPointCloudAttributeCollection requestAttributes = request.attributes(); - requestAttributes.extend( attributes(), filterExpression.referencedAttributes() ); - auto [ blockOffset, blockSize ] = mHierarchyNodePos.value( n ); - int pointCount = mHierarchy.value( n ); - - return new QgsCopcPointCloudBlockRequest( n, mUri, attributes(), requestAttributes, - scale(), offset(), filterExpression, request.filterRect(), - blockOffset, blockSize, pointCount, *mLazInfo.get() ); -} - -bool QgsRemoteCopcPointCloudIndex::isValid() const -{ - return mIsValid; -} - -void QgsRemoteCopcPointCloudIndex::fetchHierarchyPage( uint64_t offset, uint64_t byteSize ) const -{ - Q_ASSERT( byteSize > 0 ); - - QNetworkRequest nr = QNetworkRequest( QUrl( mUri ) ); - QgsSetRequestInitiatorClass( nr, QStringLiteral( "QgsRemoteCopcPointCloudIndex" ) ); - nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); - nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); - QByteArray queryRange = QStringLiteral( "bytes=%1-%2" ).arg( offset ).arg( offset + byteSize - 1 ).toLocal8Bit(); - nr.setRawHeader( "Range", queryRange ); - - std::unique_ptr reply( QgsApplication::tileDownloadManager()->get( nr ) ); - - QEventLoop loop; - connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit ); - loop.exec(); - - if ( reply->error() != QNetworkReply::NoError ) - { - QgsDebugError( QStringLiteral( "Request failed: " ) + mUri ); - return; - } - - QByteArray data = reply->data(); - - populateHierarchy( data.constData(), byteSize ); -} - -void QgsRemoteCopcPointCloudIndex::copyCommonProperties( QgsRemoteCopcPointCloudIndex *destination ) const -{ - QgsCopcPointCloudIndex::copyCommonProperties( destination ); - - // QgsRemoteCopcPointCloudIndex specific fields - destination->mHierarchyNodes = mHierarchyNodes; -} - -///@endcond diff --git a/src/core/pointcloud/qgsremotecopcpointcloudindex.h b/src/core/pointcloud/qgsremotecopcpointcloudindex.h deleted file mode 100644 index a8ea876c7a16..000000000000 --- a/src/core/pointcloud/qgsremotecopcpointcloudindex.h +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************** - qgsremotecopcpointcloudindex.h - -------------------- - begin : March 2022 - copyright : (C) 2022 by Belgacem Nedjima - email : belgacem dot nedjima at gmail dot com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef QGSREMOTECOPCPOINTCLOUDINDEX_H -#define QGSREMOTECOPCPOINTCLOUDINDEX_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "qgspointcloudindex.h" -#include "qgscopcpointcloudindex.h" -#include "qgslazinfo.h" - -#include "lazperf/header.hpp" -#include "lazperf/vlr.hpp" - -///@cond PRIVATE -#define SIP_NO_FILE - -class QgsCoordinateReferenceSystem; -class QgsTileDownloadManager; -class QgsCopcPointCloudBlockRequest; - -class CORE_EXPORT QgsRemoteCopcPointCloudIndex: public QgsCopcPointCloudIndex -{ - Q_OBJECT - public: - - explicit QgsRemoteCopcPointCloudIndex(); - ~QgsRemoteCopcPointCloudIndex(); - - std::unique_ptr clone() const override; - - void load( const QString &uri ) override; - - std::unique_ptr nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request ) override; - QgsPointCloudBlockRequest *asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request ) override; - - bool isValid() const override; - - QgsPointCloudIndex::AccessType accessType() const override { return QgsPointCloudIndex::Remote; } - - /** - * Copies common properties to the \a destination index - * \since QGIS 3.26 - */ - void copyCommonProperties( QgsRemoteCopcPointCloudIndex *destination ) const; - - protected: - virtual void fetchHierarchyPage( uint64_t offset, uint64_t byteSize ) const override; - - QUrl mUrl; - - mutable QSet mHierarchyNodes; -}; - -///@endcond - -#endif // QGSREMOTECOPCPOINTCLOUDINDEX_H diff --git a/src/core/providers/copc/qgscopcprovider.cpp b/src/core/providers/copc/qgscopcprovider.cpp index 909e07e65d16..12d04199fc6b 100644 --- a/src/core/providers/copc/qgscopcprovider.cpp +++ b/src/core/providers/copc/qgscopcprovider.cpp @@ -19,7 +19,6 @@ #include "qgscopcprovider.h" #include "moc_qgscopcprovider.cpp" #include "qgscopcpointcloudindex.h" -#include "qgsremotecopcpointcloudindex.h" #include "qgsruntimeprofiler.h" #include "qgsapplication.h" #include "qgsprovidersublayerdetails.h" @@ -39,11 +38,7 @@ QgsCopcProvider::QgsCopcProvider( Qgis::DataProviderReadFlags flags ) : QgsPointCloudDataProvider( uri, options, flags ) { - bool isRemote = uri.startsWith( QStringLiteral( "http" ), Qt::CaseSensitivity::CaseInsensitive ); - if ( isRemote ) - mIndex.reset( new QgsRemoteCopcPointCloudIndex ); - else - mIndex.reset( new QgsCopcPointCloudIndex ); + mIndex.reset( new QgsCopcPointCloudIndex ); std::unique_ptr< QgsScopedRuntimeProfile > profile; if ( QgsApplication::profiler()->groupIsActive( QStringLiteral( "projectload" ) ) ) diff --git a/src/core/providers/vpc/qgsvirtualpointcloudprovider.cpp b/src/core/providers/vpc/qgsvirtualpointcloudprovider.cpp index 2bd066696c17..3f76477399ac 100644 --- a/src/core/providers/vpc/qgsvirtualpointcloudprovider.cpp +++ b/src/core/providers/vpc/qgsvirtualpointcloudprovider.cpp @@ -22,7 +22,6 @@ #include "moc_qgsvirtualpointcloudprovider.cpp" #include "qgscopcpointcloudindex.h" #include "qgseptpointcloudindex.h" -#include "qgsremotecopcpointcloudindex.h" #include "qgsremoteeptpointcloudindex.h" #include "qgspointcloudsubindex.h" #include "qgspointcloudclassifiedrenderer.h" @@ -395,7 +394,7 @@ void QgsVirtualPointCloudProvider::loadSubIndex( int i ) if ( sl.uri().startsWith( QStringLiteral( "http" ), Qt::CaseSensitivity::CaseInsensitive ) ) { if ( sl.uri().endsWith( QStringLiteral( "copc.laz" ), Qt::CaseSensitivity::CaseInsensitive ) ) - sl.setIndex( new QgsRemoteCopcPointCloudIndex() ); + sl.setIndex( new QgsCopcPointCloudIndex() ); else if ( sl.uri().endsWith( QStringLiteral( "ept.json" ), Qt::CaseSensitivity::CaseInsensitive ) ) sl.setIndex( new QgsRemoteEptPointCloudIndex() ); } From ed004d82e53f3336c19a853ef583f7a21e6a3c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ko=C5=88a=C5=99=C3=ADk?= Date: Tue, 12 Nov 2024 09:57:56 +0100 Subject: [PATCH 2/2] Improve error handling --- src/core/pointcloud/qgspointcloudstatscalculator.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/pointcloud/qgspointcloudstatscalculator.cpp b/src/core/pointcloud/qgspointcloudstatscalculator.cpp index 86f13c064f70..4cdf9388b1f0 100644 --- a/src/core/pointcloud/qgspointcloudstatscalculator.cpp +++ b/src/core/pointcloud/qgspointcloudstatscalculator.cpp @@ -70,6 +70,11 @@ struct StatsProcessor else { QgsPointCloudBlockRequest *request = mIndex->asyncNodeData( node, mRequest ); + if ( request == nullptr ) + { + QgsDebugError( QStringLiteral( "Unable to calculate statistics for node %1: Got nullptr async request" ).arg( node.toString() ) ); + return QgsPointCloudStatistics(); + } QEventLoop loop; QObject::connect( request, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit ); QObject::connect( mFeedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );