Branch data Line data Source code
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 : : 18 : : #include "qgsremoteeptpointcloudindex.h" 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" 32 : : #include "qgscoordinatereferencesystem.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" 40 : : #include "qgsblockingnetworkrequest.h" 41 : : 42 : : #include "qgsfileutils.h" 43 : : #include "qgsapplication.h" 44 : : #include "qgspointcloudblockrequest.h" 45 : : 46 : : ///@cond PRIVATE 47 : : 48 : 0 : QgsRemoteEptPointCloudIndex::QgsRemoteEptPointCloudIndex() : QgsEptPointCloudIndex() 49 : 0 : { 50 : 0 : mHierarchyNodes.insert( IndexedPointCloudNode( 0, 0, 0, 0 ) ); 51 : 0 : } 52 : : 53 : 0 : QgsRemoteEptPointCloudIndex::~QgsRemoteEptPointCloudIndex() = default; 54 : : 55 : 0 : QList<IndexedPointCloudNode> QgsRemoteEptPointCloudIndex::nodeChildren( const IndexedPointCloudNode &n ) const 56 : : { 57 : 0 : QList<IndexedPointCloudNode> lst; 58 : 0 : if ( !loadNodeHierarchy( n ) ) 59 : 0 : return lst; 60 : : 61 : 0 : int d = n.d() + 1; 62 : 0 : int x = n.x() * 2; 63 : 0 : int y = n.y() * 2; 64 : 0 : int z = n.z() * 2; 65 : : 66 : 0 : for ( int i = 0; i < 8; ++i ) 67 : : { 68 : 0 : int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 ); 69 : 0 : IndexedPointCloudNode n2( d, x + dx, y + dy, z + dz ); 70 : 0 : if ( loadNodeHierarchy( n2 ) ) 71 : 0 : lst.append( n2 ); 72 : 0 : } 73 : 0 : return lst; 74 : 0 : } 75 : : 76 : 0 : void QgsRemoteEptPointCloudIndex::load( const QString &url ) 77 : : { 78 : 0 : mUrl = QUrl( url ); 79 : : 80 : 0 : QStringList splitUrl = url.split( '/' ); 81 : : 82 : 0 : mUrlFileNamePart = splitUrl.back(); 83 : 0 : splitUrl.pop_back(); 84 : 0 : mUrlDirectoryPart = splitUrl.join( '/' ); 85 : : 86 : 0 : QNetworkRequest nr( url ); 87 : : 88 : 0 : QgsBlockingNetworkRequest req; 89 : 0 : QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr ); 90 : 0 : if ( errCode != QgsBlockingNetworkRequest::NoError ) 91 : : { 92 : 0 : QgsDebugMsg( QStringLiteral( "Request failed: " ) + url ); 93 : 0 : mIsValid = false; 94 : 0 : return; 95 : : } 96 : : 97 : 0 : QgsNetworkReplyContent reply = req.reply(); 98 : 0 : mIsValid = loadSchema( reply.content() ); 99 : 0 : } 100 : : 101 : 0 : QgsPointCloudBlock *QgsRemoteEptPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request ) 102 : : { 103 : 0 : std::unique_ptr<QgsPointCloudBlockRequest> blockRequest( asyncNodeData( n, request ) ); 104 : 0 : if ( !blockRequest ) 105 : 0 : return nullptr; 106 : : 107 : 0 : QEventLoop loop; 108 : 0 : connect( blockRequest.get(), &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit ); 109 : 0 : loop.exec(); 110 : : 111 : 0 : if ( !blockRequest->block() ) 112 : : { 113 : 0 : QgsDebugMsg( QStringLiteral( "Error downloading node %1 data, error : %2 " ).arg( n.toString(), blockRequest->errorStr() ) ); 114 : 0 : } 115 : : 116 : 0 : return blockRequest->block(); 117 : 0 : } 118 : : 119 : 0 : QgsPointCloudBlockRequest *QgsRemoteEptPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request ) 120 : : { 121 : 0 : if ( !loadNodeHierarchy( n ) ) 122 : 0 : return nullptr; 123 : : 124 : 0 : QString fileUrl; 125 : 0 : if ( mDataType == QLatin1String( "binary" ) ) 126 : : { 127 : 0 : fileUrl = QStringLiteral( "%1/ept-data/%2.bin" ).arg( mUrlDirectoryPart, n.toString() ); 128 : 0 : } 129 : 0 : else if ( mDataType == QLatin1String( "zstandard" ) ) 130 : : { 131 : 0 : fileUrl = QStringLiteral( "%1/ept-data/%2.zst" ).arg( mUrlDirectoryPart, n.toString() ); 132 : 0 : } 133 : 0 : else if ( mDataType == QLatin1String( "laszip" ) ) 134 : : { 135 : 0 : fileUrl = QStringLiteral( "%1/ept-data/%2.laz" ).arg( mUrlDirectoryPart, n.toString() ); 136 : 0 : } 137 : : else 138 : : { 139 : 0 : return nullptr; 140 : : } 141 : : 142 : 0 : return new QgsPointCloudBlockRequest( n, fileUrl, mDataType, attributes(), request.attributes() ); 143 : 0 : } 144 : : 145 : 0 : bool QgsRemoteEptPointCloudIndex::hasNode( const IndexedPointCloudNode &n ) const 146 : : { 147 : 0 : return loadNodeHierarchy( n ); 148 : : } 149 : : 150 : 0 : bool QgsRemoteEptPointCloudIndex::loadNodeHierarchy( const IndexedPointCloudNode &nodeId ) const 151 : : { 152 : 0 : if ( mHierarchy.contains( nodeId ) ) 153 : 0 : return true; 154 : 0 : QVector<IndexedPointCloudNode> nodePathToRoot; 155 : : { 156 : 0 : IndexedPointCloudNode currentNode = nodeId; 157 : 0 : do 158 : : { 159 : 0 : nodePathToRoot.push_back( currentNode ); 160 : 0 : currentNode = currentNode.parentNode(); 161 : 0 : } 162 : 0 : while ( currentNode.d() >= 0 ); 163 : : } 164 : : 165 : 0 : for ( int i = nodePathToRoot.size() - 1; i >= 0 && !mHierarchy.contains( nodeId ); --i ) 166 : : { 167 : 0 : IndexedPointCloudNode node = nodePathToRoot[i]; 168 : : //! The hierarchy of the node is found => No need to load its file 169 : 0 : if ( mHierarchy.contains( node ) ) 170 : 0 : continue; 171 : : 172 : 0 : if ( !mHierarchyNodes.contains( node ) ) 173 : 0 : continue; 174 : : 175 : 0 : const QString fileUrl = QStringLiteral( "%1/ept-hierarchy/%2.json" ).arg( mUrlDirectoryPart, node.toString() ); 176 : 0 : QNetworkRequest nr( fileUrl ); 177 : : 178 : 0 : nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); 179 : 0 : nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); 180 : : 181 : 0 : QgsBlockingNetworkRequest req; 182 : 0 : QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr ); 183 : 0 : if ( errCode != QgsBlockingNetworkRequest::NoError ) 184 : : { 185 : 0 : QgsDebugMsgLevel( QStringLiteral( "unable to read hierarchy from file %1" ).arg( fileUrl ), 2 ); 186 : 0 : return false; 187 : : } 188 : : 189 : 0 : QgsNetworkReplyContent reply = req.reply(); 190 : : 191 : 0 : QByteArray dataJsonH = reply.content(); 192 : : QJsonParseError errH; 193 : 0 : const QJsonDocument docH = QJsonDocument::fromJson( dataJsonH, &errH ); 194 : 0 : if ( errH.error != QJsonParseError::NoError ) 195 : : { 196 : 0 : QgsDebugMsgLevel( QStringLiteral( "QJsonParseError when reading hierarchy from file %1" ).arg( fileUrl ), 2 ); 197 : 0 : return false; 198 : : } 199 : : 200 : 0 : const QJsonObject rootHObj = docH.object(); 201 : 0 : for ( auto it = rootHObj.constBegin(); it != rootHObj.constEnd(); ++it ) 202 : : { 203 : 0 : QString nodeIdStr = it.key(); 204 : 0 : int nodePointCount = it.value().toInt(); 205 : 0 : IndexedPointCloudNode nodeId = IndexedPointCloudNode::fromString( nodeIdStr ); 206 : 0 : if ( nodePointCount > 0 ) 207 : 0 : mHierarchy[nodeId] = nodePointCount; 208 : 0 : else if ( nodePointCount == -1 ) 209 : 0 : mHierarchyNodes.insert( nodeId ); 210 : 0 : } 211 : 0 : } 212 : : 213 : 0 : return mHierarchy.contains( nodeId ); 214 : 0 : } 215 : : 216 : 0 : bool QgsRemoteEptPointCloudIndex::isValid() const 217 : : { 218 : 0 : return mIsValid; 219 : : } 220 : : 221 : : ///@endcond