QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
31 #include "qgseptdecoder.h"
33 #include "qgspointcloudrequest.h"
34 #include "qgspointcloudattribute.h"
35 #include "qgslogger.h"
36 #include "qgsfeedback.h"
37 #include "qgsmessagelog.h"
38 
39 #include "qgstiledownloadmanager.h"
41 #include "qgslazdecoder.h"
42 #include "qgsfileutils.h"
43 #include "qgsapplication.h"
45 #include "qgspointcloudexpression.h"
46 
48 
49 QgsRemoteCopcPointCloudIndex::QgsRemoteCopcPointCloudIndex() = default;
50 
51 QgsRemoteCopcPointCloudIndex::~QgsRemoteCopcPointCloudIndex() = default;
52 
53 std::unique_ptr<QgsPointCloudIndex> QgsRemoteCopcPointCloudIndex::clone() const
54 {
55  QgsRemoteCopcPointCloudIndex *clone = new QgsRemoteCopcPointCloudIndex;
56  QMutexLocker locker( &mHierarchyMutex );
57  copyCommonProperties( clone );
58  return std::unique_ptr<QgsPointCloudIndex>( clone );
59 }
60 
61 QList<IndexedPointCloudNode> QgsRemoteCopcPointCloudIndex::nodeChildren( const IndexedPointCloudNode &n ) const
62 {
63  fetchNodeHierarchy( n );
64 
65  mHierarchyMutex.lock();
66  Q_ASSERT( mHierarchy.contains( n ) );
67  QList<IndexedPointCloudNode> lst;
68  lst.reserve( 8 );
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();
74 
75  for ( int i = 0; i < 8; ++i )
76  {
77  int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
78  const IndexedPointCloudNode n2( d, x + dx, y + dy, z + dz );
79  if ( fetchNodeHierarchy( n2 ) && mHierarchy[n] > 0 )
80  lst.append( n2 );
81  }
82  return lst;
83 }
84 
85 void QgsRemoteCopcPointCloudIndex::load( const QString &url )
86 {
87  mUrl = QUrl( url );
88  mLazInfo.reset( new QgsLazInfo( QgsLazInfo::fromUrl( mUrl ) ) );
89  mIsValid = mLazInfo->isValid();
90  if ( mIsValid )
91  {
92  mIsValid = loadSchema( *mLazInfo.get() );
93  if ( mIsValid )
94  {
95  loadHierarchy();
96  }
97  }
98  if ( !mIsValid )
99  {
100  mError = tr( "Unable to recognize %1 as a LAZ file: \"%2\"" ).arg( url, mLazInfo->error() );
101  }
102 }
103 
104 QgsPointCloudBlock *QgsRemoteCopcPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
105 {
106  std::unique_ptr<QgsPointCloudBlockRequest> blockRequest( asyncNodeData( n, request ) );
107  if ( !blockRequest )
108  return nullptr;
109 
110  QEventLoop loop;
111  connect( blockRequest.get(), &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
112  loop.exec();
113 
114  if ( !blockRequest->block() )
115  {
116  QgsDebugMsg( QStringLiteral( "Error downloading node %1 data, error : %2 " ).arg( n.toString(), blockRequest->errorStr() ) );
117  }
118 
119  return blockRequest->block();
120 }
121 
122 QgsPointCloudBlockRequest *QgsRemoteCopcPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
123 {
124  if ( !fetchNodeHierarchy( n ) )
125  return nullptr;
126  QMutexLocker locker( &mHierarchyMutex );
127 
128  // we need to create a copy of the expression to pass to the decoder
129  // as the same QgsPointCloudExpression object might be concurrently
130  // used on another thread, for example in a 3d view
131  QgsPointCloudExpression filterExpression = mFilterExpression;
132  QgsPointCloudAttributeCollection requestAttributes = request.attributes();
133  requestAttributes.extend( attributes(), filterExpression.referencedAttributes() );
134  auto [ blockOffset, blockSize ] = mHierarchyNodePos.value( n );
135  int pointCount = mHierarchy.value( n );
136 
137  return new QgsCopcPointCloudBlockRequest( n, mUrl.toString(), attributes(), requestAttributes,
138  scale(), offset(), filterExpression,
139  blockOffset, blockSize, pointCount, *mLazInfo.get() );
140 }
141 
142 bool QgsRemoteCopcPointCloudIndex::hasNode( const IndexedPointCloudNode &n ) const
143 {
144  return fetchNodeHierarchy( n );
145 }
146 
147 bool QgsRemoteCopcPointCloudIndex::fetchNodeHierarchy( const IndexedPointCloudNode &n ) const
148 {
149  QMutexLocker locker( &mHierarchyMutex );
150 
151  QVector<IndexedPointCloudNode> ancestors;
152  IndexedPointCloudNode foundRoot = n;
153  while ( !mHierarchy.contains( foundRoot ) )
154  {
155  ancestors.push_front( foundRoot );
156  foundRoot = foundRoot.parentNode();
157  }
158  ancestors.push_front( foundRoot );
159  for ( IndexedPointCloudNode n : ancestors )
160  {
161  auto hierarchyIt = mHierarchy.constFind( n );
162  if ( hierarchyIt == mHierarchy.constEnd() )
163  return false;
164 
165  int nodesCount = *hierarchyIt;
166  if ( nodesCount < 0 )
167  {
168  auto hierarchyNodePos = mHierarchyNodePos.constFind( n );
169  fetchHierarchyPage( hierarchyNodePos->first, hierarchyNodePos->second );
170  }
171  }
172  return mHierarchy.contains( n );
173 }
174 
175 bool QgsRemoteCopcPointCloudIndex::isValid() const
176 {
177  return mIsValid;
178 }
179 
180 void QgsRemoteCopcPointCloudIndex::fetchHierarchyPage( uint64_t offset, uint64_t byteSize ) const
181 {
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 );
187 
188  std::unique_ptr<QgsTileDownloadManagerReply> reply( QgsApplication::tileDownloadManager()->get( nr ) );
189 
190  QEventLoop loop;
191  connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit );
192  loop.exec();
193 
194  if ( reply->error() != QNetworkReply::NoError )
195  {
196  QgsDebugMsg( QStringLiteral( "Request failed: " ) + mUrl.toString() );
197  return;
198  }
199 
200  QByteArray data = reply->data();
201 
202  struct CopcVoxelKey
203  {
204  int32_t level;
205  int32_t x;
206  int32_t y;
207  int32_t z;
208  };
209 
210  struct CopcEntry
211  {
212  CopcVoxelKey key;
213  uint64_t offset;
214  int32_t byteSize;
215  int32_t pointCount;
216  };
217 
218  for ( uint64_t i = 0; i < byteSize; i += sizeof( CopcEntry ) )
219  {
220  CopcEntry *entry = reinterpret_cast<CopcEntry *>( data.data() + i );
221  const IndexedPointCloudNode nodeId( entry->key.level, entry->key.x, entry->key.y, entry->key.z );
222  mHierarchy[nodeId] = entry->pointCount;
223  mHierarchyNodePos.insert( nodeId, QPair<uint64_t, int32_t>( entry->offset, entry->byteSize ) );
224  }
225 }
226 
227 void QgsRemoteCopcPointCloudIndex::copyCommonProperties( QgsRemoteCopcPointCloudIndex *destination ) const
228 {
229  QgsCopcPointCloudIndex::copyCommonProperties( destination );
230 
231  // QgsRemoteCopcPointCloudIndex specific fields
232  destination->mUrl = mUrl;
233  destination->mHierarchyNodes = mHierarchyNodes;
234 }
235 
QgsTileDownloadManagerReply::finished
void finished()
Emitted when the reply has finished (either with a success or with a failure)
qgstiledownloadmanager.h
QgsApplication::tileDownloadManager
static QgsTileDownloadManager * tileDownloadManager()
Returns the application's tile download manager, used for download of map tiles when rendering.
Definition: qgsapplication.cpp:2430
qgsblockingnetworkrequest.h
IndexedPointCloudNode::y
int y() const
Returns y.
Definition: qgspointcloudindex.cpp:73
IndexedPointCloudNode::x
int x() const
Returns x.
Definition: qgspointcloudindex.cpp:68
qgspointcloudattribute.h
QgsPointCloudBlock
Base class for storing raw data from point cloud nodes.
Definition: qgspointcloudblock.h:38
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
qgseptdecoder.h
IndexedPointCloudNode::d
int d() const
Returns d.
Definition: qgspointcloudindex.cpp:63
QgsPointCloudRequest
Point cloud data request.
Definition: qgspointcloudrequest.h:39
IndexedPointCloudNode::parentNode
IndexedPointCloudNode parentNode() const
Returns the parent of the node.
Definition: qgspointcloudindex.cpp:45
QgsPointCloudAttributeCollection::extend
void extend(const QgsPointCloudAttributeCollection &otherCollection, const QSet< QString > &matchingNames)
Adds specific missing attributes from another QgsPointCloudAttributeCollection.
Definition: qgspointcloudattribute.cpp:149
qgsapplication.h
IndexedPointCloudNode
Represents a indexed point cloud node in octree.
Definition: qgspointcloudindex.h:57
QgsPointCloudAttributeCollection
Collection of point cloud attributes.
Definition: qgspointcloudattribute.h:141
qgscopcpointcloudblockrequest.h
qgsfileutils.h
QgsPointCloudRequest::attributes
QgsPointCloudAttributeCollection attributes() const
Returns attributes.
Definition: qgspointcloudrequest.cpp:24
QgsLazInfo
Class for extracting information contained in LAZ file such as the public header block and variable l...
Definition: qgslazinfo.h:38
qgslazdecoder.h
QgsPointCloudBlockRequest
Base class for handling loading QgsPointCloudBlock asynchronously.
Definition: qgspointcloudblockrequest.h:36
QgsCopcPointCloudBlockRequest
Base class for handling loading QgsPointCloudBlock asynchronously from a remote COPC dataset.
Definition: qgscopcpointcloudblockrequest.h:39
QgsPointCloudBlockRequest::finished
void finished()
Emitted when the request processing has finished.
IndexedPointCloudNode::z
int z() const
Returns z.
Definition: qgspointcloudindex.cpp:78
qgslogger.h
IndexedPointCloudNode::toString
QString toString() const
Encode node to string.
Definition: qgspointcloudindex.cpp:58
qgspointcloudrequest.h
QgsLazInfo::fromUrl
static QgsLazInfo fromUrl(QUrl &url)
Static function to create a QgsLazInfo class from a file over network.
Definition: qgslazinfo.cpp:292
qgsfeedback.h
qgscoordinatereferencesystem.h
qgsremotecopcpointcloudindex.h
qgsmessagelog.h