QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsremoteeptpointcloudindex.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsremoteeptpointcloudindex.cpp
3  --------------------
4  begin : March 2021
5  copyright : (C) 2021 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 
42 #include "qgsfileutils.h"
43 #include "qgsapplication.h"
45 #include "qgspointcloudexpression.h"
46 
48 
49 QgsRemoteEptPointCloudIndex::QgsRemoteEptPointCloudIndex() : QgsEptPointCloudIndex()
50 {
51  mHierarchyNodes.insert( IndexedPointCloudNode( 0, 0, 0, 0 ) );
52 }
53 
54 QgsRemoteEptPointCloudIndex::~QgsRemoteEptPointCloudIndex() = default;
55 
56 std::unique_ptr<QgsPointCloudIndex> QgsRemoteEptPointCloudIndex::clone() const
57 {
58  QgsRemoteEptPointCloudIndex *clone = new QgsRemoteEptPointCloudIndex;
59  QMutexLocker locker( &mHierarchyMutex );
60  copyCommonProperties( clone );
61  return std::unique_ptr<QgsPointCloudIndex>( clone );
62 }
63 
64 QList<IndexedPointCloudNode> QgsRemoteEptPointCloudIndex::nodeChildren( const IndexedPointCloudNode &n ) const
65 {
66  QList<IndexedPointCloudNode> lst;
67  if ( !loadNodeHierarchy( n ) )
68  return lst;
69 
70  const int d = n.d() + 1;
71  const int x = n.x() * 2;
72  const int y = n.y() * 2;
73  const int z = n.z() * 2;
74 
75  lst.reserve( 8 );
76  for ( int i = 0; i < 8; ++i )
77  {
78  int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
79  const IndexedPointCloudNode n2( d, x + dx, y + dy, z + dz );
80  if ( loadNodeHierarchy( n2 ) )
81  lst.append( n2 );
82  }
83  return lst;
84 }
85 
86 void QgsRemoteEptPointCloudIndex::load( const QString &url )
87 {
88  mUrl = QUrl( url );
89 
90  QStringList splitUrl = url.split( '/' );
91 
92  mUrlFileNamePart = splitUrl.back();
93  splitUrl.pop_back();
94  mUrlDirectoryPart = splitUrl.join( '/' );
95 
96  QNetworkRequest nr( url );
97 
99  const QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr );
100  if ( errCode != QgsBlockingNetworkRequest::NoError )
101  {
102  QgsDebugMsg( QStringLiteral( "Request failed: " ) + url );
103  mIsValid = false;
104  return;
105  }
106 
107  const QgsNetworkReplyContent reply = req.reply();
108  mIsValid = loadSchema( reply.content() );
109 }
110 
111 QgsPointCloudBlock *QgsRemoteEptPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
112 {
113  std::unique_ptr<QgsPointCloudBlockRequest> blockRequest( asyncNodeData( n, request ) );
114  if ( !blockRequest )
115  return nullptr;
116 
117  QEventLoop loop;
118  connect( blockRequest.get(), &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
119  loop.exec();
120 
121  if ( !blockRequest->block() )
122  {
123  QgsDebugMsg( QStringLiteral( "Error downloading node %1 data, error : %2 " ).arg( n.toString(), blockRequest->errorStr() ) );
124  }
125 
126  return blockRequest->block();
127 }
128 
129 QgsPointCloudBlockRequest *QgsRemoteEptPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
130 {
131  if ( !loadNodeHierarchy( n ) )
132  return nullptr;
133 
134  QString fileUrl;
135  if ( mDataType == QLatin1String( "binary" ) )
136  {
137  fileUrl = QStringLiteral( "%1/ept-data/%2.bin" ).arg( mUrlDirectoryPart, n.toString() );
138  }
139  else if ( mDataType == QLatin1String( "zstandard" ) )
140  {
141  fileUrl = QStringLiteral( "%1/ept-data/%2.zst" ).arg( mUrlDirectoryPart, n.toString() );
142  }
143  else if ( mDataType == QLatin1String( "laszip" ) )
144  {
145  fileUrl = QStringLiteral( "%1/ept-data/%2.laz" ).arg( mUrlDirectoryPart, n.toString() );
146  }
147  else
148  {
149  return nullptr;
150  }
151 
152  // we need to create a copy of the expression to pass to the decoder
153  // as the same QgsPointCloudExpression object might be concurrently
154  // used on another thread, for example in a 3d view
155  QgsPointCloudExpression filterExpression = mFilterExpression;
156  QgsPointCloudAttributeCollection requestAttributes = request.attributes();
157  requestAttributes.extend( attributes(), filterExpression.referencedAttributes() );
158  return new QgsEptPointCloudBlockRequest( n, fileUrl, mDataType, attributes(), requestAttributes, scale(), offset(), filterExpression );
159 }
160 
161 bool QgsRemoteEptPointCloudIndex::hasNode( const IndexedPointCloudNode &n ) const
162 {
163  return loadNodeHierarchy( n );
164 }
165 
166 bool QgsRemoteEptPointCloudIndex::loadNodeHierarchy( const IndexedPointCloudNode &nodeId ) const
167 {
168  mHierarchyMutex.lock();
169  bool found = mHierarchy.contains( nodeId );
170  mHierarchyMutex.unlock();
171  if ( found )
172  return true;
173 
174  QVector<IndexedPointCloudNode> nodePathToRoot;
175  {
176  IndexedPointCloudNode currentNode = nodeId;
177  do
178  {
179  nodePathToRoot.push_back( currentNode );
180  currentNode = currentNode.parentNode();
181  }
182  while ( currentNode.d() >= 0 );
183  }
184 
185  for ( int i = nodePathToRoot.size() - 1; i >= 0 && !mHierarchy.contains( nodeId ); --i )
186  {
187  const IndexedPointCloudNode node = nodePathToRoot[i];
189  mHierarchyMutex.lock();
190  const bool foundInHierarchy = mHierarchy.contains( node );
191  const bool foundInHierarchyNodes = mHierarchyNodes.contains( node );
192  mHierarchyMutex.unlock();
193  if ( foundInHierarchy )
194  continue;
195 
196  if ( !foundInHierarchyNodes )
197  continue;
198 
199  const QString fileUrl = QStringLiteral( "%1/ept-hierarchy/%2.json" ).arg( mUrlDirectoryPart, node.toString() );
200  QNetworkRequest nr( fileUrl );
201 
202  nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
203  nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
204 
206  const QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr );
207  if ( errCode != QgsBlockingNetworkRequest::NoError )
208  {
209  QgsDebugMsgLevel( QStringLiteral( "unable to read hierarchy from file %1" ).arg( fileUrl ), 2 );
210  return false;
211  }
212 
213  const QgsNetworkReplyContent reply = req.reply();
214 
215  const QByteArray dataJsonH = reply.content();
216  QJsonParseError errH;
217  const QJsonDocument docH = QJsonDocument::fromJson( dataJsonH, &errH );
218  if ( errH.error != QJsonParseError::NoError )
219  {
220  QgsDebugMsgLevel( QStringLiteral( "QJsonParseError when reading hierarchy from file %1" ).arg( fileUrl ), 2 );
221  return false;
222  }
223 
224  const QJsonObject rootHObj = docH.object();
225  for ( auto it = rootHObj.constBegin(); it != rootHObj.constEnd(); ++it )
226  {
227  const QString nodeIdStr = it.key();
228  const int nodePointCount = it.value().toInt();
229  const IndexedPointCloudNode nodeId = IndexedPointCloudNode::fromString( nodeIdStr );
230  mHierarchyMutex.lock();
231  if ( nodePointCount > 0 )
232  mHierarchy[nodeId] = nodePointCount;
233  else if ( nodePointCount == -1 )
234  mHierarchyNodes.insert( nodeId );
235  mHierarchyMutex.unlock();
236  }
237  }
238 
239  mHierarchyMutex.lock();
240  found = mHierarchy.contains( nodeId );
241  mHierarchyMutex.unlock();
242 
243  return found;
244 }
245 
246 bool QgsRemoteEptPointCloudIndex::isValid() const
247 {
248  return mIsValid;
249 }
250 
251 void QgsRemoteEptPointCloudIndex::copyCommonProperties( QgsRemoteEptPointCloudIndex *destination ) const
252 {
253  QgsEptPointCloudIndex::copyCommonProperties( destination );
254 
255  // QgsRemoteEptPointCloudIndex specific fields
256  destination->mUrlDirectoryPart = mUrlDirectoryPart;
257  destination->mUrlFileNamePart = mUrlFileNamePart;
258  destination->mUrl = mUrl;
259  destination->mHierarchyNodes = mHierarchyNodes;
260 }
261 
qgsremoteeptpointcloudindex.h
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
qgstiledownloadmanager.h
QgsEptPointCloudBlockRequest
Base class for handling loading QgsPointCloudBlock asynchronously from a remote EPT dataset.
Definition: qgseptpointcloudblockrequest.h:38
qgsblockingnetworkrequest.h
IndexedPointCloudNode::y
int y() const
Returns y.
Definition: qgspointcloudindex.cpp:73
QgsBlockingNetworkRequest::reply
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Definition: qgsblockingnetworkrequest.h:193
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
qgseptpointcloudblockrequest.h
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
QgsBlockingNetworkRequest::ErrorCode
ErrorCode
Error codes.
Definition: qgsblockingnetworkrequest.h:52
IndexedPointCloudNode
Represents a indexed point cloud node in octree.
Definition: qgspointcloudindex.h:57
QgsPointCloudAttributeCollection
Collection of point cloud attributes.
Definition: qgspointcloudattribute.h:141
QgsBlockingNetworkRequest::NoError
@ NoError
No error was encountered.
Definition: qgsblockingnetworkrequest.h:54
qgsfileutils.h
QgsPointCloudRequest::attributes
QgsPointCloudAttributeCollection attributes() const
Returns attributes.
Definition: qgspointcloudrequest.cpp:24
QgsBlockingNetworkRequest::get
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
Definition: qgsblockingnetworkrequest.cpp:58
QgsPointCloudBlockRequest
Base class for handling loading QgsPointCloudBlock asynchronously.
Definition: qgspointcloudblockrequest.h:36
IndexedPointCloudNode::fromString
static IndexedPointCloudNode fromString(const QString &str)
Creates node from string.
Definition: qgspointcloudindex.cpp:50
QgsPointCloudBlockRequest::finished
void finished()
Emitted when the request processing has finished.
QgsNetworkReplyContent::content
QByteArray content() const
Returns the reply content.
Definition: qgsnetworkreply.h:171
QgsNetworkReplyContent
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
Definition: qgsnetworkreply.h:28
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
qgsfeedback.h
qgscoordinatereferencesystem.h
QgsBlockingNetworkRequest
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
Definition: qgsblockingnetworkrequest.h:46
qgsmessagelog.h