QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgsremotecopcpointcloudindex.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsremotecopcpointcloudindex.cpp
3 --------------------
4 begin : March 2022
5 copyright : (C) 2022 by Belgacem Nedjima
6 email : belgacem dot nedjima at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include <QFile>
21#include <QFileInfo>
22#include <QDir>
23#include <QJsonArray>
24#include <QJsonDocument>
25#include <QJsonObject>
26#include <QTime>
27#include <QtDebug>
28#include <QQueue>
29#include <QTimer>
30
33#include "qgslogger.h"
35#include "qgsapplication.h"
37#include "qgspointcloudexpression.h"
39
41
42QgsRemoteCopcPointCloudIndex::QgsRemoteCopcPointCloudIndex() = default;
43
44QgsRemoteCopcPointCloudIndex::~QgsRemoteCopcPointCloudIndex() = default;
45
46std::unique_ptr<QgsPointCloudIndex> QgsRemoteCopcPointCloudIndex::clone() const
47{
48 QgsRemoteCopcPointCloudIndex *clone = new QgsRemoteCopcPointCloudIndex;
49 QMutexLocker locker( &mHierarchyMutex );
50 copyCommonProperties( clone );
51 return std::unique_ptr<QgsPointCloudIndex>( clone );
52}
53
54QList<IndexedPointCloudNode> QgsRemoteCopcPointCloudIndex::nodeChildren( const IndexedPointCloudNode &n ) const
55{
56 fetchNodeHierarchy( n );
57
58 mHierarchyMutex.lock();
59 Q_ASSERT( mHierarchy.contains( n ) );
60 QList<IndexedPointCloudNode> lst;
61 lst.reserve( 8 );
62 const int d = n.d() + 1;
63 const int x = n.x() * 2;
64 const int y = n.y() * 2;
65 const int z = n.z() * 2;
66 mHierarchyMutex.unlock();
67
68 for ( int i = 0; i < 8; ++i )
69 {
70 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
71 const IndexedPointCloudNode n2( d, x + dx, y + dy, z + dz );
72 if ( fetchNodeHierarchy( n2 ) && mHierarchy[n] >= 0 )
73 lst.append( n2 );
74 }
75 return lst;
76}
77
78void QgsRemoteCopcPointCloudIndex::load( const QString &url )
79{
80 mUrl = QUrl( url );
81 mLazInfo.reset( new QgsLazInfo( QgsLazInfo::fromUrl( mUrl ) ) );
82 mIsValid = mLazInfo->isValid();
83 if ( mIsValid )
84 {
85 mIsValid = loadSchema( *mLazInfo.get() );
86 if ( mIsValid )
87 {
88 loadHierarchy();
89 }
90 }
91 if ( !mIsValid )
92 {
93 mError = tr( "Unable to recognize %1 as a LAZ file: \"%2\"" ).arg( url, mLazInfo->error() );
94 }
95}
96
97std::unique_ptr<QgsPointCloudBlock> QgsRemoteCopcPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
98{
99 std::unique_ptr<QgsPointCloudBlockRequest> blockRequest( asyncNodeData( n, request ) );
100 if ( !blockRequest )
101 return nullptr;
102
103 QEventLoop loop;
104 connect( blockRequest.get(), &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
105 loop.exec();
106
107 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
108
109 if ( !block )
110 {
111 QgsDebugError( QStringLiteral( "Error downloading node %1 data, error : %2 " ).arg( n.toString(), blockRequest->errorStr() ) );
112 }
113
114 return block;
115}
116
117QgsPointCloudBlockRequest *QgsRemoteCopcPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
118{
119 if ( !fetchNodeHierarchy( n ) )
120 return nullptr;
121 QMutexLocker locker( &mHierarchyMutex );
122
123 // we need to create a copy of the expression to pass to the decoder
124 // as the same QgsPointCloudExpression object might be concurrently
125 // used on another thread, for example in a 3d view
126 QgsPointCloudExpression filterExpression = mFilterExpression;
127 QgsPointCloudAttributeCollection requestAttributes = request.attributes();
128 requestAttributes.extend( attributes(), filterExpression.referencedAttributes() );
129 auto [ blockOffset, blockSize ] = mHierarchyNodePos.value( n );
130 int pointCount = mHierarchy.value( n );
131
132 return new QgsCopcPointCloudBlockRequest( n, mUrl.toString(), attributes(), requestAttributes,
133 scale(), offset(), filterExpression, request.filterRect(),
134 blockOffset, blockSize, pointCount, *mLazInfo.get() );
135}
136
137bool QgsRemoteCopcPointCloudIndex::hasNode( const IndexedPointCloudNode &n ) const
138{
139 return fetchNodeHierarchy( n );
140}
141
142bool QgsRemoteCopcPointCloudIndex::fetchNodeHierarchy( const IndexedPointCloudNode &n ) const
143{
144 QMutexLocker locker( &mHierarchyMutex );
145
146 QVector<IndexedPointCloudNode> ancestors;
147 IndexedPointCloudNode foundRoot = n;
148 while ( !mHierarchy.contains( foundRoot ) )
149 {
150 ancestors.push_front( foundRoot );
151 foundRoot = foundRoot.parentNode();
152 }
153 ancestors.push_front( foundRoot );
154 for ( IndexedPointCloudNode n : ancestors )
155 {
156 auto hierarchyIt = mHierarchy.constFind( n );
157 if ( hierarchyIt == mHierarchy.constEnd() )
158 return false;
159
160 int nodesCount = *hierarchyIt;
161 if ( nodesCount < 0 )
162 {
163 auto hierarchyNodePos = mHierarchyNodePos.constFind( n );
164 fetchHierarchyPage( hierarchyNodePos->first, hierarchyNodePos->second );
165 }
166 }
167 return mHierarchy.contains( n );
168}
169
170bool QgsRemoteCopcPointCloudIndex::isValid() const
171{
172 return mIsValid;
173}
174
175void QgsRemoteCopcPointCloudIndex::fetchHierarchyPage( uint64_t offset, uint64_t byteSize ) const
176{
177 QNetworkRequest nr( mUrl );
178 QgsSetRequestInitiatorClass( nr, QStringLiteral( "QgsRemoteCopcPointCloudIndex" ) );
179 nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
180 nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
181 QByteArray queryRange = QStringLiteral( "bytes=%1-%2" ).arg( offset ).arg( offset + byteSize - 1 ).toLocal8Bit();
182 nr.setRawHeader( "Range", queryRange );
183
184 std::unique_ptr<QgsTileDownloadManagerReply> reply( QgsApplication::tileDownloadManager()->get( nr ) );
185
186 QEventLoop loop;
187 connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit );
188 loop.exec();
189
190 if ( reply->error() != QNetworkReply::NoError )
191 {
192 QgsDebugError( QStringLiteral( "Request failed: " ) + mUrl.toString() );
193 return;
194 }
195
196 QByteArray data = reply->data();
197
198 struct CopcVoxelKey
199 {
200 int32_t level;
201 int32_t x;
202 int32_t y;
203 int32_t z;
204 };
205
206 struct CopcEntry
207 {
208 CopcVoxelKey key;
209 uint64_t offset;
210 int32_t byteSize;
211 int32_t pointCount;
212 };
213
214 for ( uint64_t i = 0; i < byteSize; i += sizeof( CopcEntry ) )
215 {
216 CopcEntry *entry = reinterpret_cast<CopcEntry *>( data.data() + i );
217 const IndexedPointCloudNode nodeId( entry->key.level, entry->key.x, entry->key.y, entry->key.z );
218 mHierarchy[nodeId] = entry->pointCount;
219 mHierarchyNodePos.insert( nodeId, QPair<uint64_t, int32_t>( entry->offset, entry->byteSize ) );
220 }
221}
222
223void QgsRemoteCopcPointCloudIndex::copyCommonProperties( QgsRemoteCopcPointCloudIndex *destination ) const
224{
225 QgsCopcPointCloudIndex::copyCommonProperties( destination );
226
227 // QgsRemoteCopcPointCloudIndex specific fields
228 destination->mUrl = mUrl;
229 destination->mHierarchyNodes = mHierarchyNodes;
230}
231
Represents a indexed point cloud node in octree.
int y() const
Returns y.
int x() const
Returns x.
QString toString() const
Encode node to string.
int d() const
Returns d.
IndexedPointCloudNode parentNode() const
Returns the parent of the node.
int z() const
Returns z.
static QgsTileDownloadManager * tileDownloadManager()
Returns the application's tile download manager, used for download of map tiles when rendering.
Base class for handling loading QgsPointCloudBlock asynchronously from a remote COPC dataset.
Class for extracting information contained in LAZ file such as the public header block and variable l...
Definition qgslazinfo.h:39
static QgsLazInfo fromUrl(QUrl &url)
Static function to create a QgsLazInfo class from a file over network.
Collection of point cloud attributes.
void extend(const QgsPointCloudAttributeCollection &otherCollection, const QSet< QString > &matchingNames)
Adds specific missing attributes from another QgsPointCloudAttributeCollection.
Base class for handling loading QgsPointCloudBlock asynchronously.
void finished()
Emitted when the request processing has finished.
Point cloud data request.
QgsPointCloudAttributeCollection attributes() const
Returns attributes.
QgsRectangle filterRect() const
Returns the rectangle from which points will be taken, in point cloud's crs.
void finished()
Emitted when the reply has finished (either with a success or with a failure)
#define QgsDebugError(str)
Definition qgslogger.h:38
#define QgsSetRequestInitiatorClass(request, _class)