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