22#include <lazperf/readers.hpp>
23#include <lazperf/writers.hpp>
27 : mIndex( layer->index() )
34 if ( attribute.
name().compare( QLatin1String(
"X" ), Qt::CaseInsensitive ) == 0 ||
35 attribute.
name().compare( QLatin1String(
"Y" ), Qt::CaseInsensitive ) == 0 ||
36 attribute.
name().compare( QLatin1String(
"Z" ), Qt::CaseInsensitive ) == 0 )
59 const QSet<int> uniquePoints( pts.constBegin(), pts.constEnd() );
60 QVector<int> sortedPoints( uniquePoints.constBegin(), uniquePoints.constEnd() );
61 std::sort( sortedPoints.begin(), sortedPoints.end() );
63 if ( sortedPoints.constFirst() < 0 ||
68 QgsCopcPointCloudIndex *copcIndex =
static_cast<QgsCopcPointCloudIndex *
>( editIndex->mIndex.
get() );
71 if ( editIndex->mEditedNodeData.contains( n ) )
73 chunkData = editIndex->mEditedNodeData[n];
77 QPair<uint64_t, int32_t> offsetSizePair = copcIndex->mHierarchyNodePos[n];
78 chunkData = copcIndex->readRange( offsetSizePair.first, offsetSizePair.second );
81 QByteArray data = updateChunkValues( copcIndex, chunkData, *at, value, n, pts );
87static void updatePoint(
char *pointBuffer,
int pointFormat,
const QString &attributeName,
double newValue )
89 if ( attributeName == QLatin1String(
"Intensity" ) )
91 quint16 newValueShort =
static_cast<quint16
>( newValue );
92 memcpy( pointBuffer + 12, &newValueShort,
sizeof( qint16 ) );
94 else if ( attributeName == QLatin1String(
"ReturnNumber" ) )
96 uchar newByteValue =
static_cast<uchar
>( newValue ) & 0xf;
97 pointBuffer[14] =
static_cast<char>( ( pointBuffer[14] & 0xf0 ) | newByteValue );
99 else if ( attributeName == QLatin1String(
"NumberOfReturns" ) )
101 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0xf ) << 4;
102 pointBuffer[14] =
static_cast<char>( ( pointBuffer[14] & 0xf ) | newByteValue );
104 else if ( attributeName == QLatin1String(
"Synthetic" ) )
106 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 );
107 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfe ) | newByteValue );
109 else if ( attributeName == QLatin1String(
"KeyPoint" ) )
111 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 1;
112 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfd ) | newByteValue );
114 else if ( attributeName == QLatin1String(
"Withheld" ) )
116 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 2;
117 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xfb ) | newByteValue );
119 else if ( attributeName == QLatin1String(
"Overlap" ) )
121 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 3;
122 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xf7 ) | newByteValue );
124 else if ( attributeName == QLatin1String(
"ScannerChannel" ) )
126 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x3 ) << 4;
127 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xcf ) | newByteValue );
129 else if ( attributeName == QLatin1String(
"ScanDirectionFlag" ) )
131 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 6;
132 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0xbf ) | newByteValue );
134 else if ( attributeName == QLatin1String(
"EdgeOfFlightLine" ) )
136 uchar newByteValue = (
static_cast<uchar
>( newValue ) & 0x1 ) << 7;
137 pointBuffer[15] =
static_cast<char>( ( pointBuffer[15] & 0x7f ) | newByteValue );
139 else if ( attributeName == QLatin1String(
"Classification" ) )
141 pointBuffer[16] =
static_cast<char>(
static_cast<uchar
>( newValue ) );
143 else if ( attributeName == QLatin1String(
"UserData" ) )
145 pointBuffer[17] =
static_cast<char>(
static_cast<uchar
>( newValue ) );
147 else if ( attributeName == QLatin1String(
"ScanAngleRank" ) )
149 qint16 newValueShort =
static_cast<qint16
>( newValue );
150 memcpy( pointBuffer + 18, &newValueShort,
sizeof( qint16 ) );
152 else if ( attributeName == QLatin1String(
"PointSourceId" ) )
154 quint16 newValueShort =
static_cast<quint16
>( newValue );
155 memcpy( pointBuffer + 20, &newValueShort,
sizeof( quint16 ) );
157 else if ( attributeName == QLatin1String(
"GpsTime" ) )
159 memcpy( pointBuffer + 22, &newValue,
sizeof(
double ) );
161 else if ( pointFormat == 7 || pointFormat == 8 )
163 if ( attributeName == QLatin1String(
"Red" ) )
165 quint16 newValueShort =
static_cast<quint16
>( newValue );
166 memcpy( pointBuffer + 30, &newValueShort,
sizeof( quint16 ) );
168 else if ( attributeName == QLatin1String(
"Green" ) )
170 quint16 newValueShort =
static_cast<quint16
>( newValue );
171 memcpy( pointBuffer + 32, &newValueShort,
sizeof( quint16 ) );
173 else if ( attributeName == QLatin1String(
"Blue" ) )
175 quint16 newValueShort =
static_cast<quint16
>( newValue );
176 memcpy( pointBuffer + 34, &newValueShort,
sizeof( quint16 ) );
178 else if ( pointFormat == 8 )
180 if ( attributeName == QLatin1String(
"Infrared" ) )
182 quint16 newValueShort =
static_cast<quint16
>( newValue );
183 memcpy( pointBuffer + 36, &newValueShort,
sizeof( quint16 ) );
190QByteArray QgsPointCloudLayerEditUtils::updateChunkValues( QgsCopcPointCloudIndex *copcIndex,
const QByteArray &chunkData,
const QgsPointCloudAttribute &attribute,
double newValue,
const QgsPointCloudNodeId &n,
const QVector<int> &pointIndices )
192 Q_ASSERT( copcIndex->mHierarchy.contains( n ) );
193 Q_ASSERT( copcIndex->mHierarchyNodePos.contains( n ) );
195 int pointCount = copcIndex->mHierarchy[n];
197 lazperf::header14 header = copcIndex->mLazInfo->header();
199 lazperf::reader::chunk_decompressor decompressor( header.pointFormat(), header.ebCount(), chunkData.constData() );
200 lazperf::writer::chunk_compressor compressor( header.pointFormat(), header.ebCount() );
202 std::unique_ptr<char []> decodedData(
new char[ header.point_record_length ] );
205 Q_ASSERT( header.pointFormat() == 6 || header.pointFormat() == 7 || header.pointFormat() == 8 );
207 QSet<int> pointIndicesSet( pointIndices.constBegin(), pointIndices.constEnd() );
209 QString attributeName = attribute.
name();
211 for (
int i = 0 ; i < pointCount; ++i )
213 decompressor.decompress( decodedData.get() );
214 char *buf = decodedData.get();
216 if ( pointIndicesSet.contains( i ) )
219 updatePoint( buf, header.point_format_id, attributeName, newValue );
222 compressor.compress( decodedData.get() );
225 std::vector<unsigned char> data = compressor.done();
226 return QByteArray( (
const char * ) data.data(), (
int ) data.size() );
232 const QVector<QgsPointCloudAttribute> attributes = allAttributes.
attributes();
234 const char *ptr = data.data();
237 for (
int i = 0; i < nPoints; ++i )
243 outData.append( ptr, attr.size() );
257 const QString name = attribute.
name().toUpper();
259 if ( name == QLatin1String(
"INTENSITY" ) )
260 return value >= 0 && value <= 65535;
261 if ( name == QLatin1String(
"RETURNNUMBER" ) )
262 return value >= 0 && value <= 15;
263 if ( name == QLatin1String(
"NUMBEROFRETURNS" ) )
264 return value >= 0 && value <= 15;
265 if ( name == QLatin1String(
"SCANCHANNEL" ) )
266 return value >= 0 && value <= 3;
267 if ( name == QLatin1String(
"SCANDIRECTIONFLAG" ) )
268 return value >= 0 && value <= 1;
269 if ( name == QLatin1String(
"EDGEOFFLIGHTLINE" ) )
270 return value >= 0 && value <= 1;
271 if ( name == QLatin1String(
"CLASSIFICATION" ) )
272 return value >= 0 && value <= 255;
273 if ( name == QLatin1String(
"USERDATA" ) )
274 return value >= 0 && value <= 255;
275 if ( name == QLatin1String(
"SCANANGLE" ) )
276 return value >= -30'000 && value <= 30'000;
277 if ( name == QLatin1String(
"POINTSOURCEID" ) )
278 return value >= 0 && value <= 65535;
279 if ( name == QLatin1String(
"GPSTIME" ) )
281 if ( name == QLatin1String(
"SYNTHETIC" ) )
282 return value >= 0 && value <= 1;
283 if ( name == QLatin1String(
"KEYPOINT" ) )
284 return value >= 0 && value <= 1;
285 if ( name == QLatin1String(
"WITHHELD" ) )
286 return value >= 0 && value <= 1;
287 if ( name == QLatin1String(
"OVERLAP" ) )
288 return value >= 0 && value <= 1;
289 if ( name == QLatin1String(
"RED" ) )
290 return value >= 0 && value <= 65535;
291 if ( name == QLatin1String(
"GREEN" ) )
292 return value >= 0 && value <= 65535;
293 if ( name == QLatin1String(
"BLUE" ) )
294 return value >= 0 && value <= 65535;
Collection of point cloud attributes.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
int indexOf(const QString &name) const
Returns the index of the attribute with the specified name.
Attribute for point cloud data pair of name and size in bytes.
int size() const
Returns size of the attribute in bytes.
QString name() const
Returns name of the attribute.
DataType type() const
Returns the data type.
The QgsPointCloudEditingIndex class is a QgsPointCloudIndex that is used as an editing buffer when ed...
bool updateNodeData(const QHash< QgsPointCloudNodeId, QByteArray > &data)
Tries to update the data for the specified nodes.
QgsPointCloudNode getNode(const QgsPointCloudNodeId &id) const
Returns object for a given node.
bool hasNode(const QgsPointCloudNodeId &id) const
Returns whether the octree contain given node.
QgsAbstractPointCloudIndex * get()
Returns pointer to the implementation class.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
QgsPointCloudLayerEditUtils(QgsPointCloudLayer *layer)
Ctor.
static QByteArray dataForAttributes(const QgsPointCloudAttributeCollection &allAttributes, const QByteArray &data, const QgsPointCloudRequest &request)
Takes data comprising of allAttributes and returns a QByteArray with data only for the attributes inc...
static bool isAttributeValueValid(const QgsPointCloudAttribute &attribute, double value)
Check if value is within proper range for the attribute.
bool changeAttributeValue(const QgsPointCloudNodeId &n, const QVector< int > &points, const QgsPointCloudAttribute &attribute, double value)
Attempts to modify attribute values for specific points in the editing buffer.
Represents a map layer supporting display of point clouds.
Represents a indexed point cloud node's position in octree.
bool isValid() const
Returns whether node is valid.
qint64 pointCount() const
Returns number of points contained in node data.
Point cloud data request.
QgsPointCloudAttributeCollection attributes() const
Returns attributes.