23#include "lazperf/readers.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 );
173 if ( mPointFormat == 6 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 9 || mPointFormat == 10 )
178 if ( mPointFormat != 0 && mPointFormat != 2 )
182 if ( mPointFormat == 2 || mPointFormat == 3 || mPointFormat == 5 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 10 )
188 if ( mPointFormat == 8 || mPointFormat == 10 )
195void QgsLazInfo::parseExtrabyteAttributes()
197 QByteArray ebVlrRaw =
vlrData(
"LASF_Spec", 4 );
208 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributes;
209 lazperf::eb_vlr ebVlr;
210 ebVlr.fill( rawData, length );
211 for ( std::vector<lazperf::eb_vlr::ebfield>::reverse_iterator it = ebVlr.items.rbegin(); it != ebVlr.items.rend(); ++it )
213 lazperf::eb_vlr::ebfield &
field = *it;
216 switch (
field.data_type )
267 int accOffset = ( extrabyteAttributes.empty() ?
pointRecordLength : extrabyteAttributes.back().offset ) - ebAtrr.
size;
268 ebAtrr.
offset = accOffset;
269 extrabyteAttributes.push_back( ebAtrr );
271 return extrabyteAttributes;
278 char headerRawData[ 375 ];
280 file.read( headerRawData, 375 );
284 std::unique_ptr<char[]> vlrEntriesRawData(
new char[ vlrDataSize ] );
286 file.read( vlrEntriesRawData.get(), vlrDataSize );
298 lazInfo.mError = QStringLiteral(
"The server of submitted URL doesn't support range queries" );
304 QNetworkRequest nr( url );
305 nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
306 nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
false );
307 nr.setRawHeader(
"Range",
"bytes=0-374" );
312 QgsDebugMsg( QStringLiteral(
"Request failed: " ) + url.toString() );
313 lazInfo.mError = QStringLiteral(
"Range query 0-374 to \"%1\" failed: \"%2\"" ).arg( url.toString() ).arg( req.
errorMessage() );
318 QByteArray lazHeaderData = reply.
content();
320 lazInfo.
parseRawHeader( lazHeaderData.data(), lazHeaderData.size() );
325 QNetworkRequest nr( url );
326 nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
327 nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
false );
329 QByteArray vlrRequestRange = QStringLiteral(
"bytes=%1-%2" ).arg( firstVlrOffset ).arg( lazInfo.
firstPointRecordOffset() - 1 ).toLocal8Bit();
330 nr.setRawHeader(
"Range", vlrRequestRange );
335 QgsDebugMsg( QStringLiteral(
"Request failed: " ) + url.toString() );
337 lazInfo.mError = QStringLiteral(
"Range query %1-%2 to \"%3\" failed: \"%4\"" ).arg( firstVlrOffset ).arg( lazInfo.
firstPointRecordOffset() - 1 )
351 QNetworkRequest nr( url );
352 nr.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork );
353 nr.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
false );
354 nr.setRawHeader(
"Range",
"bytes=0-0" );
360 const QString acceptRangesHeader = reply.
rawHeader( QStringLiteral(
"Accept-Ranges" ).toLocal8Bit() );
361 return acceptRangesHeader.compare( QStringLiteral(
"bytes" ), Qt::CaseSensitivity::CaseInsensitive ) == 0;
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.
ErrorCode head(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "head" operation on the specified request.
QString errorMessage() const
Returns the error message string, after a get(), post(), head() or put() request has been made.
@ 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.
Class for extracting information contained in LAZ file such as the public header block and variable l...
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.
static QgsLazInfo fromUrl(QUrl &url)
Static function to create a QgsLazInfo class from a file over network.
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.
static bool supportsRangeQueries(QUrl &url)
Static function to check whether the server of URL url supports range queries.
QgsLazInfo()
Constructor for an empty laz info parser.
int pointRecordLength() const
Returns the length of each point record in bytes.
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...
QByteArray content() const
Returns the reply content.
QByteArray rawHeader(const QByteArray &headerName) const
Returns the content of the header with the specified headerName, or an empty QByteArray if the specif...
void push_back(const QgsPointCloudAttribute &attribute)
Adds extra attribute.
Attribute for point cloud data pair of name and size in bytes.
@ 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.
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
#define QgsDebugMsgLevel(str, level)