24 #include <QJsonDocument>
25 #include <QJsonObject>
45 #include "qgspointcloudexpression.h"
49 QgsRemoteCopcPointCloudIndex::QgsRemoteCopcPointCloudIndex() =
default;
51 QgsRemoteCopcPointCloudIndex::~QgsRemoteCopcPointCloudIndex() =
default;
53 std::unique_ptr<QgsPointCloudIndex> QgsRemoteCopcPointCloudIndex::clone()
const
55 QgsRemoteCopcPointCloudIndex *clone =
new QgsRemoteCopcPointCloudIndex;
56 QMutexLocker locker( &mHierarchyMutex );
57 copyCommonProperties( clone );
58 return std::unique_ptr<QgsPointCloudIndex>( clone );
61 QList<IndexedPointCloudNode> QgsRemoteCopcPointCloudIndex::nodeChildren(
const IndexedPointCloudNode &n )
const
63 fetchNodeHierarchy( n );
65 mHierarchyMutex.lock();
66 Q_ASSERT( mHierarchy.contains( n ) );
67 QList<IndexedPointCloudNode> lst;
69 const int d = n.
d() + 1;
70 const int x = n.
x() * 2;
71 const int y = n.
y() * 2;
72 const int z = n.
z() * 2;
73 mHierarchyMutex.unlock();
75 for (
int i = 0; i < 8; ++i )
77 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
79 if ( fetchNodeHierarchy( n2 ) && mHierarchy[n] > 0 )
85 void QgsRemoteCopcPointCloudIndex::load(
const QString &url )
89 mIsValid = mLazInfo->isValid();
92 mIsValid = loadSchema( *mLazInfo.get() );
100 mError = tr(
"Unable to recognize %1 as a LAZ file: \"%2\"" ).arg( url, mLazInfo->error() );
106 std::unique_ptr<QgsPointCloudBlockRequest> blockRequest( asyncNodeData( n, request ) );
114 if ( !blockRequest->block() )
116 QgsDebugMsg( QStringLiteral(
"Error downloading node %1 data, error : %2 " ).arg( n.
toString(), blockRequest->errorStr() ) );
119 return blockRequest->block();
124 if ( !fetchNodeHierarchy( n ) )
126 QMutexLocker locker( &mHierarchyMutex );
131 QgsPointCloudExpression filterExpression = mFilterExpression;
133 requestAttributes.
extend( attributes(), filterExpression.referencedAttributes() );
134 auto [ blockOffset, blockSize ] = mHierarchyNodePos.value( n );
135 int pointCount = mHierarchy.value( n );
138 scale(), offset(), filterExpression,
139 blockOffset, blockSize, pointCount, *mLazInfo.get() );
144 return fetchNodeHierarchy( n );
149 QMutexLocker locker( &mHierarchyMutex );
151 QVector<IndexedPointCloudNode> ancestors;
153 while ( !mHierarchy.contains( foundRoot ) )
155 ancestors.push_front( foundRoot );
158 ancestors.push_front( foundRoot );
161 auto hierarchyIt = mHierarchy.constFind( n );
162 if ( hierarchyIt == mHierarchy.constEnd() )
165 int nodesCount = *hierarchyIt;
166 if ( nodesCount < 0 )
168 auto hierarchyNodePos = mHierarchyNodePos.constFind( n );
169 fetchHierarchyPage( hierarchyNodePos->first, hierarchyNodePos->second );
172 return mHierarchy.contains( n );
175 bool QgsRemoteCopcPointCloudIndex::isValid()
const
180 void QgsRemoteCopcPointCloudIndex::fetchHierarchyPage( uint64_t offset, uint64_t byteSize )
const
182 QNetworkRequest nr( mUrl );
183 nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
184 nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
185 QByteArray queryRange = QStringLiteral(
"bytes=%1-%2" ).arg( offset ).arg( offset + byteSize - 1 ).toLocal8Bit();
186 nr.setRawHeader(
"Range", queryRange );
194 if ( reply->error() != QNetworkReply::NoError )
196 QgsDebugMsg( QStringLiteral(
"Request failed: " ) + mUrl.toString() );
200 QByteArray data = reply->data();
218 for ( uint64_t i = 0; i < byteSize; i +=
sizeof( CopcEntry ) )
220 CopcEntry *entry =
reinterpret_cast<CopcEntry *
>( data.data() + i );
222 mHierarchy[nodeId] = entry->pointCount;
223 mHierarchyNodePos.insert( nodeId, QPair<uint64_t, int32_t>( entry->offset, entry->byteSize ) );
227 void QgsRemoteCopcPointCloudIndex::copyCommonProperties( QgsRemoteCopcPointCloudIndex *destination )
const
229 QgsCopcPointCloudIndex::copyCommonProperties( destination );
232 destination->mUrl = mUrl;
233 destination->mHierarchyNodes = mHierarchyNodes;