29 #include <QElapsedTimer>
33 #include <laszip/laszip_api.h>
41 bool& preserveCoordinateShift,
47 bool useLasOffset =
false;
66 preserveCoordinateShift,
70 preserveCoordinateShift =
false;
82 QStringList{
"las",
"laz"},
84 QStringList{
"LAS file (*.las *.laz)"},
85 QStringList{
"LAS file (*.las *.laz)"},
88 m_openDialog.resetShouldSkipDialog();
95 laszip_POINTER laszipReader;
96 laszip_header* laszipHeader{
nullptr};
97 laszip_BOOL isCompressed{
false};
98 laszip_CHAR* errorMsg{
nullptr};
100 if (laszip_create(&laszipReader))
106 if (laszip_open_reader(laszipReader, qPrintable(fileName), &isCompressed))
108 laszip_get_error(laszipHeader, &errorMsg);
110 laszip_clean(laszipReader);
111 laszip_destroy(laszipReader);
115 if (laszip_get_header_pointer(laszipReader, &laszipHeader))
117 laszip_get_error(laszipHeader, &errorMsg);
119 laszip_close_reader(laszipReader);
120 laszip_clean(laszipReader);
121 laszip_destroy(laszipReader);
125 laszip_U64 pointCount;
126 if (laszipHeader->version_minor == 4)
128 pointCount = laszipHeader->extended_number_of_point_records;
132 pointCount = laszipHeader->number_of_point_records;
138 CVLog::Error(
"Files with more that %lu points are not supported", pointCount);
146 std::unique_ptr<FileInfo> infoOfCurrentFile = std::make_unique<FileInfo>();
147 infoOfCurrentFile->version.minorVersion = laszipHeader->version_minor;
148 infoOfCurrentFile->version.pointFormat = laszipHeader->point_data_format;
149 infoOfCurrentFile->extraScalarFields = availableEXtraScalarFields;
151 bool fileContentIsDifferentFromPrevious = (m_infoOfLastOpened && (*m_infoOfLastOpened != *infoOfCurrentFile));
153 m_openDialog.
setInfo(laszipHeader->version_minor, laszipHeader->point_data_format, pointCount);
155 m_infoOfLastOpened = std::move(infoOfCurrentFile);
181 if (m_openDialog.result() == QDialog::Rejected)
183 laszip_close_reader(laszipReader);
184 laszip_clean(laszipReader);
185 laszip_destroy(laszipReader);
195 auto pointCloud = std::make_unique<ccPointCloud>(QFileInfo(fileName).fileName());
196 if (!pointCloud->reserve(pointCount))
198 laszip_close_reader(laszipReader);
199 laszip_clean(laszipReader);
200 laszip_destroy(laszipReader);
206 laszip_F64 laszipCoordinates[3] = {0};
207 laszip_point* laszipPoint{
nullptr};
209 bool preserveGlobalShift{
true};
211 if (laszip_get_point_pointer(laszipReader, &laszipPoint))
213 laszip_get_error(laszipHeader, &errorMsg);
215 laszip_close_reader(laszipReader);
216 laszip_clean(laszipReader);
217 laszip_destroy(laszipReader);
222 availableEXtraScalarFields,
228 std::unique_ptr<LasWaveformLoader> waveformLoader{
nullptr};
231 waveformLoader = std::make_unique<LasWaveformLoader>(*laszipHeader,
241 progressDialog.
setInfo(
"Loading points");
243 progressDialog.
start();
247 for (
unsigned i = 0; i < pointCount; ++i)
255 if (laszip_read_point(laszipReader))
261 if (laszip_get_coordinates(laszipReader, laszipCoordinates))
272 laszipHeader->y_offset,
280 if (preserveGlobalShift)
282 pointCloud->setGlobalShift(globalShift);
285 if (globalShift.
norm2() != 0.0)
288 "(%.2f ; %.2f ; %.2f)",
299 pointCloud->addPoint(currentPoint);
324 waveformLoader->loadWaveform(*pointCloud, *laszipPoint);
332 if (field.sf ==
nullptr)
337 field.sf->computeMinAndMax();
338 field.sf->setSaturationStart(field.sf->getMin());
339 field.sf->setSaturationStop(field.sf->getMax());
340 field.sf->setMinDisplayed(field.sf->getMin());
341 field.sf->setMaxDisplayed(field.sf->getMax());
365 auto cMin =
static_cast<int64_t
>(field.sf->getMin());
366 auto cMax =
static_cast<int64_t
>(field.sf->getMax());
367 int64_t steps = std::min<int64_t>(cMax - cMin + 1, 256);
368 field.sf->setColorRampSteps(steps);
379 pointCloud->addScalarField(field.sf);
384 for (
size_t i = 0; i < field.numElements(); ++i)
386 assert(field.scalarFields[i] !=
nullptr);
387 field.scalarFields[i]->computeMinAndMax();
388 field.scalarFields[i]->setSaturationStart(field.scalarFields[i]->getMin());
389 field.scalarFields[i]->setSaturationStop(field.scalarFields[i]->getMax());
390 field.scalarFields[i]->setMinDisplayed(field.scalarFields[i]->getMin());
391 field.scalarFields[i]->setMaxDisplayed(field.scalarFields[i]->getMax());
392 pointCloud->addScalarField(field.scalarFields[i]);
399 pointCloud->setCurrentDisplayedScalarField(idx);
401 else if (pointCloud->getNumberOfScalarFields() > 0)
403 pointCloud->setCurrentDisplayedScalarField(0);
405 pointCloud->showColors(pointCloud->hasColors());
406 pointCloud->showSF(!pointCloud->hasColors() && pointCloud->hasDisplayedScalarField());
410 extraField.resetScalarFieldsPointers();
415 container.
addChild(pointCloud.release());
419 laszip_get_error(laszipHeader, &errorMsg);
423 laszip_close_reader(laszipReader);
424 laszip_clean(laszipReader);
425 laszip_destroy(laszipReader);
428 qint64 elapsed =
timer.elapsed();
429 int32_t minutes =
timer.elapsed() / (1000 * 60);
430 elapsed -= minutes * (1000 * 60);
431 int32_t
seconds = elapsed / 1000;
433 CVLog::Print(QString(
"[LAS] File loaded in %1m%2s%3ms").arg(minutes).arg(
seconds).arg(elapsed));
460 if (!pointCloud->getOwnGlobalBB(bbMin, bbMax))
462 if (pointCloud->size() != 0)
479 CCVector3d globaShift = pointCloud->getGlobalShift();
480 bool hasGlobalShift = pointCloud->isShifted();
485 if (!lasOffsetCanBeUsed)
489 CVLog::Warning(QString(
"[LAS] The former LAS offset (%1 ; %2 ; %3) doesn't seem to be optimal").arg(lasOffset.
x).arg(lasOffset.
y).arg(lasOffset.
z));
492 if (hasGlobalShift && (globalShiftCanBeUsed || !minBBCornerCanBeUsed))
494 CVLog::Warning(
"[LAS] Will use the entity Global Shift as LAS offset");
495 lasOffset = -globaShift;
497 else if (minBBCornerCanBeUsed)
499 CVLog::Warning(
"[LAS] Will use the minimum bounding-box corner (X, Y) as LAS offset");
500 lasOffset.
x = bbMin.
x;
501 lasOffset.
y = bbMin.
y;
514 CCVector3d optimalScale(1.0e-9 * std::max<double>(diag.
x, 1.0),
515 1.0e-9 * std::max<double>(diag.
y, 1.0),
516 1.0e-9 * std::max<double>(diag.
z, 1.0));
520 bool canUseOriginalScale =
false;
522 if (hasScaleMetaData)
525 canUseOriginalScale = (originalScale.
x >= optimalScale.
x
526 && originalScale.
y >= optimalScale.
y
527 && originalScale.
z >= optimalScale.
z);
533 double n =
ceil(log10(maxScale));
534 maxScale = pow(10.0, n);
535 optimalScale.
x = optimalScale.
y = optimalScale.
z = maxScale;
539 if (hasScaleMetaData)
554 bestVersion = savedVersion;
572 if (saveDialog.result() == QDialog::Rejected)
589 params.lasOffset = lasOffset;
595 uint sfCount = pointCloud->getNumberOfScalarFields();
596 for (
uint index = 0; index < sfCount; index++)
599 const char* sfName = sf->
getName();
601 for (
auto& el :
params.standardFields)
602 if (strcmp(sfName, el.name()) == 0)
608 for (
auto& el :
params.extraFields)
609 if (strcmp(sfName, el.scalarFields[0]->getName()) == 0)
616 CVLog::Print(
"[LAS] scalar field " + QString(sfName) +
" will be saved automatically in the extra fields of the output file");
618 const std::string stdName = sfName;
623 CVLog::Warning(
"[LAS] Extra Scalar field name '%s' is too long and will be truncated",
627 field.
type = LasExtraScalarField::DataType::f32;
630 params.extraFields.push_back(field);
644 progressDialog.
setInfo(
"Saving points");
645 QScopedPointer<cloudViewer::NormalizedProgress> normProgress;
649 progressDialog.
start();
652 for (
unsigned i = 0; i < pointCloud->size(); ++i)
660 if (normProgress && !normProgress->oneStep())
677 QString wdpFilename = QString(
"%1/%2.wdp").arg(info.path(), info.baseName());
678 QFile fwfFile(wdpFilename);
680 if (!fwfFile.open(QIODevice::WriteOnly))
689 QDataStream stream(&fwfFile);
692 fwfFile.write(
reinterpret_cast<const char*
>(fwfData->data()), fwfData->size());
693 CVLog::Print(QString(
"[LAS] Successfully saved FWF in external file '%1'").arg(wdpFilename));
Vector3Tpl< double > CCVector3d
Double 3D Vector.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
int64_t CV_CLASS_ENUM
Type of object type flags (64 bits)
CC_FILE_ERROR
Typical I/O filter errors.
@ CC_FERR_CANCELED_BY_USER
@ CC_FERR_THIRD_PARTY_LIB_FAILURE
@ CC_FERR_NOT_IMPLEMENTED
@ CC_FERR_BAD_ENTITY_TYPE
@ CC_FERR_NOT_ENOUGH_MEMORY
static CCVector3d GetGlobalShift(FileIOFilter::LoadParameters ¶meters, bool &preserveCoordinateShift, const CCVector3d &lasOffset, const CCVector3d &firstPoint)
CC_FILE_ERROR TileLasReader(laszip_POINTER laszipReader, const QString &originName, const LasTilingOptions &options)
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Print(const char *format,...)
Prints out a formatted message in console.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
static bool HandleGlobalShift(const CCVector3d &P, CCVector3d &Pshift, bool &preserveCoordinateShift, LoadParameters &loadParameters, bool useInputCoordinatesShiftIfPossible=false)
Shortcut to the ecvGlobalShiftManager mechanism specific for files.
CC_FILE_ERROR loadFile(const QString &fileName, ccHObject &container, LoadParameters ¶meters) override
Loads one or more entities from a file.
bool canSave(CV_CLASS_ENUM type, bool &multiple, bool &exclusive) const override
Returns whether this I/O filter can save the specified type of entity.
CC_FILE_ERROR saveToFile(ccHObject *entity, const QString &filename, const SaveParameters ¶meters) override
Saves an entity (or a group of) to a file.
LasTilingOptions tilingOptions() const
bool shouldSkipDialog() const
void setAvailableScalarFields(const std::vector< LasScalarField > &scalarFields, const std::vector< LasExtraScalarField > &extraScalarFields)
double timeShiftValue() const
@ Tile
The user wants to tile the file into multiple smaller ones.
@ Load
The user wants to load the file in ACloudViewer.
bool shouldForce8bitColors() const
void setInfo(int versionMinor, int pointFormatId, qulonglong numPoints)
void filterOutNotChecked(std::vector< LasScalarField > &scalarFields, std::vector< LasExtraScalarField > &extraScalarFields)
bool shouldIgnoreFieldsWithDefaultValues() const
void resetShouldSkipDialog()
void setExtraScalarFields(const std::vector< LasExtraScalarField > &extraScalarFields)
Set the extra LAS scalar fields saved from the original file.
void setOriginalScale(const CCVector3d &scale, bool canUseScale, bool autoCheck=true)
bool shouldSaveWaveform() const
Returns whether the user wants to save the Waveforms.
void selectedVersion(uint8_t &versionMajor, uint8_t &versionMinor) const
Returns the version currently selected.
bool shouldSaveRGB() const
Returns whether the user wants to save RGB.
std::vector< LasScalarField > fieldsToSave() const
uint8_t selectedPointFormat() const
Returns the point format currently selected.
CCVector3d chosenScale() const
Returns the currently selected scale.
std::vector< LasExtraScalarField > extraFieldsToSave() const
void setVersionAndPointFormat(const LasDetails::LasVersion versionAndFmt)
void setOptimalScale(const CCVector3d &scale, bool autoCheck=false)
Set scale that would offer the user the best precision.
CC_FILE_ERROR open(const QString filePath)
bool savesWaveforms() const
QString getLastError() const
CC_FILE_ERROR saveNextPoint()
CC_FILE_ERROR handleRGBValue(ccPointCloud &pointCloud, const laszip_point ¤tPoint)
const std::vector< LasExtraScalarField > & extraFields() const
CC_FILE_ERROR handleScalarFields(ccPointCloud &pointCloud, const laszip_point ¤tPoint)
CC_FILE_ERROR handleExtraScalarFields(ccPointCloud &pointCloud, const laszip_point ¤tPoint)
void setManualTimeShift(double timeShift)
void setForce8bitRgbMode(bool state)
void setIgnoreFieldsWithDefaultValues(bool state)
const std::vector< LasScalarField > & standardFields() const
Type norm2() const
Returns vector square norm.
static ccColorScale::Shared GetDefaultScale(DEFAULT_SCALES scale=BGYR)
Returns a pre-defined color scale (static shortcut)
Hierarchical CLOUDVIEWER Object.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
bool isA(CV_CLASS_ENUM type) const
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
QSharedPointer< const FWFDataContainer > SharedFWFDataContainer
A scalar field associated to display-related parameters.
bool oneStep()
Increments total progress value of a single unit.
const char * getName() const
Returns scalar field name.
static bool NeedShift(const CCVector3d &P)
Returns whether a particular point (coordinates) is too big or not.
Mode
Strategy to handle coordinates shift/scale.
Graphical progress indicator (thread-safe)
virtual void start() override
virtual void setInfo(const char *infoStr) override
Notifies some information about the ongoing process.
virtual bool isCancelRequested() override
Checks if the process should be canceled.
virtual void setMethodTitle(const char *methodTitle) override
Notifies the algorithm title.
static void error(char *msg)
bool HasRGB(unsigned pointFormatId)
Returns whether the point format supports RGB.
LasVersion SelectBestVersion(const ccPointCloud &cloud)
bool HasWaveform(unsigned pointFormatId)
Returns whether the point format supports Waveforms.
constexpr const char * Intensity
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Generic loading parameters.
ecvGlobalShiftManager::Mode shiftHandlingMode
How to handle big coordinates.
QWidget * parentWidget
Parent widget (if any)
bool alwaysDisplayLoadDialog
bool sessionStart
Session start (whether the load action is the first of a session)
bool * coordinatesShiftEnabled
Whether shift on load has been applied after loading (optional)
Generic saving parameters.
QWidget * parentWidget
Parent widget (if any)
bool alwaysDisplaySaveDialog
static std::vector< LasScalarField > ForPointFormat(unsigned pointFormatId)
@ ExtendedNumberOfReturns
std::vector< LasExtraScalarField > extraScalarFields