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