ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
LasSaver.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 "LasSaver.h"
9 
10 #include "LasMetadata.h"
11 
12 // Qt
13 #include <QDate>
14 // DB
15 #include <ecvGlobalShiftManager.h>
16 #include <ecvPointCloud.h>
17 
19  : m_cloudToSave(cloud)
20 {
21  // restore the global encoding (if any) - must be done before calling initLaszipHeader
22  LasMetadata::LoadGlobalEncoding(cloud, m_laszipHeader.global_encoding);
23  // restore the project UUID (if any)
24  LasMetadata::LoadProjectUUID(cloud, m_laszipHeader);
25 
26  initLaszipHeader(parameters);
27 
29  unsigned totalExtraByteSize = LasExtraScalarField::TotalExtraBytesSize(parameters.extraFields);
30 
31  if (totalExtraByteSize > 0)
32  {
33  m_laszipHeader.point_data_record_length += totalExtraByteSize;
34 
35  size_t newNumVlrs = m_laszipHeader.number_of_variable_length_records + 1;
36  laszip_vlr* vlrs = new laszip_vlr_struct[newNumVlrs];
37  // Move already existing vlrs
38  for (size_t i = 0; i < m_laszipHeader.number_of_variable_length_records; i++)
39  {
40  vlrs[i] = m_laszipHeader.vlrs[i];
41  }
42  LasExtraScalarField::InitExtraBytesVlr(vlrs[newNumVlrs - 1], parameters.extraFields);
43 
44  delete m_laszipHeader.vlrs;
45  m_laszipHeader.vlrs = vlrs;
46  m_laszipHeader.number_of_variable_length_records = static_cast<laszip_U32>(newNumVlrs);
47 
48  m_laszipHeader.offset_to_point_data += LasDetails::SizeOfVlrs(&m_laszipHeader.vlrs[newNumVlrs - 1], 1);
49  }
50 
51  m_fieldsSaver.setStandarFields(std::move(parameters.standardFields));
52  m_fieldsSaver.setExtraFields(std::move(parameters.extraFields));
53  m_shouldSaveRGB = parameters.shouldSaveRGB && cloud.hasColors();
54 
55  if (parameters.shouldSaveWaveform)
56  {
57  assert(LasDetails::HasWaveform(m_laszipHeader.point_data_format) && cloud.hasFWF());
58  m_waveformSaver = std::make_unique<LasWaveformSaver>(cloud);
59  }
60 }
61 
62 void LasSaver::initLaszipHeader(const Parameters& parameters)
63 {
64  QDate currentDate = QDate::currentDate();
65  m_laszipHeader.file_creation_year = currentDate.year();
66  m_laszipHeader.file_creation_day = currentDate.dayOfYear();
67 
68  m_laszipHeader.version_major = parameters.versionMajor;
69  m_laszipHeader.version_minor = parameters.versionMinor;
70  m_laszipHeader.point_data_format = parameters.pointFormat;
71 
72  // TODO global encoding wkt and other
73  if (LasDetails::HasWaveform(m_laszipHeader.point_data_format) && m_cloudToSave.hasFWF())
74  {
75  // We always store FWF externally
76  m_laszipHeader.global_encoding |= 0b0000'0100; // bit 2 = Waveform Data Packets External
77  m_laszipHeader.global_encoding &= (~0b0000'0010); // bit 1 = Waveform Data Packets Internal (deprecated, we do this 'just in case')
78  }
79 
80  m_laszipHeader.header_size = LasDetails::HeaderSize(m_laszipHeader.version_minor);
81  m_laszipHeader.offset_to_point_data = m_laszipHeader.header_size;
82  m_laszipHeader.point_data_record_length = LasDetails::PointFormatSize(m_laszipHeader.point_data_format);
83 
84  LasVlr vlr;
85  if (LasMetadata::LoadVlrs(m_cloudToSave, vlr))
86  {
87  m_laszipHeader.vlrs = new laszip_vlr_struct[vlr.numVlrs()];
88  m_laszipHeader.number_of_variable_length_records = vlr.numVlrs();
89  for (laszip_U32 i = 0; i < m_laszipHeader.number_of_variable_length_records; ++i)
90  {
91  LasDetails::CloneVlrInto(vlr.vlrs[i], m_laszipHeader.vlrs[i]);
92  }
93  m_laszipHeader.offset_to_point_data += LasDetails::SizeOfVlrs(m_laszipHeader.vlrs, m_laszipHeader.number_of_variable_length_records);
94  }
95 
96  // set LAS scale
97  m_laszipHeader.x_scale_factor = parameters.lasScale.x;
98  m_laszipHeader.y_scale_factor = parameters.lasScale.y;
99  m_laszipHeader.z_scale_factor = parameters.lasScale.z;
100 
101  // set LAS offset
102  m_laszipHeader.x_offset = parameters.lasOffset.x;
103  m_laszipHeader.y_offset = parameters.lasOffset.y;
104  m_laszipHeader.z_offset = parameters.lasOffset.z;
105 
106  strncpy(m_laszipHeader.generating_software, "ACloudViewer", 32);
107 }
109 {
110  if (m_laszipWriter)
111  {
112  laszip_close_writer(m_laszipWriter);
113  laszip_clean(m_laszipWriter);
114  laszip_destroy(m_laszipWriter);
115  }
116 }
117 
118 CC_FILE_ERROR LasSaver::open(const QString filePath)
119 {
120  laszip_CHAR* errorMsg{nullptr};
121  if (laszip_create(&m_laszipWriter))
122  {
123  CVLog::Warning("[LAS] laszip failed to create the writer");
125  }
126 
127  if (laszip_set_header(m_laszipWriter, &m_laszipHeader))
128  {
129  laszip_get_error(m_laszipWriter, &errorMsg);
130  CVLog::Warning("[LAS] laszip error :'%s'", errorMsg);
132  }
133 
134  if (laszip_open_writer(m_laszipWriter, qPrintable(filePath), filePath.endsWith("laz")))
135  {
136  laszip_get_error(m_laszipWriter, &errorMsg);
137  CVLog::Warning("[LAS] laszip error :'%s'", errorMsg);
139  }
140 
141  if (laszip_get_point_pointer(m_laszipWriter, &m_laszipPoint))
142  {
143  fprintf(stderr, "DLL ERROR: getting point pointer from laszip writer\n");
145  }
146 
147  return CC_FERR_NO_ERROR;
148 }
149 
151 {
152  if (!m_laszipPoint)
153  {
154  assert(false);
156  }
157 
158  if (m_currentPointIndex >= m_cloudToSave.size())
159  {
160  return CC_FERR_NO_SAVE;
161  }
162  laszip_CHAR* errorMsg{nullptr};
163 
164  // reset point
165  int totalExtraByteSize = m_laszipPoint->num_extra_bytes;
166  laszip_U8* extra_bytes = m_laszipPoint->extra_bytes;
167  *m_laszipPoint = {};
168  m_laszipPoint->extra_bytes = extra_bytes;
169  m_laszipPoint->num_extra_bytes = totalExtraByteSize;
170  m_laszipPoint->extended_point_type = m_laszipHeader.point_data_format >= 6;
171 
172  const CCVector3* point = m_cloudToSave.getPoint(m_currentPointIndex);
173  const CCVector3d globalPoint = m_cloudToSave.toGlobal3d<PointCoordinateType>(*point);
174 
175  laszip_F64 coords[3];
176  coords[0] = globalPoint.x;
177  coords[1] = globalPoint.y;
178  coords[2] = globalPoint.z;
179 
180  if (laszip_set_coordinates(m_laszipWriter, coords))
181  {
182  laszip_get_error(m_laszipWriter, &errorMsg);
183  CVLog::Warning("[LAS] laszip error :'%s'", errorMsg);
185  }
186 
187  m_fieldsSaver.handleScalarFields(m_currentPointIndex, *m_laszipPoint);
188  m_fieldsSaver.handleExtraFields(m_currentPointIndex, *m_laszipPoint);
189 
190  if (m_waveformSaver)
191  {
192  m_waveformSaver->handlePoint(m_currentPointIndex, *m_laszipPoint);
193  }
194 
195  if (m_shouldSaveRGB)
196  {
197  assert(LasDetails::HasRGB(m_laszipHeader.point_data_format) && m_cloudToSave.hasColors());
198  const ecvColor::Rgb& color = m_cloudToSave.getPointColor(m_currentPointIndex);
199  m_laszipPoint->rgb[0] = static_cast<laszip_U16>(color.r) << 8;
200  m_laszipPoint->rgb[1] = static_cast<laszip_U16>(color.g) << 8;
201  m_laszipPoint->rgb[2] = static_cast<laszip_U16>(color.b) << 8;
202  }
203 
204  if (laszip_write_point(m_laszipWriter))
205  {
206  laszip_get_error(m_laszipWriter, &errorMsg);
207  CVLog::Warning("[LAS] laszip error :'%s'", errorMsg);
209  }
210 
211  if (laszip_update_inventory(m_laszipWriter))
212  {
213  laszip_get_error(m_laszipWriter, &errorMsg);
214  CVLog::Warning("[LAS] laszip error :'%s'", errorMsg);
216  }
217 
218  m_currentPointIndex++;
219  return CC_FERR_NO_ERROR;
220 }
221 
223 {
224  return m_waveformSaver != nullptr;
225 }
226 
227 QString LasSaver::getLastError() const
228 {
229  laszip_CHAR* errorMsg{nullptr};
230  laszip_get_error(m_laszipWriter, &errorMsg);
231 
232  return errorMsg;
233 }
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
CC_FILE_ERROR
Typical I/O filter errors.
Definition: FileIOFilter.h:20
@ CC_FERR_THIRD_PARTY_LIB_FAILURE
Definition: FileIOFilter.h:36
@ CC_FERR_NO_SAVE
Definition: FileIOFilter.h:27
@ CC_FERR_NO_ERROR
Definition: FileIOFilter.h:21
@ CC_FERR_INTERNAL
Definition: FileIOFilter.h:39
laszip_vlr laszip_vlr_struct
Definition: LasDetails.h:46
math::float4 color
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static void UpdateByteOffsets(std::vector< LasExtraScalarField > &extraFields)
static void InitExtraBytesVlr(laszip_vlr_struct &vlr, const std::vector< LasExtraScalarField > &extraFields)
static unsigned TotalExtraBytesSize(const std::vector< LasExtraScalarField > &extraScalarFields)
~LasSaver() noexcept
Definition: LasSaver.cpp:108
LasSaver(ccPointCloud &cloud, Parameters &parameters)
Definition: LasSaver.cpp:18
CC_FILE_ERROR open(const QString filePath)
Definition: LasSaver.cpp:118
bool savesWaveforms() const
Definition: LasSaver.cpp:222
QString getLastError() const
Definition: LasSaver.cpp:227
CC_FILE_ERROR saveNextPoint()
Definition: LasSaver.cpp:150
void setStandarFields(std::vector< LasScalarField > &&standardFields)
void setExtraFields(std::vector< LasExtraScalarField > &&extraFields)
void handleExtraFields(size_t pointIndex, laszip_point &point)
Saves the extra scalar fields values for pointIndex into the given laszip_point.
void handleScalarFields(size_t pointIndex, laszip_point &point)
Saves the scalar fields values for pointIndex into the given laszip_point.
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasFWF() const
Returns whether the cloud has associated Full WaveForm data.
bool hasColors() const override
Returns whether colors are enabled or not.
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
CCVector3d toGlobal3d(const Vector3Tpl< T > &Plocal) const
Returns the point back-projected into the original coordinates system.
unsigned size() const override
Definition: PointCloudTpl.h:38
const CCVector3 * getPoint(unsigned index) const override
RGB color structure.
Definition: ecvColorTypes.h:49
uint16_t PointFormatSize(unsigned pointFormat)
Definition: LasDetails.cpp:80
bool HasRGB(unsigned pointFormatId)
Returns whether the point format supports RGB.
Definition: LasDetails.h:132
bool HasWaveform(unsigned pointFormatId)
Returns whether the point format supports Waveforms.
Definition: LasDetails.h:143
uint16_t HeaderSize(unsigned versionMinor)
Returns the header size for the given minor version of the standard used.
Definition: LasDetails.cpp:112
void CloneVlrInto(const laszip_vlr_struct &src, laszip_vlr_struct &dst)
Clones the content of the src vlr into the dst vlr.
Definition: LasDetails.cpp:285
unsigned SizeOfVlrs(const laszip_vlr_struct *vlrs, unsigned numVlrs)
Definition: LasDetails.cpp:145
bool LoadGlobalEncoding(const ccPointCloud &pointCloud, uint16_t &outGlobalEncoding)
bool LoadProjectUUID(const ccPointCloud &pointCloud, laszip_header &header)
bool LoadVlrs(const ccPointCloud &pointCloud, LasVlr &vlr)
std::vector< LasScalarField > standardFields
Definition: LasSaver.h:46
std::vector< LasExtraScalarField > extraFields
Definition: LasSaver.h:47
Definition: LasVlr.h:38
std::vector< laszip_vlr_struct > vlrs
Definition: LasVlr.h:120
laszip_U32 numVlrs() const
Definition: LasVlr.h:52
Algorithm parameters.
Definition: lsd.c:149