ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
LASFields.h
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 #pragma once
9 
10 // qCC_db
11 #include <ecvPointCloud.h>
12 #include <ecvScalarField.h>
13 
14 // Qt
15 #include <QSharedPointer>
16 
17 // System
18 #include <vector>
19 
20 class ccScalarField;
21 class ccPointCloud;
22 
23 static const char LAS_SCALE_X_META_DATA[] = "LAS.scale.x";
24 static const char LAS_SCALE_Y_META_DATA[] = "LAS.scale.y";
25 static const char LAS_SCALE_Z_META_DATA[] = "LAS.scale.z";
26 static const char LAS_OFFSET_X_META_DATA[] = "LAS.offset.x";
27 static const char LAS_OFFSET_Y_META_DATA[] = "LAS.offset.y";
28 static const char LAS_OFFSET_Z_META_DATA[] = "LAS.offset.z";
29 static const char LAS_VERSION_MAJOR_META_DATA[] = "LAS.version.major";
30 static const char LAS_VERSION_MINOR_META_DATA[] = "LAS.version.minor";
31 static const char LAS_POINT_FORMAT_META_DATA[] = "LAS.point_format";
32 static const char LAS_GLOBAL_ENCODING_META_DATA[] = "LAS.global_encoding";
33 static const char LAS_PROJECT_UUID_META_DATA[] = "LAS.project_uuid";
34 
35 enum LAS_FIELDS {
36  LAS_X = 0,
37  LAS_Y = 1,
38  LAS_Z = 2,
48  LAS_RED = 12,
49  LAS_GREEN = 13,
50  LAS_BLUE = 14,
51  LAS_TIME = 15,
52  LAS_EXTRA = 16,
53  // Sub fields
59  // Invald flag
60  LAS_INVALID = 255
61 };
62 
63 const char LAS_FIELD_NAMES[][28] = {
64  "X",
65  "Y",
66  "Z",
67  "Intensity",
68  "ReturnNumber",
69  "NumberOfReturns",
70  "ScanDirectionFlag",
71  "EdgeOfFlightLine",
72  "Classification",
73  "ScanAngleRank",
74  "UserData",
75  "PointSourceId",
76  "Red",
77  "Green",
78  "Blue",
79  "GpsTime",
80  "extra",
81  "[Classif] Value",
82  "[Classif] Synthetic flag",
83  "[Classif] Key-point flag",
84  "[Classif] Withheld flag",
85  "[Classif] Overlap flag",
86 };
87 
89 struct LasField {
91  typedef QSharedPointer<LasField> Shared;
92 
95  double defaultVal = 0,
96  double min = 0.0,
97  double max = -1.0,
98  uint8_t _minPointFormat = 0)
99  : type(fieldType),
100  sf(nullptr),
101  firstValue(0.0),
102  minValue(min),
103  maxValue(max),
104  defaultValue(defaultVal),
105  minPointFormat(_minPointFormat) {}
106 
108  virtual inline QString getName() const {
109  return type < LAS_INVALID ? QString(LAS_FIELD_NAMES[type]) : QString();
110  }
111 
113  static bool GetLASFields(ccPointCloud* cloud,
114  std::vector<LasField>& fieldsToSave,
115  uint8_t& minPointFormat) {
116  try {
117  // official LAS fields
118  std::vector<LasField> lasFields;
119  lasFields.reserve(14);
120  {
121  lasFields.emplace_back(LAS_CLASSIFICATION, 0, 0, 255,
122  0); // unsigned char: between 0 and 255
123  lasFields.emplace_back(LAS_CLASSIF_VALUE, 0, 0, 31,
124  0); // 5 bits: between 0 and 31
125  lasFields.emplace_back(LAS_CLASSIF_SYNTHETIC, 0, 0, 1,
126  0); // 1 bit: 0 or 1
127  lasFields.emplace_back(LAS_CLASSIF_KEYPOINT, 0, 0, 1,
128  0); // 1 bit: 0 or 1
129  lasFields.emplace_back(LAS_CLASSIF_WITHHELD, 0, 0, 1,
130  0); // 1 bit: 0 or 1
131  lasFields.emplace_back(LAS_CLASSIF_OVERLAP, 0, 0, 1,
132  6); // 1 bit: 0 or 1
133  lasFields.emplace_back(LAS_INTENSITY, 0, 0, 65535,
134  0); // 16 bits: between 0 and 65536
135  lasFields.emplace_back(LAS_TIME, 0, 0, -1.0,
136  1); // 8 bytes (double)
137  lasFields.emplace_back(LAS_RETURN_NUMBER, 1, 1, 7,
138  0); // 3 bits: between 1 and 7
139  lasFields.emplace_back(LAS_NUMBER_OF_RETURNS, 1, 1, 7,
140  0); // 3 bits: between 1 and 7
141  lasFields.emplace_back(LAS_SCAN_DIRECTION, 0, 0, 1,
142  0); // 1 bit: 0 or 1
143  lasFields.emplace_back(LAS_FLIGHT_LINE_EDGE, 0, 0, 1,
144  0); // 1 bit: 0 or 1
145  lasFields.emplace_back(LAS_SCAN_ANGLE_RANK, 0, -90, 90,
146  0); // signed char: between -90 and +90
147  lasFields.emplace_back(LAS_USER_DATA, 0, 0, 255,
148  0); // unsigned char: between 0 and 255
149  lasFields.emplace_back(LAS_POINT_SOURCE_ID, 1, 0, 65535,
150  0); // 16 bits: between 1 and 65536
151  }
152 
153  // we are going to check now the existing cloud SFs
154  for (unsigned i = 0; i < cloud->getNumberOfScalarFields(); ++i) {
155  ccScalarField* sf =
156  static_cast<ccScalarField*>(cloud->getScalarField(i));
157  // find an equivalent in official LAS fields
158  QString sfName = QString(sf->getName()).toUpper();
159  bool outBounds = false;
160  for (size_t j = 0; j < lasFields.size(); ++j) {
161  // if the name matches
162  if (sfName == lasFields[j].getName().toUpper()) {
163  // check bounds
164  double sfMin = sf->getGlobalShift() + sf->getMax();
165  double sfMax = sf->getGlobalShift() + sf->getMax();
166  if (sfMin < lasFields[j].minValue ||
167  (lasFields[j].maxValue != -1.0 &&
168  sfMax > lasFields[j].maxValue)) // outbounds?
169  {
171  QString("[LAS] Found a '%1' scalar field, "
172  "but its values outbound LAS "
173  "specifications (%2-%3)...")
174  .arg(sf->getName())
175  .arg(lasFields[j].minValue)
176  .arg(lasFields[j].maxValue));
177  outBounds = true;
178  } else {
179  // we add the SF to the list of saved fields
180  fieldsToSave.push_back(lasFields[j]);
181  fieldsToSave.back().sf = sf;
182 
185  fieldsToSave.back().minPointFormat);
186  }
187  break;
188  }
189  }
190  }
191  } catch (const std::bad_alloc&) {
192  CVLog::Warning("[LasField::GetLASFields] Not enough memory");
193  return false;
194  }
195 
196  return true;
197  }
198 
199  static unsigned GetFormatRecordLength(uint8_t pointFormat) {
200  switch (pointFormat) {
201  case 0:
202  return 20; // 0 - base
203  case 1:
204  return 20 + 8; // 1 - base + GPS
205  case 2:
206  return 20 + 6; // 2 - base + RGB
207  case 3:
208  return 20 + 8 + 6; // 3 - base + GPS + RGB
209  case 4:
210  return 20 + 8 + 29; // 4 - base + GPS + FWF
211  case 5:
212  return 20 + 8 + 6 + 29; // 5 - base + GPS + FWF + RGB
213  case 6:
214  return 30; // 6 - base (GPS included)
215  case 7:
216  return 30 + 6; // 7 - base + RGB
217  case 8:
218  return 30 + 6 + 2; // 8 - base + RGB + NIR (not used)
219  case 9:
220  return 30 + 29; // 9 - base + FWF
221  case 10:
222  return 30 + 6 + 2 + 29; // 10 - base + RGB + NIR + FWF
223  default:
224  assert(false);
225  return 0;
226  }
227  }
228 
229  static uint8_t VersionMinorForPointFormat(uint8_t pointFormat) {
230  return pointFormat >= 6 ? 4 : 2;
231  }
232 
233  static uint8_t UpdateMinPointFormat(uint8_t minPointFormat,
234  bool withRGB,
235  bool withFWF,
236  bool allowLegacyFormats = true) {
237  // can we keep the (short) legacy formats?
238  if (allowLegacyFormats && minPointFormat < 6) {
239  // LAS formats:
240  // 0 - base
241  // 1 - base + GPS TIME
242  // 2 - base + RGB
243  // 3 - base + GPS + RGB
244  // 4 - base + GPS + FWF
245  // 5 - base + GPS + FWF + RGB
246 
247  if (withFWF) {
248  // 0, 1 --> 4
249  minPointFormat = std::max(minPointFormat, (uint8_t)4);
250  }
251 
252  if (withRGB) {
253  if (minPointFormat < 2) {
254  // 0 --> 2
255  // 1 --> 3
256  minPointFormat += 2;
257  } else if (minPointFormat == 4) {
258  // 4 --> 5
259  minPointFormat = 5;
260  }
261  // else the format already has colors
262  }
263  } else // we'll use extended versions (up to 15 returns, up to 256
264  // classes for classification, higher precision scan angle)
265  {
266  // new LAS formats:
267  // 6 - base (GPS included)
268  // 7 - base + RGB
269  // 8 - base + RGB + NIR (not used)
270  // 9 - base + FWF
271  // 10 - base + FWF + RGB + NIR
272  assert(minPointFormat <= 6); // in effect, standard LAS fields will
273  // only require version 6 at most
274 
275  minPointFormat = std::max(minPointFormat, (uint8_t)6);
276  // FWF data?
277  if (withFWF) {
278  // 6 --> 9
279  minPointFormat = std::max(minPointFormat, (uint8_t)9);
280  }
281  // Colors?
282  if (withRGB) {
283  if (minPointFormat == 6) {
284  // 6 --> 7
285  minPointFormat = 7;
286  } else if (minPointFormat == 9) {
287  // 9 --> 10
288  minPointFormat = 10;
289  }
290  }
291  }
292 
293  return minPointFormat;
294  }
295 
296  static QString SanitizeString(const QString& str) {
297  QString sanitizedStr = str;
298  sanitizedStr.replace('=', "_eq_");
299  sanitizedStr.replace(' ', "__");
300 
301  if (sanitizedStr.size() > 32) {
302  sanitizedStr = sanitizedStr.left(32);
303  }
304 
305  return sanitizedStr;
306  }
307 
308  static QString DesanitizeString(const QString& str) {
309  QString desanitizedStr = str;
310  desanitizedStr.replace("_eq_", "=");
311  desanitizedStr.replace("__", " ");
312 
313  return desanitizedStr;
314  }
315 
316  double getSafeValue(unsigned index) const {
317  if (sf) {
318  ScalarType value = sf->getValue(index);
319 
320  // PDAL doesn't accept NaN values
322  return value;
323  } else {
324  return defaultValue;
325  }
326  }
327 
328  assert(false);
329  return defaultValue;
330  }
331 
334  double firstValue;
335  double minValue;
336  double maxValue;
337  double defaultValue;
338  uint8_t minPointFormat;
339 };
static const char LAS_VERSION_MAJOR_META_DATA[]
Definition: LASFields.h:29
static const char LAS_OFFSET_X_META_DATA[]
Definition: LASFields.h:26
static const char LAS_SCALE_X_META_DATA[]
Definition: LASFields.h:23
const char LAS_FIELD_NAMES[][28]
Definition: LASFields.h:63
static const char LAS_OFFSET_Y_META_DATA[]
Definition: LASFields.h:27
static const char LAS_PROJECT_UUID_META_DATA[]
Definition: LASFields.h:33
LAS_FIELDS
Definition: LASFields.h:35
@ LAS_INVALID
Definition: LASFields.h:60
@ LAS_CLASSIF_KEYPOINT
Definition: LASFields.h:56
@ LAS_Z
Definition: LASFields.h:38
@ LAS_SCAN_DIRECTION
Definition: LASFields.h:42
@ LAS_POINT_SOURCE_ID
Definition: LASFields.h:47
@ LAS_CLASSIF_OVERLAP
Definition: LASFields.h:58
@ LAS_RETURN_NUMBER
Definition: LASFields.h:40
@ LAS_Y
Definition: LASFields.h:37
@ LAS_CLASSIF_VALUE
Definition: LASFields.h:54
@ LAS_FLIGHT_LINE_EDGE
Definition: LASFields.h:43
@ LAS_TIME
Definition: LASFields.h:51
@ LAS_NUMBER_OF_RETURNS
Definition: LASFields.h:41
@ LAS_SCAN_ANGLE_RANK
Definition: LASFields.h:45
@ LAS_EXTRA
Definition: LASFields.h:52
@ LAS_CLASSIF_WITHHELD
Definition: LASFields.h:57
@ LAS_BLUE
Definition: LASFields.h:50
@ LAS_X
Definition: LASFields.h:36
@ LAS_GREEN
Definition: LASFields.h:49
@ LAS_RED
Definition: LASFields.h:48
@ LAS_CLASSIF_SYNTHETIC
Definition: LASFields.h:55
@ LAS_USER_DATA
Definition: LASFields.h:46
@ LAS_CLASSIFICATION
Definition: LASFields.h:44
@ LAS_INTENSITY
Definition: LASFields.h:39
static const char LAS_SCALE_Z_META_DATA[]
Definition: LASFields.h:25
static const char LAS_SCALE_Y_META_DATA[]
Definition: LASFields.h:24
static const char LAS_OFFSET_Z_META_DATA[]
Definition: LASFields.h:28
static const char LAS_POINT_FORMAT_META_DATA[]
Definition: LASFields.h:31
static const char LAS_GLOBAL_ENCODING_META_DATA[]
Definition: LASFields.h:32
static const char LAS_VERSION_MINOR_META_DATA[]
Definition: LASFields.h:30
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
A scalar field associated to display-related parameters.
double getGlobalShift() const
Returns the global shift (if any)
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
const char * getName() const
Returns scalar field name.
Definition: ScalarField.h:43
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
Definition: ScalarField.h:61
ScalarType getMax() const
Returns the maximum value.
Definition: ScalarField.h:74
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
LAS field descriptor.
Definition: LASFields.h:89
double getSafeValue(unsigned index) const
Definition: LASFields.h:316
double minValue
Definition: LASFields.h:335
ccScalarField * sf
Definition: LASFields.h:333
virtual QString getName() const
Returns official field name.
Definition: LASFields.h:108
LasField(LAS_FIELDS fieldType=LAS_INVALID, double defaultVal=0, double min=0.0, double max=-1.0, uint8_t _minPointFormat=0)
Default constructor.
Definition: LASFields.h:94
double maxValue
Definition: LASFields.h:336
static QString DesanitizeString(const QString &str)
Definition: LASFields.h:308
static bool GetLASFields(ccPointCloud *cloud, std::vector< LasField > &fieldsToSave, uint8_t &minPointFormat)
Returns the (compliant) LAS fields in a point cloud.
Definition: LASFields.h:113
static uint8_t UpdateMinPointFormat(uint8_t minPointFormat, bool withRGB, bool withFWF, bool allowLegacyFormats=true)
Definition: LASFields.h:233
double firstValue
Definition: LASFields.h:334
double defaultValue
Definition: LASFields.h:337
QSharedPointer< LasField > Shared
Shared type.
Definition: LASFields.h:91
uint8_t minPointFormat
Definition: LASFields.h:338
static unsigned GetFormatRecordLength(uint8_t pointFormat)
Definition: LASFields.h:199
LAS_FIELDS type
Definition: LASFields.h:332
static QString SanitizeString(const QString &str)
Definition: LASFields.h:296
static uint8_t VersionMinorForPointFormat(uint8_t pointFormat)
Definition: LASFields.h:229