22#include <lazperf/readers.hpp>
23#include <lazperf/writers.hpp>
26static void updatePoint(
char *pointBuffer,
int pointFormat,
const QString &attributeName,
double newValue )
28 if ( attributeName == QLatin1String(
"Intensity" ) )
30 quint16 newValueShort =
static_cast<quint16
>( newValue );
31 memcpy( pointBuffer + 12, &newValueShort,
sizeof( qint16 ) );
33 else if ( attributeName == QLatin1String(
"ReturnNumber" ) )
35 uchar newByteValue =
static_cast<uchar
>( newValue ) & 0xf;
36 pointBuffer[14] =
static_cast<char>( ( pointBuffer[14] & 0xf0 ) | newByteValue );
38 else if ( attributeName == QLatin1String(
"NumberOfReturns" ) )
40 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0xf ) << 4;
41 pointBuffer[14] =
static_cast<char>( ( pointBuffer[14] & 0xf ) | newByteValue );
43 else if ( attributeName == QLatin1String(
"Synthetic" ) )
45 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 );
46 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfe ) | newByteValue );
48 else if ( attributeName == QLatin1String(
"KeyPoint" ) )
50 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 1;
51 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfd ) | newByteValue );
53 else if ( attributeName == QLatin1String(
"Withheld" ) )
55 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 2;
56 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfb ) | newByteValue );
58 else if ( attributeName == QLatin1String(
"Overlap" ) )
60 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 3;
61 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xf7 ) | newByteValue );
63 else if ( attributeName == QLatin1String(
"ScannerChannel" ) )
65 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x3 ) << 4;
66 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xcf ) | newByteValue );
68 else if ( attributeName == QLatin1String(
"ScanDirectionFlag" ) )
70 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 6;
71 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xbf ) | newByteValue );
73 else if ( attributeName == QLatin1String(
"EdgeOfFlightLine" ) )
75 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 7;
76 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0x7f ) | newByteValue );
78 else if ( attributeName == QLatin1String(
"Classification" ) )
80 pointBuffer[16] =
static_cast<char>(
static_cast<uchar
>( newValue ) );
82 else if ( attributeName == QLatin1String(
"UserData" ) )
84 pointBuffer[17] =
static_cast<char>(
static_cast<uchar
>( newValue ) );
86 else if ( attributeName == QLatin1String(
"ScanAngleRank" ) )
88 qint16 newValueShort =
static_cast<qint16
>( std::round( newValue / 0.006 ) );
89 memcpy( pointBuffer + 18, &newValueShort,
sizeof( qint16 ) );
91 else if ( attributeName == QLatin1String(
"PointSourceId" ) )
93 quint16 newValueShort =
static_cast<quint16
>( newValue );
94 memcpy( pointBuffer + 20, &newValueShort,
sizeof( quint16 ) );
96 else if ( attributeName == QLatin1String(
"GpsTime" ) )
98 memcpy( pointBuffer + 22, &newValue,
sizeof(
double ) );
100 else if ( pointFormat == 7 || pointFormat == 8 )
102 if ( attributeName == QLatin1String(
"Red" ) )
104 quint16 newValueShort =
static_cast<quint16
>( newValue );
105 memcpy( pointBuffer + 30, &newValueShort,
sizeof( quint16 ) );
107 else if ( attributeName == QLatin1String(
"Green" ) )
109 quint16 newValueShort =
static_cast<quint16
>( newValue );
110 memcpy( pointBuffer + 32, &newValueShort,
sizeof( quint16 ) );
112 else if ( attributeName == QLatin1String(
"Blue" ) )
114 quint16 newValueShort =
static_cast<quint16
>( newValue );
115 memcpy( pointBuffer + 34, &newValueShort,
sizeof( quint16 ) );
117 else if ( pointFormat == 8 )
119 if ( attributeName == QLatin1String(
"Infrared" ) )
121 quint16 newValueShort =
static_cast<quint16
>( newValue );
122 memcpy( pointBuffer + 36, &newValueShort,
sizeof( quint16 ) );
131 QgsEventTracing::ScopedEvent _trace( QStringLiteral(
"PointCloud" ), QStringLiteral(
"QgsPointCloudLayerEditUtils::updateChunkValues" ) );
136 QMutexLocker locker( &copcIndex->mHierarchyMutex );
138 Q_ASSERT( copcIndex->mHierarchy.contains( n ) );
139 Q_ASSERT( copcIndex->mHierarchyNodePos.contains( n ) );
141 pointCount = copcIndex->mHierarchy[n];
144 lazperf::header14 header = copcIndex->mLazInfo->header();
146 lazperf::reader::chunk_decompressor decompressor( header.pointFormat(), header.ebCount(), chunkData.constData() );
147 lazperf::writer::chunk_compressor compressor( header.pointFormat(), header.ebCount() );
149 std::unique_ptr<char[]> decodedData(
new char[header.point_record_length] );
152 Q_ASSERT( header.pointFormat() == 6 || header.pointFormat() == 7 || header.pointFormat() == 8 );
154 QString attributeName = attribute.
name();
156 for (
int i = 0; i < pointCount; ++i )
158 decompressor.decompress( decodedData.get() );
159 char *buf = decodedData.get();
161 if ( pointValues.contains( i ) )
164 updatePoint( buf, header.point_format_id, attributeName, newValue ? *newValue : pointValues[i] );
167 compressor.compress( decodedData.get() );
170 std::vector<unsigned char> data = compressor.done();
171 return QByteArray( (
const char * ) data.data(), (
int ) data.size() );
176 const QString name = attribute.
name().toUpper();
178 if ( name == QLatin1String(
"INTENSITY" ) )
179 return value >= 0 && value <= 65535;
180 if ( name == QLatin1String(
"RETURNNUMBER" ) )
181 return value >= 0 && value <= 15;
182 if ( name == QLatin1String(
"NUMBEROFRETURNS" ) )
183 return value >= 0 && value <= 15;
184 if ( name == QLatin1String(
"SCANNERCHANNEL" ) )
185 return value >= 0 && value <= 3;
186 if ( name == QLatin1String(
"SCANDIRECTIONFLAG" ) )
187 return value >= 0 && value <= 1;
188 if ( name == QLatin1String(
"EDGEOFFLIGHTLINE" ) )
189 return value >= 0 && value <= 1;
190 if ( name == QLatin1String(
"CLASSIFICATION" ) )
191 return value >= 0 && value <= 255;
192 if ( name == QLatin1String(
"USERDATA" ) )
193 return value >= 0 && value <= 255;
194 if ( name == QLatin1String(
"SCANANGLERANK" ) )
195 return value >= -180 && value <= 180;
196 if ( name == QLatin1String(
"POINTSOURCEID" ) )
197 return value >= 0 && value <= 65535;
198 if ( name == QLatin1String(
"GPSTIME" ) )
200 if ( name == QLatin1String(
"SYNTHETIC" ) )
201 return value >= 0 && value <= 1;
202 if ( name == QLatin1String(
"KEYPOINT" ) )
203 return value >= 0 && value <= 1;
204 if ( name == QLatin1String(
"WITHHELD" ) )
205 return value >= 0 && value <= 1;
206 if ( name == QLatin1String(
"OVERLAP" ) )
207 return value >= 0 && value <= 1;
208 if ( name == QLatin1String(
"RED" ) )
209 return value >= 0 && value <= 65535;
210 if ( name == QLatin1String(
"GREEN" ) )
211 return value >= 0 && value <= 65535;
212 if ( name == QLatin1String(
"BLUE" ) )
213 return value >= 0 && value <= 65535;
214 if ( name == QLatin1String(
"INFRARED" ) )
215 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.