20#include "lazperf/vlr.hpp"
31 if ( mVersion.first == 1 && mVersion.second == 4 )
33 if ( mVersion.first == 1 && mVersion.second == 3 )
35 if ( mVersion.first == 1 && mVersion.second <= 2 )
43 if ( std::string( data, 4 ) !=
"LASF" )
45 mError = QStringLiteral(
"Supplied header is not from a LAZ file" );
49 std::istringstream file( std::string( data, length ) );
50 lazperf::header14
header = lazperf::header14::create( file );
58 uint64_t currentOffset = 0;
59 for ( uint64_t i = 0; i < ( uint64_t )mVlrCount && currentOffset < length; ++i )
61 lazperf::vlr_header vlrHeader;
62 vlrHeader.fill( data + currentOffset, 54 );
65 vlr.
userId = QString::fromStdString( vlrHeader.user_id );
67 vlr.
data = QByteArray( data + currentOffset + 54, vlrHeader.data_length );
68 mVlrVector.push_back( vlr );
69 currentOffset += 54 + vlrHeader.data_length;
73 parseExtrabyteAttributes();
77void QgsLazInfo::parseHeader( lazperf::header14 &header )
83 mCreationYearDay = QPair<uint16_t, uint16_t>(
header.creation.year,
header.creation.day );
84 mVersion = QPair<uint8_t, uint8_t>(
header.version.major,
header.version.minor );
85 mPointFormat =
header.pointFormat();
87 mProjectId = QString( QByteArray(
header.guid, 16 ).toHex() );
88 mSystemId = QString::fromLocal8Bit(
header.system_identifier, 32 );
89 while ( !mSystemId.isEmpty() && mSystemId.back() ==
'\0' )
91 mSystemId.remove( mSystemId.size() - 1, 1 );
93 mSoftwareId = QString::fromLocal8Bit(
header.generating_software, 32 ).trimmed();
94 while ( !mSoftwareId.isEmpty() && mSoftwareId.back() ==
'\0' )
96 mSoftwareId.remove( mSoftwareId.size() - 1, 1 );
102 mVlrCount =
header.vlr_count;
104 parseLazAttributes();
108void QgsLazInfo::parseCrs()
111 for (
LazVlr &vlr : mVlrVector )
113 if ( vlr.userId.trimmed() == QLatin1String(
"LASF_Projection" ) && vlr.recordId == 2112 )
123 QVariantMap metadata;
124 metadata[ QStringLiteral(
"creation_year" ) ] = mHeader.creation.year;
125 metadata[ QStringLiteral(
"creation_day" ) ] = mHeader.creation.day;
126 metadata[ QStringLiteral(
"major_version" ) ] = mHeader.version.major;
127 metadata[ QStringLiteral(
"minor_version" ) ] = mHeader.version.minor;
128 metadata[ QStringLiteral(
"dataformat_id" ) ] = mHeader.pointFormat();
129 metadata[ QStringLiteral(
"scale_x" ) ] = mScale.x();
130 metadata[ QStringLiteral(
"scale_y" ) ] = mScale.y();
131 metadata[ QStringLiteral(
"scale_z" ) ] = mScale.z();
132 metadata[ QStringLiteral(
"offset_x" ) ] = mOffset.x();
133 metadata[ QStringLiteral(
"offset_y" ) ] = mOffset.y();
134 metadata[ QStringLiteral(
"offset_z" ) ] = mOffset.z();
135 metadata[ QStringLiteral(
"project_id" ) ] = QString( QByteArray( mHeader.guid, 16 ).toHex() );
136 metadata[ QStringLiteral(
"system_id" ) ] = QString::fromLocal8Bit( mHeader.system_identifier, 32 );
137 metadata[ QStringLiteral(
"software_id" ) ] = QString::fromLocal8Bit( mHeader.generating_software, 32 );
143 for (
LazVlr vlr : mVlrVector )
145 if ( vlr.userId == userId && vlr.recordId == recordId )
153void QgsLazInfo::parseLazAttributes()
155 if ( mPointFormat < 0 || mPointFormat > 10 )
157 QgsDebugMsgLevel( QStringLiteral(
"Invalid point record format %1" ).arg( mPointFormat ), 2 );
177 if ( mPointFormat == 6 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 9 || mPointFormat == 10 )
181 if ( mPointFormat != 0 && mPointFormat != 2 )
185 if ( mPointFormat == 2 || mPointFormat == 3 || mPointFormat == 5 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 10 )
191 if ( mPointFormat == 8 || mPointFormat == 10 )
198void QgsLazInfo::parseExtrabyteAttributes()
200 QByteArray ebVlrRaw =
vlrData(
"LASF_Spec", 4 );
203 for ( QgsLazInfo::ExtraBytesAttributeDetails attr : mExtrabyteAttributes )
205 mAttributes.push_back( QgsPointCloudAttribute( attr.attribute, attr.type ) );
211 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributes;
212 lazperf::eb_vlr ebVlr;
213 ebVlr.fill( rawData, length );
214 for ( std::vector<lazperf::eb_vlr::ebfield>::reverse_iterator it = ebVlr.items.rbegin(); it != ebVlr.items.rend(); ++it )
216 lazperf::eb_vlr::ebfield &field = *it;
218 ebAtrr.
attribute = QString::fromStdString( field.name );
219 switch ( field.data_type )
223 ebAtrr.
size = field.options;
267 ebAtrr.
size = field.options;
270 int accOffset = ( extrabyteAttributes.empty() ?
pointRecordLength : extrabyteAttributes.back().offset ) - ebAtrr.
size;
271 ebAtrr.
offset = accOffset;
272 extrabyteAttributes.push_back( ebAtrr );
274 return extrabyteAttributes;
281 char headerRawData[ 375 ];
283 file.read( headerRawData, 375 );
287 std::unique_ptr<char[]> vlrEntriesRawData(
new char[ vlrDataSize ] );
289 file.read( vlrEntriesRawData.get(), vlrDataSize );
301 QNetworkRequest nr( url );
303 nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
304 nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
false );
305 nr.setRawHeader(
"Range",
"bytes=0-374" );
311 QgsDebugError( QStringLiteral(
"Request failed: " ) + url.toString() );
313 if ( req.
reply().
attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() == 200 )
319 lazInfo.mError = QStringLiteral(
"Range query 0-374 to \"%1\" failed: \"%2\"" ).arg( url.toString() ).arg( req.
errorMessage() );
326 QByteArray lazHeaderData = reply.
content();
328 lazInfo.
parseRawHeader( lazHeaderData.data(), lazHeaderData.size() );
331 const QUrl requestedUrl = reply.
request().url();
332 if ( requestedUrl != url && authcfg.isEmpty() )
334 url.setUrl( requestedUrl.toString() );
340 QNetworkRequest nr( url );
342 nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
343 nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
false );
345 QByteArray vlrRequestRange = QStringLiteral(
"bytes=%1-%2" ).arg( firstVlrOffset ).arg( lazInfo.
firstPointRecordOffset() - 1 ).toLocal8Bit();
346 nr.setRawHeader(
"Range", vlrRequestRange );
352 QgsDebugError( QStringLiteral(
"Request failed: " ) + url.toString() );
354 lazInfo.mError = QStringLiteral(
"Range query %1-%2 to \"%3\" failed: \"%4\"" ).arg( firstVlrOffset ).arg( lazInfo.
firstPointRecordOffset() - 1 )
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
QString errorMessage() const
Returns the error message string, after a get(), post(), head() or put() request has been made.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
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...
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
uint32_t firstVariableLengthRecord() const
Returns the absolute offset to the first variable length record in the LAZ file.
QByteArray vlrData(QString userId, int recordId)
Returns the binary data of the variable length record with the user identifier userId and record iden...
uint32_t firstPointRecordOffset() const
Returns the absolute offset to the first point record in the LAZ file.
lazperf::header14 header() const
Returns the LAZPERF header object.
void parseRawVlrEntries(char *data, uint64_t length)
Parses the variable length records found in the array data of length length.
QVariantMap toMetadata() const
Returns a map containing various metadata extracted from the LAZ file.
void parseRawHeader(char *data, uint64_t length)
Parses the raw header data loaded from a LAZ file.
static QgsLazInfo fromFile(std::ifstream &file)
Static function to create a QgsLazInfo class from a file.
QgsLazInfo()
Constructor for an empty laz info parser.
int pointRecordLength() const
Returns the length of each point record in bytes.
static QgsLazInfo fromUrl(QUrl &url, const QString &authcfg=QString())
Static function to create a QgsLazInfo class from a file over network.
static QVector< ExtraBytesAttributeDetails > parseExtrabytes(char *rawData, int length, int pointRecordLength)
Static function to parse the raw extrabytes VLR into a list of recognizable extrabyte attributes.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QVariant attribute(QNetworkRequest::Attribute code) const
Returns the attribute associated with the code.
QByteArray content() const
Returns the reply content.
QNetworkRequest request() const
Returns the original network request.
@ UShort
Unsigned short int 2 bytes.
@ Short
Short int 2 bytes.
@ UChar
Unsigned char 1 byte.
@ UInt32
Unsigned int32 4 bytes.
@ UInt64
Unsigned int64 8 bytes.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
#define QgsSetRequestInitiatorClass(request, _class)