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();
77 void 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();
108 void 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 )
153 void 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 )
195 void 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" );
358 const QList<QgsNetworkReplyContent::RawHeaderPair> pairs = reply.
rawHeaderPairs();
359 bool acceptsRanges =
false;
360 for (
const auto &pair : pairs )
362 if ( QString::fromLocal8Bit( pair.first ).compare( QStringLiteral(
"Accept-Ranges" ), Qt::CaseInsensitive ) == 0 &&
363 QString::fromLocal8Bit( pair.second ).compare( QStringLiteral(
"bytes" ), Qt::CaseInsensitive ) == 0 )
365 acceptsRanges =
true;