18#include "lazperf/readers.hpp"
19#include "lazperf/writers.hpp"
27using namespace Qt::StringLiterals;
29static void updatePoint(
char *pointBuffer,
int pointFormat,
const QString &attributeName,
double newValue )
31 if ( attributeName ==
"Intensity"_L1 )
33 quint16 newValueShort =
static_cast<quint16
>( newValue );
34 memcpy( pointBuffer + 12, &newValueShort,
sizeof( qint16 ) );
36 else if ( attributeName ==
"ReturnNumber"_L1 )
38 uchar newByteValue =
static_cast<uchar
>( newValue ) & 0xf;
39 pointBuffer[14] =
static_cast<char>( ( pointBuffer[14] & 0xf0 ) | newByteValue );
41 else if ( attributeName ==
"NumberOfReturns"_L1 )
43 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0xf ) << 4;
44 pointBuffer[14] =
static_cast<char>( ( pointBuffer[14] & 0xf ) | newByteValue );
46 else if ( attributeName ==
"Synthetic"_L1 )
48 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 );
49 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfe ) | newByteValue );
51 else if ( attributeName ==
"KeyPoint"_L1 )
53 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 1;
54 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfd ) | newByteValue );
56 else if ( attributeName ==
"Withheld"_L1 )
58 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 2;
59 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfb ) | newByteValue );
61 else if ( attributeName ==
"Overlap"_L1 )
63 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 3;
64 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xf7 ) | newByteValue );
66 else if ( attributeName ==
"ScannerChannel"_L1 )
68 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x3 ) << 4;
69 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xcf ) | newByteValue );
71 else if ( attributeName ==
"ScanDirectionFlag"_L1 )
73 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 6;
74 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xbf ) | newByteValue );
76 else if ( attributeName ==
"EdgeOfFlightLine"_L1 )
78 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 7;
79 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0x7f ) | newByteValue );
81 else if ( attributeName ==
"Classification"_L1 )
83 pointBuffer[16] =
static_cast<char>(
static_cast<uchar
>( newValue ) );
85 else if ( attributeName ==
"UserData"_L1 )
87 pointBuffer[17] =
static_cast<char>(
static_cast<uchar
>( newValue ) );
89 else if ( attributeName ==
"ScanAngleRank"_L1 )
91 qint16 newValueShort =
static_cast<qint16
>( std::round( newValue / 0.006 ) );
92 memcpy( pointBuffer + 18, &newValueShort,
sizeof( qint16 ) );
94 else if ( attributeName ==
"PointSourceId"_L1 )
96 quint16 newValueShort =
static_cast<quint16
>( newValue );
97 memcpy( pointBuffer + 20, &newValueShort,
sizeof( quint16 ) );
99 else if ( attributeName ==
"GpsTime"_L1 )
101 memcpy( pointBuffer + 22, &newValue,
sizeof(
double ) );
103 else if ( pointFormat == 7 || pointFormat == 8 )
105 if ( attributeName ==
"Red"_L1 )
107 quint16 newValueShort =
static_cast<quint16
>( newValue );
108 memcpy( pointBuffer + 30, &newValueShort,
sizeof( quint16 ) );
110 else if ( attributeName ==
"Green"_L1 )
112 quint16 newValueShort =
static_cast<quint16
>( newValue );
113 memcpy( pointBuffer + 32, &newValueShort,
sizeof( quint16 ) );
115 else if ( attributeName ==
"Blue"_L1 )
117 quint16 newValueShort =
static_cast<quint16
>( newValue );
118 memcpy( pointBuffer + 34, &newValueShort,
sizeof( quint16 ) );
120 else if ( pointFormat == 8 )
122 if ( attributeName ==
"Infrared"_L1 )
124 quint16 newValueShort =
static_cast<quint16
>( newValue );
125 memcpy( pointBuffer + 36, &newValueShort,
sizeof( quint16 ) );
134 QgsEventTracing::ScopedEvent _trace( u
"PointCloud"_s, u
"QgsPointCloudLayerEditUtils::updateChunkValues"_s );
139 QMutexLocker locker( &copcIndex->mHierarchyMutex );
141 Q_ASSERT( copcIndex->mHierarchy.contains( n ) );
142 Q_ASSERT( copcIndex->mHierarchyNodePos.contains( n ) );
144 pointCount = copcIndex->mHierarchy[n];
147 lazperf::header14 header = copcIndex->mLazInfo->header();
149 lazperf::reader::chunk_decompressor decompressor( header.pointFormat(), header.ebCount(), chunkData.constData() );
150 lazperf::writer::chunk_compressor compressor( header.pointFormat(), header.ebCount() );
152 std::unique_ptr<char[]> decodedData(
new char[header.point_record_length] );
155 Q_ASSERT( header.pointFormat() == 6 || header.pointFormat() == 7 || header.pointFormat() == 8 );
157 QString attributeName = attribute.
name();
159 for (
int i = 0; i < pointCount; ++i )
161 decompressor.decompress( decodedData.get() );
162 char *buf = decodedData.get();
164 if ( pointValues.contains( i ) )
167 updatePoint( buf, header.point_format_id, attributeName, newValue ? *newValue : pointValues[i] );
170 compressor.compress( decodedData.get() );
173 std::vector<unsigned char> data = compressor.done();
174 return QByteArray( (
const char * ) data.data(), (
int ) data.size() );
179 const QString name = attribute.
name().toUpper();
181 if ( name ==
"INTENSITY"_L1 )
182 return value >= 0 && value <= 65535;
183 if ( name ==
"RETURNNUMBER"_L1 )
184 return value >= 0 && value <= 15;
185 if ( name ==
"NUMBEROFRETURNS"_L1 )
186 return value >= 0 && value <= 15;
187 if ( name ==
"SCANNERCHANNEL"_L1 )
188 return value >= 0 && value <= 3;
189 if ( name ==
"SCANDIRECTIONFLAG"_L1 )
190 return value >= 0 && value <= 1;
191 if ( name ==
"EDGEOFFLIGHTLINE"_L1 )
192 return value >= 0 && value <= 1;
193 if ( name ==
"CLASSIFICATION"_L1 )
194 return value >= 0 && value <= 255;
195 if ( name ==
"USERDATA"_L1 )
196 return value >= 0 && value <= 255;
197 if ( name ==
"SCANANGLERANK"_L1 )
198 return value >= -180 && value <= 180;
199 if ( name ==
"POINTSOURCEID"_L1 )
200 return value >= 0 && value <= 65535;
201 if ( name ==
"GPSTIME"_L1 )
203 if ( name ==
"SYNTHETIC"_L1 )
204 return value >= 0 && value <= 1;
205 if ( name ==
"KEYPOINT"_L1 )
206 return value >= 0 && value <= 1;
207 if ( name ==
"WITHHELD"_L1 )
208 return value >= 0 && value <= 1;
209 if ( name ==
"OVERLAP"_L1 )
210 return value >= 0 && value <= 1;
211 if ( name ==
"RED"_L1 )
212 return value >= 0 && value <= 65535;
213 if ( name ==
"GREEN"_L1 )
214 return value >= 0 && value <= 65535;
215 if ( name ==
"BLUE"_L1 )
216 return value >= 0 && value <= 65535;
217 if ( name ==
"INFRARED"_L1 )
218 return value >= 0 && value <= 65535;
Attribute for point cloud data pair of name and size in bytes.
QString name() const
Returns name of the attribute.
static QByteArray updateChunkValues(QgsCopcPointCloudIndex *copcIndex, const QByteArray &chunkData, const QgsPointCloudAttribute &attribute, const QgsPointCloudNodeId &n, const QHash< int, double > &pointValues, std::optional< double > newValue=std::nullopt)
Sets new classification value for the given points in voxel and return updated chunk data.
static bool isAttributeValueValid(const QgsPointCloudAttribute &attribute, double value)
Check if value is within proper range for the attribute.
Represents an indexed point cloud node's position in octree.