QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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"
38#include "qgspointcloudexpression.h"
41
43
44QgsRemoteCopcPointCloudIndex::QgsRemoteCopcPointCloudIndex() = default;
45
46QgsRemoteCopcPointCloudIndex::~QgsRemoteCopcPointCloudIndex() = default;
47
48std::unique_ptr<QgsPointCloudIndex> QgsRemoteCopcPointCloudIndex::clone() const
49{
50 QgsRemoteCopcPointCloudIndex *clone = new QgsRemoteCopcPointCloudIndex;
51 QMutexLocker locker( &mHierarchyMutex );
52 copyCommonProperties( clone );
53 return std::unique_ptr<QgsPointCloudIndex>( clone );
54}
55
56void QgsRemoteCopcPointCloudIndex::load( const QString &uri )
57{
58 mUri = uri;
59 QUrl url( uri );
60 mLazInfo.reset( new QgsLazInfo( QgsLazInfo::fromUrl( url ) ) );
61 mIsValid = mLazInfo->isValid();
62 if ( mIsValid )
63 {
64 mIsValid = loadSchema( *mLazInfo.get() );
65 if ( mIsValid )
66 {
67 loadHierarchy();
68 }
69 }
70 if ( !mIsValid )
71 {
72 mError = tr( "Unable to recognize %1 as a LAZ file: \"%2\"" ).arg( uri, mLazInfo->error() );
73 }
74}
75
76std::unique_ptr<QgsPointCloudBlock> QgsRemoteCopcPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
77{
78 if ( QgsPointCloudBlock *cached = getNodeDataFromCache( n, request ) )
79 {
80 return std::unique_ptr<QgsPointCloudBlock>( cached );
81 }
82
83 std::unique_ptr<QgsPointCloudBlockRequest> blockRequest( asyncNodeData( n, request ) );
84 if ( !blockRequest )
85 return nullptr;
86
87 QEventLoop loop;
88 connect( blockRequest.get(), &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
89 loop.exec();
90
91 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
92
93 if ( !block )
94 {
95 QgsDebugError( QStringLiteral( "Error downloading node %1 data, error : %2 " ).arg( n.toString(), blockRequest->errorStr() ) );
96 }
97
98 storeNodeDataToCache( block.get(), n, request );
99 return block;
100}
101
102QgsPointCloudBlockRequest *QgsRemoteCopcPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
103{
104 if ( QgsPointCloudBlock *cached = getNodeDataFromCache( n, request ) )
105 {
106 return new QgsCachedPointCloudBlockRequest( cached, n, mUri, attributes(), request.attributes(),
107 scale(), offset(), mFilterExpression, request.filterRect() );
108 }
109
110 if ( !fetchNodeHierarchy( n ) )
111 return nullptr;
112 QMutexLocker locker( &mHierarchyMutex );
113
114 // we need to create a copy of the expression to pass to the decoder
115 // as the same QgsPointCloudExpression object might be concurrently
116 // used on another thread, for example in a 3d view
117 QgsPointCloudExpression filterExpression = mFilterExpression;
118 QgsPointCloudAttributeCollection requestAttributes = request.attributes();
119 requestAttributes.extend( attributes(), filterExpression.referencedAttributes() );
120 auto [ blockOffset, blockSize ] = mHierarchyNodePos.value( n );
121 int pointCount = mHierarchy.value( n );
122
123 return new QgsCopcPointCloudBlockRequest( n, mUri, attributes(), requestAttributes,
124 scale(), offset(), filterExpression, request.filterRect(),
125 blockOffset, blockSize, pointCount, *mLazInfo.get() );
126}
127
128bool QgsRemoteCopcPointCloudIndex::isValid() const
129{
130 return mIsValid;
131}
132
133void QgsRemoteCopcPointCloudIndex::fetchHierarchyPage( uint64_t offset, uint64_t byteSize ) const
134{
135 Q_ASSERT( byteSize > 0 );
136
137 QNetworkRequest nr = QNetworkRequest( QUrl( mUri ) );
138 QgsSetRequestInitiatorClass( nr, QStringLiteral( "QgsRemoteCopcPointCloudIndex" ) );
139 nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
140 nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
141 QByteArray queryRange = QStringLiteral( "bytes=%1-%2" ).arg( offset ).arg( offset + byteSize - 1 ).toLocal8Bit();
142 nr.setRawHeader( "Range", queryRange );
143
144 std::unique_ptr<QgsTileDownloadManagerReply> reply( QgsApplication::tileDownloadManager()->get( nr ) );
145
146 QEventLoop loop;
147 connect( reply.get(), &QgsTileDownloadManagerReply::finished, &loop, &QEventLoop::quit );
148 loop.exec();
149
150 if ( reply->error() != QNetworkReply::NoError )
151 {
152 QgsDebugError( QStringLiteral( "Request failed: " ) + mUri );
153 return;
154 }
155
156 QByteArray data = reply->data();
157
158 populateHierarchy( data.constData(), byteSize );
159}
160
161void QgsRemoteCopcPointCloudIndex::copyCommonProperties( QgsRemoteCopcPointCloudIndex *destination ) const
162{
163 QgsCopcPointCloudIndex::copyCommonProperties( destination );
164
165 // QgsRemoteCopcPointCloudIndex specific fields
166 destination->mHierarchyNodes = mHierarchyNodes;
167}
168
Represents a indexed point cloud node in octree.
QString toString() const
Encode node to string.
static QgsTileDownloadManager * tileDownloadManager()
Returns the application's tile download manager, used for download of map tiles when rendering.
Class for handling a QgsPointCloudBlockRequest using existing cached QgsPointCloudBlock.
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.
Definition: qgslazinfo.cpp:297
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.
Base class for storing raw data from point cloud nodes.
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)