ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
LasExtraScalarField.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "LasExtraScalarField.h"
9 
10 #include "LasDetails.h"
11 
12 // DB
13 #include <CVLog.h>
14 #include <ecvPointCloud.h>
15 #include <ecvScalarField.h>
16 // LASzip
17 #include <laszip/laszip_api.h>
18 // Qt
19 #include <QDataStream>
20 // System
21 #include <cstring>
22 #include <stdexcept>
23 
24 QDataStream& operator>>(QDataStream& dataStream, LasExtraScalarField& extraScalarField)
25 {
26  dataStream.setByteOrder(QDataStream::ByteOrder::LittleEndian);
27 
28  uint8_t dataType = 0;
29  dataStream.skipRawData(2);
30  dataStream >> dataType >> extraScalarField.options;
31  dataStream.readRawData(extraScalarField.name, 32);
32  dataStream.skipRawData(4);
33  dataStream.readRawData(reinterpret_cast<char*>(extraScalarField.noData), 3 * 8);
34  dataStream.readRawData(reinterpret_cast<char*>(extraScalarField.mins), 3 * 8);
35  dataStream.readRawData(reinterpret_cast<char*>(extraScalarField.maxs), 3 * 8);
36  dataStream >> extraScalarField.scales[0] >> extraScalarField.scales[1] >> extraScalarField.scales[2];
37  dataStream >> extraScalarField.offsets[0] >> extraScalarField.offsets[1] >> extraScalarField.offsets[2];
38  dataStream.readRawData(reinterpret_cast<char*>(extraScalarField.description), 32);
39 
40  auto type_and_dim_size = LasExtraScalarField::DataTypeFromValue(dataType);
41  extraScalarField.type = std::get<0>(type_and_dim_size);
42  extraScalarField.dimensions = std::get<1>(type_and_dim_size);
43 
44  return dataStream;
45 }
46 
47 QDataStream& operator<<(QDataStream& dataStream, const LasExtraScalarField& extraScalarField)
48 {
49  dataStream.setByteOrder(QDataStream::ByteOrder::LittleEndian);
50 
51  uint8_t emptyByte{0};
52  dataStream << emptyByte << emptyByte;
53  dataStream << extraScalarField.typeCode() << extraScalarField.options;
54  dataStream.writeRawData(extraScalarField.name, 32);
55  dataStream << emptyByte << emptyByte << emptyByte << emptyByte;
56  dataStream.writeRawData(reinterpret_cast<const char*>(extraScalarField.noData), 3 * 8);
57  dataStream.writeRawData(reinterpret_cast<const char*>(extraScalarField.mins), 3 * 8);
58  dataStream.writeRawData(reinterpret_cast<const char*>(extraScalarField.maxs), 3 * 8);
59  dataStream << extraScalarField.scales[0] << extraScalarField.scales[1] << extraScalarField.scales[2];
60  dataStream << extraScalarField.offsets[0] << extraScalarField.offsets[1] << extraScalarField.offsets[2];
61  dataStream.writeRawData(reinterpret_cast<const char*>(extraScalarField.description), 32);
62 
63  return dataStream;
64 }
65 
66 std::tuple<LasExtraScalarField::DataType, LasExtraScalarField::DimensionSize>
68 {
69  if (value >= 31)
70  {
71  return {DataType::Invalid, DimensionSize::One};
72  }
73 
74  if (value == 0)
75  {
76  return {DataType::Undocumented, DimensionSize::One};
77  }
78 
80 
81  // value is now in range [1..30]
82 
83  if (value >= 21)
84  {
85  value -= 20;
86  dimSize = DimensionSize::Three;
87  }
88 
89  if (value >= 11)
90  {
91  value -= 10;
92  dimSize = DimensionSize::Two;
93  }
94 
95  // value is now be in range [1..10]
96  DataType dataType;
97  switch (value)
98  {
99  case 1:
100  dataType = DataType::u8;
101  break;
102  case 3:
103  dataType = DataType::u16;
104  break;
105  case 5:
106  dataType = DataType::u32;
107  break;
108  case 7:
109  dataType = DataType::u64;
110  break;
111  case 2:
112  dataType = DataType::i8;
113  break;
114  case 4:
115  dataType = DataType::i16;
116  break;
117  case 6:
118  dataType = DataType::i32;
119  break;
120  case 8:
121  dataType = DataType::i64;
122  break;
123  case 9:
124  dataType = DataType::f32;
125  break;
126  case 10:
127  dataType = DataType::f64;
128  break;
129  }
130 
131  return {dataType, dimSize};
132 }
133 
135 {
136  switch (type)
137  {
138  case Undocumented:
139  case u8:
140  return sizeof(uint8_t);
141  case u16:
142  return sizeof(uint16_t);
143  case u32:
144  return sizeof(uint32_t);
145  case u64:
146  return sizeof(uint64_t);
147  case i8:
148  return sizeof(int8_t);
149  case i16:
150  return sizeof(int16_t);
151  case i32:
152  return sizeof(int32_t);
153  case i64:
154  return sizeof(int64_t);
155  case f32:
156  return sizeof(float);
157  case f64:
158  return sizeof(double);
159  case Invalid:
160  return 0;
161  }
162 
163  Q_ASSERT_X(false, "elementSize", "Unhandled data type");
164  return 0;
165 }
166 
168 {
169  return elementSize() * numElements();
170 }
171 
173 {
174  return static_cast<unsigned>(dimensions);
175 }
176 
177 std::vector<LasExtraScalarField>
178 LasExtraScalarField::ParseExtraScalarFields(const laszip_header& laszipHeader)
179 {
180  auto* extraBytesVlr = std::find_if(laszipHeader.vlrs,
181  laszipHeader.vlrs + laszipHeader.number_of_variable_length_records,
183  if (extraBytesVlr < laszipHeader.vlrs + laszipHeader.number_of_variable_length_records)
184  {
185  return LasExtraScalarField::ParseExtraScalarFields(*extraBytesVlr);
186  }
187  return {};
188 }
189 
190 std::vector<LasExtraScalarField>
192 {
193  if (!LasDetails::IsExtraBytesVlr(extraBytesVlr))
194  {
195  return {};
196  }
197 
198  std::vector<LasExtraScalarField> info;
199  QByteArray data(reinterpret_cast<char*>(extraBytesVlr.data), extraBytesVlr.record_length_after_header);
200  QDataStream dataStream(data);
201 
202  int numExtraFields = extraBytesVlr.record_length_after_header / 192;
203 
204  unsigned byteOffset{0};
205  for (int j = 0; j < numExtraFields; ++j)
206  {
207  LasExtraScalarField ebInfo;
208  dataStream >> ebInfo;
209  ebInfo.byteOffset = byteOffset;
210 
211  if (ebInfo.type != DataType::Undocumented && ebInfo.type != ebInfo.DataType::Invalid)
212  {
213  info.push_back(ebInfo);
214  }
215  else
216  {
217  CVLog::Warning("Undocumented or invalid Extra Bytes are not supported");
218  }
219 
220  byteOffset += ebInfo.byteSize();
221  CVLog::Print("[LAS] Extra Bytes: Name: '%s', Type: %s -> Size %d, Offset %d",
222  ebInfo.name,
223  ebInfo.typeName(),
224  ebInfo.byteSize(),
225  ebInfo.byteOffset);
226  }
227  return info;
228 }
229 
231 {
232  return options & 1;
233 }
234 
236 {
237  return options & 2;
238 }
239 
241 {
242  return options & 4;
243 }
244 
246 {
247  return options & 8;
248 }
249 
251 {
252  return options & 16;
253 }
254 
256 {
257  if (isRelevant)
258  {
259  options |= 8;
260  }
261  else
262  {
263  options &= ~8;
264  }
265 }
266 
268 {
269  if (isRelevant)
270  {
271  options |= 16;
272  }
273  else
274  {
275  options &= ~16;
276  }
277 }
278 
280 {
281  switch (type)
282  {
289  return Unsigned;
294  return Signed;
297  return Floating;
298  }
299  return Unsigned;
300 }
301 
303  const vector<LasExtraScalarField>& extraFields)
304 {
305  strcpy(vlr.user_id, "LASF_Spec");
306  vlr.record_id = 4;
307  vlr.record_length_after_header = 192 * static_cast<laszip_U16>(extraFields.size());
308  std::fill(vlr.description, vlr.description + 32, 0);
309  vlr.data = new laszip_U8[vlr.record_length_after_header];
310 
311  QByteArray byteArray;
312  byteArray.resize(vlr.record_length_after_header);
313  QDataStream dataStream(&byteArray, QIODevice::WriteOnly);
314  for (const LasExtraScalarField& extraScalarField : extraFields)
315  {
316  dataStream << extraScalarField;
317  }
318  Q_ASSERT(byteArray.size() == vlr.record_length_after_header);
319  std::copy(byteArray.begin(), byteArray.end(), vlr.data);
320 }
321 
323 {
324  Q_ASSERT(type != DataType::Invalid);
325  uint8_t code = static_cast<uint8_t>(type);
326  code += (10 * (numElements() - 1));
327  return code;
328 }
329 
330 const char* LasExtraScalarField::typeName() const
331 {
332  switch (type)
333  {
334  case Undocumented:
335  return "Undocumented";
336  case u8:
337  return "u8";
338  case u16:
339  return "u16";
340  case u32:
341  return "u32";
342  case u64:
343  return "u64";
344  case i8:
345  return "i8";
346  case i16:
347  return "i16";
348  case i32:
349  return "i32";
350  case i64:
351  return "i64";
352  case f32:
353  return "f32";
354  case f64:
355  return "f64";
356  case Invalid:
357  return "Invalid";
358  }
359  return "";
360 }
361 
362 unsigned LasExtraScalarField::TotalExtraBytesSize(const std::vector<LasExtraScalarField>& extraScalarFields)
363 {
364  return std::accumulate(extraScalarFields.begin(),
365  extraScalarFields.end(),
366  0,
367  [](unsigned sum, const LasExtraScalarField& field)
368  { return sum + field.byteSize(); });
369 }
370 
372 {
373  for (unsigned i = 0; i < MAX_DIM_SIZE; ++i)
374  {
375  scalarFields[i] = nullptr;
376  }
377 }
378 
379 void LasExtraScalarField::UpdateByteOffsets(vector<LasExtraScalarField>& extraFields)
380 {
381  unsigned byteOffset{0};
382  for (LasExtraScalarField& extraScalarField : extraFields)
383  {
384  extraScalarField.byteOffset = byteOffset;
385  byteOffset += extraScalarField.byteSize();
386  }
387 }
388 
389 void LasExtraScalarField::MatchExtraBytesToScalarFields(vector<LasExtraScalarField>& extraScalarFields,
390  const ccPointCloud& pointCloud)
391 {
392  for (LasExtraScalarField& extraScalarField : extraScalarFields)
393  {
394  if (extraScalarField.numElements() > 1)
395  {
396  // Array fields are split into multiple ccScalarField
397  // and each of them has the index appended to the name
398  char name[50];
399  unsigned found{0};
400  for (unsigned i = 0; i < extraScalarField.numElements(); ++i)
401  {
402  snprintf(name, 50, "%s [%d]", extraScalarField.name, i);
403  int pos = pointCloud.getScalarFieldIndexByName(name);
404  if (pos >= 0)
405  {
406  extraScalarField.scalarFields[i] =
407  dynamic_cast<ccScalarField*>(pointCloud.getScalarField(pos));
408  found++;
409  CVLog::Warning("[LAS] field %s found", name);
410  }
411  else
412  {
413  CVLog::Warning("[LAS] field %s not found", name);
414  extraScalarField.scalarFields[i] = nullptr;
415  }
416  }
417  }
418  else
419  {
420  const char* nameToSearch;
421  if (extraScalarField.ccName[0] != 0)
422  {
423  // This field's name clashed with existing ccScalarField when created
424  nameToSearch = extraScalarField.ccName;
425  }
426  else
427  {
428  nameToSearch = extraScalarField.name;
429  }
430  int pos = pointCloud.getScalarFieldIndexByName(nameToSearch);
431  if (pos >= 0)
432  {
433  extraScalarField.scalarFields[0] =
434  dynamic_cast<ccScalarField*>(pointCloud.getScalarField(pos));
435  }
436  else
437  {
438  CVLog::Warning("[LAS] field %s not found", nameToSearch);
439  }
440  }
441  }
442  // Remove any Extra Scalar Field for which we could not get _all_ the corresponding
443  // ccScalarField
444  const auto notAllScalarFieldWereFound = [](const LasExtraScalarField& extraScalarField)
445  {
446  const auto ptrIsNull = [](const ccScalarField* ptr)
447  { return ptr == nullptr; };
448  return std::any_of(extraScalarField.scalarFields,
449  extraScalarField.scalarFields + extraScalarField.numElements(),
450  ptrIsNull);
451  };
452 
453  auto firstToRemove =
454  std::remove_if(extraScalarFields.begin(), extraScalarFields.end(), notAllScalarFieldWereFound);
455  extraScalarFields.erase(firstToRemove, extraScalarFields.end());
456 }
laszip_vlr laszip_vlr_struct
Definition: LasDetails.h:46
QDataStream & operator>>(QDataStream &dataStream, LasExtraScalarField &extraScalarField)
QDataStream & operator<<(QDataStream &dataStream, const LasExtraScalarField &extraScalarField)
bool copy
Definition: VtkUtils.cpp:74
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
This serves the same purpose as LasScalarField but for extra bytes.
char name[MAX_NAME_SIZE]
static constexpr unsigned MAX_DIM_SIZE
static void UpdateByteOffsets(std::vector< LasExtraScalarField > &extraFields)
unsigned elementSize() const
char description[MAX_DESCRIPTION_SIZE]
double scales[MAX_DIM_SIZE]
uint8_t noData[MAX_DIM_SIZE][8]
uint8_t mins[MAX_DIM_SIZE][8]
DataType
Data types available LAS Extra Field.
const char * typeName() const
unsigned numElements() const
void setScaleIsRelevant(bool isRelevant)
uint8_t maxs[MAX_DIM_SIZE][8]
static void InitExtraBytesVlr(laszip_vlr_struct &vlr, const std::vector< LasExtraScalarField > &extraFields)
static void MatchExtraBytesToScalarFields(std::vector< LasExtraScalarField > &extraScalarFields, const ccPointCloud &pointCloud)
void setOffsetIsRelevant(bool isRelevant)
static unsigned TotalExtraBytesSize(const std::vector< LasExtraScalarField > &extraScalarFields)
double offsets[MAX_DIM_SIZE]
static std::tuple< DataType, DimensionSize > DataTypeFromValue(uint8_t value)
ccScalarField * scalarFields[MAX_DIM_SIZE]
static std::vector< LasExtraScalarField > ParseExtraScalarFields(const laszip_header &laszipHeader)
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
A scalar field associated to display-related parameters.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
bool IsExtraBytesVlr(const laszip_vlr_struct &)
Returns whether the vlr describes extra bytes.
Definition: LasDetails.cpp:136
sqlite3_uint64 u64
Definition: shell.c:91
sqlite3_int64 i64
Definition: shell.c:90
unsigned char u8
Definition: shell.c:92
unsigned int u32
Definition: sqlite3.c:14316
short int i16
Definition: sqlite3.c:14318
signed char i8
Definition: sqlite3.c:14320
unsigned short int u16
Definition: sqlite3.c:14317