31 #include <QInputDialog>
32 #include <QSharedPointer>
33 #include <QtConcurrent>
37 #include <pdal/Dimension.hpp>
38 #include <pdal/Filter.hpp>
39 #include <pdal/Options.hpp>
40 #include <pdal/PointTable.hpp>
41 #include <pdal/PointView.hpp>
42 #include <pdal/filters/StreamCallbackFilter.hpp>
43 #include <pdal/io/BufferReader.hpp>
44 #include <pdal/io/LasHeader.hpp>
45 #include <pdal/io/LasReader.hpp>
46 #include <pdal/io/LasVLR.hpp>
47 #include <pdal/io/LasWriter.hpp>
51 using namespace pdal::Dimension;
55 #include <ui_saveLASFileDlg.h>
63 "LAS.spatialReference.nosave";
72 double defaultVal = 0.0,
76 fieldName(SanitizeString(
name)),
80 if (fieldName !=
name) {
81 CVLog::Warning(QString(
"Extra field '%1' renamed '%2' to comply to "
88 typedef QSharedPointer<ExtraLasField>
Shared;
90 inline QString
getName()
const override {
return fieldName; }
99 class LASSaveDlg :
public QDialog,
public Ui::SaveLASFileDialog {
102 : QDialog(parent),
Ui::SaveLASFileDialog() {
108 evlrListWidget->clear();
109 extraFieldGroupBox->setEnabled(
false);
110 extraFieldGroupBox->setChecked(
false);
114 QListWidgetItem* item =
new QListWidgetItem(description);
115 evlrListWidget->addItem(item);
117 item->setSelected(
true);
119 extraFieldGroupBox->setEnabled(
true);
120 extraFieldGroupBox->setChecked(
false);
124 if (!extraFieldGroupBox->isChecked())
return false;
126 QListWidgetItem* item = evlrListWidget->item(
static_cast<int>(index));
127 return item && item->isSelected();
134 QStringList{
"las",
"laz"},
"las",
135 QStringList{
"LAS cloud with PDAL (*.las *.laz)"},
136 QStringList{
"LAS cloud with PDAL (*.las *.laz)"},
141 bool& exclusive)
const {
157 return pdal::Dimension::Id::Y;
159 return pdal::Dimension::Id::Z;
179 return pdal::Dimension::Id::Red;
181 return pdal::Dimension::Id::Green;
183 return pdal::Dimension::Id::Blue;
187 return pdal::Dimension::Id::Unknown;
195 : pdal::Dimension::Id::ClassFlags;
197 return pointFormat < 6
198 ? pdal::Dimension::Id::Unknown
199 : pdal::Dimension::Id::ClassFlags;
204 return pdal::Dimension::Id::Unknown;
216 CVLog::Warning(
"[LAS] This filter can only save one cloud at a time");
220 unsigned numberOfPoints = theCloud->
size();
221 if (numberOfPoints == 0) {
230 std::vector<LasField> fieldsToSave;
232 std::vector<ExtraLasField::Shared> extraFields;
234 uint8_t minPointFormat = 0;
245 QString sfName = QString(sf->
getName()).toUpper();
247 auto name_matches = [&sfName](
const LasField& field) {
248 return sfName == field.
getName().toUpper();
250 auto pos = std::find_if(fieldsToSave.begin(), fieldsToSave.end(),
252 if (pos == fieldsToSave.end()) {
255 extraFields.emplace_back(extraField);
256 extraFields.back()->sf = sf;
261 bool hasClassification =
false;
262 bool hasClassifFlags =
false;
263 for (
const LasField& field : fieldsToSave) {
264 switch (field.type) {
267 hasClassification =
true;
273 if (minPointFormat >= 6)
274 hasClassifFlags =
true;
276 hasClassification =
true;
280 if (minPointFormat >= 6)
281 hasClassifFlags =
true;
293 QScopedPointer<ecvProgressDialog> pDlg(
nullptr);
297 pDlg->setMethodTitle(QObject::tr(
"Save LAS file"));
298 pDlg->setInfo(QObject::tr(
"Points: %L1").arg(numberOfPoints));
305 if (theCloud->
size() != 0) {
317 bool hasScaleMetaData =
false;
320 .toDouble(&hasScaleMetaData);
321 if (hasScaleMetaData) {
323 .toDouble(&hasScaleMetaData);
324 if (hasScaleMetaData) {
327 .toDouble(&hasScaleMetaData);
333 bool hasOffsetMetaData =
false;
336 .toDouble(&hasOffsetMetaData);
337 if (hasOffsetMetaData) {
339 .toDouble(&hasOffsetMetaData);
340 if (hasOffsetMetaData) {
342 .toDouble(&hasOffsetMetaData);
347 if (!hasOffsetMetaData) {
353 hasOffsetMetaData =
true;
359 lasOffset.
x = bbMin.
x;
360 lasOffset.
y = bbMin.
y;
368 "[LAS] The former LAS_OFFSET doesn't seem to be optimal. "
369 "Using the minimum bounding-box corner instead.");
376 "[LAS] Using the minimum bounding-box corner instead.");
377 lasOffset.
x = bbMin.
x;
378 lasOffset.
y = bbMin.
y;
382 "[LAS] Using the previous Global Shift instead.");
383 lasOffset = -globaShift;
396 CCVector3d optimalScale(1.0e-9 * std::max<double>(diag.
x, 1.0),
397 1.0e-9 * std::max<double>(diag.
y, 1.0),
398 1.0e-9 * std::max<double>(diag.
z, 1.0));
400 bool canUseOriginalScale =
false;
401 if (hasScaleMetaData) {
403 canUseOriginalScale = (originalLasScale.
x >= optimalScale.
x &&
404 originalLasScale.
y >= optimalScale.
y &&
405 originalLasScale.
z >= optimalScale.
z);
410 double maxScale =
std::max(optimalScale.
x,
412 double n =
ceil(log10(maxScale));
413 maxScale = pow(10.0, n);
414 optimalScale.
x = optimalScale.
y = optimalScale.
z = maxScale;
418 (canUseOriginalScale ? originalLasScale : optimalScale);
423 s_saveDlg->bestAccuracyLabel->setText(QString(
"(%1, %2, %3)")
426 .arg(optimalScale.
z));
428 if (hasScaleMetaData) {
430 QString(
"(%1, %2, %3)")
431 .arg(originalLasScale.
x)
432 .arg(originalLasScale.
y)
433 .arg(originalLasScale.
z));
435 if (!canUseOriginalScale) {
436 s_saveDlg->labelOriginal->setText(QObject::tr(
437 "Original scale is too small for this cloud "
440 s_saveDlg->labelOriginal->setStyleSheet(
"color: red;");
443 s_saveDlg->origAccuracyLabel->setText(
"none");
446 if (!hasScaleMetaData || !canUseOriginalScale) {
447 if (
s_saveDlg->origRadioButton->isChecked())
448 s_saveDlg->bestRadioButton->setChecked(
true);
449 s_saveDlg->origRadioButton->setEnabled(
false);
455 s_saveDlg->addEVLR(extraField->getName());
460 if (
s_saveDlg->bestRadioButton->isChecked()) {
461 lasScale = optimalScale;
462 }
else if (
s_saveDlg->origRadioButton->isChecked()) {
463 lasScale = originalLasScale;
464 }
else if (
s_saveDlg->customRadioButton->isChecked()) {
465 double s =
s_saveDlg->customScaleDoubleSpinBox->value();
468 }
else if (!hasScaleMetaData) {
469 lasScale = optimalScale;
472 std::vector<ExtraLasField::Shared> extraFieldsToSave;
474 for (
unsigned i = 0; i < extraFields.size(); ++i) {
478 extraFieldsToSave.push_back(extraFields[i]);
481 }
catch (
const std::bad_alloc&) {
493 unsigned previousPointFormat =
495 if (ok && previousPointFormat < 256) {
496 minPointFormat =
std::max(
static_cast<uint8_t
>(previousPointFormat),
504 unsigned ptsWritten = 0;
506 auto convertOne = [&](PointRef&
point) {
507 if (ptsWritten == numberOfPoints)
return false;
509 if (pDlg && pDlg->isCancelRequested()) {
518 point.setField(Id::Y, Pglobal.
y);
519 point.setField(Id::Z, Pglobal.
z);
525 point.setField(Id::Red,
static_cast<uint16_t
>(rgb.
r) << 8);
526 point.setField(Id::Green,
static_cast<uint16_t
>(rgb.
g) << 8);
527 point.setField(Id::Blue,
static_cast<uint16_t
>(rgb.
b) << 8);
531 uint8_t classFlags = 0;
532 uint8_t classification = 0;
533 for (
const LasField& lasField : fieldsToSave) {
535 Id pdalId =
typeToId(lasField.type, minPointFormat);
536 switch (lasField.type) {
546 point.setField(pdalId,
547 lasField.getSafeValue(ptsWritten) +
548 lasField.sf->getGlobalShift());
551 classification =
static_cast<uint8_t
>(
553 lasField.getSafeValue(ptsWritten)) &
557 classification =
static_cast<uint8_t
>(
559 lasField.getSafeValue(ptsWritten)) &
560 (minPointFormat < 6 ? 31 : 255));
563 if (lasField.getSafeValue(ptsWritten) != 0) {
564 if (minPointFormat < 6)
573 if (lasField.getSafeValue(ptsWritten) != 0) {
574 if (minPointFormat < 6)
583 if (lasField.getSafeValue(ptsWritten) != 0) {
584 if (minPointFormat < 6)
585 classification |= 128;
593 if (lasField.getSafeValue(ptsWritten) != 0) {
594 if (minPointFormat >= 6)
604 point.setField(pdalId, lasField.getSafeValue(ptsWritten));
608 if (hasClassification)
610 if (hasClassifFlags)
point.setField(Id::ClassFlags, classFlags);
614 point.setField(extraField->pdalId,
615 extraField->getSafeValue(ptsWritten) +
616 extraField->sf->getGlobalShift());
627 Options writerOptions;
632 writerOptions.add(
"a_srs", wkt.toStdString());
634 writerOptions.add(
"dataformat_id", minPointFormat);
636 writerOptions.add(
"offset_x", lasOffset.
x);
637 writerOptions.add(
"offset_y", lasOffset.
y);
638 writerOptions.add(
"offset_z", lasOffset.
z);
640 writerOptions.add(
"scale_x", lasScale.
x);
641 writerOptions.add(
"scale_y", lasScale.
y);
642 writerOptions.add(
"scale_z", lasScale.
z);
644 writerOptions.add(
"filename",
filename.toStdString());
645 writerOptions.add(
"extra_dims",
"all");
649 unsigned global_encoding =
653 writerOptions.add(
"global_encoding", global_encoding);
660 writerOptions.add(
"project_id", uuid.toStdString());
670 writerOptions.add(
"minor_version", minor_version);
675 StreamCallbackFilter f;
676 f.setCallback(convertOne);
678 writer.setOptions(writerOptions);
681 point_count_t tableSize = 3;
682 if (hasColors) tableSize += 3;
683 tableSize += fieldsToSave.size();
684 tableSize += extraFieldsToSave.size();
686 FixedPointTable table(tableSize);
688 table.layout()->registerDim(
Id::X);
689 table.layout()->registerDim(Id::Y);
690 table.layout()->registerDim(Id::Z);
693 table.layout()->registerDim(Id::Red);
694 table.layout()->registerDim(Id::Green);
695 table.layout()->registerDim(Id::Blue);
698 for (
const LasField& lasField : fieldsToSave) {
699 std::string dimName = lasField.getName().toStdString();
700 Id pdalId = id(dimName);
701 table.layout()->registerDim(pdalId);
705 std::string dimName = extraField->getName().toStdString();
708 Type t = Type::Double;
710 table.layout()->registerOrAssignDim(dimName, t);
713 writer.prepare(table);
714 writer.execute(table);
715 }
catch (
const pdal::pdal_error& p) {
716 CVLog::Error(QString(
"PDAL exception: %1").arg(p.what()));
718 }
catch (
const std::exception& e) {
719 CVLog::Error(QString(
"PDAL generic exception: %1").arg(e.what()));
725 return callbackError;
742 const QString& absoluteBaseFilename,
746 const LasHeader& header) {
750 X = (
Z == 2 ? 0 :
Z + 1);
751 Y = (
X == 2 ? 0 :
X + 1);
762 }
catch (
const std::bad_alloc&) {
771 QString ext = (header.compressed() ?
"laz" :
"las");
773 for (
unsigned i = 0; i <
width; ++i) {
774 for (
unsigned j = 0; j <
height; ++j) {
775 unsigned ii =
index(i, j);
776 QString
filename = absoluteBaseFilename +
778 .arg(QString::number(i),
779 QString::number(j), ext);
789 void addPoint(
const PointViewPtr& buffer,
unsigned pointIndex) {
793 buffer->getFieldAs<
double>(Id::Y, pointIndex),
794 buffer->getFieldAs<
double>(Id::Z, pointIndex));
801 outputView->appendPoint(*buffer, pointIndex);
807 Options writerOptions;
809 BufferReader bufferReader;
811 writerOptions.add(
"filename",
fileNames[i].toStdString());
815 writer.setInput(bufferReader);
816 writer.setOptions(writerOptions);
817 writer.prepare(table);
818 writer.execute(table);
819 }
catch (
const pdal_error& e) {
820 CVLog::Error(QString(
"PDAL exception '%1'").arg(e.what()));
826 inline unsigned index(
unsigned i,
unsigned j)
const {
return i + j *
w; }
918 for (
size_t i = 0; i < extraNamesToLoad.size(); ++i) {
919 QString
name = QString::fromStdString(extraNamesToLoad[i]);
931 if (field && field->sf) {
932 field->sf->computeMinAndMax();
942 int cMin =
static_cast<int>(field->sf->getMin());
943 int cMax =
static_cast<int>(field->sf->getMax());
944 field->sf->setColorRampSteps(
945 std::min<int>(cMax - cMin + 1, 256));
949 field->sf->setColorScale(
959 field->sf->release();
963 QString(
"[LAS] All '%1' values were the same (%2)! We "
969 .arg(field->firstValue));
987 las_opts.add(
"filename",
filename.toStdString());
988 las_opts.add(
"use_eb_vlr",
true);
990 lasReader.setOptions(las_opts);
991 FixedPointTable
fields(100);
992 PointLayoutPtr layout(
fields.layout());
993 if (
nullptr == layout) {
997 lasReader.prepare(
fields);
998 LasHeader lasHeader = lasReader.header();
1000 unsigned nbOfPoints =
static_cast<unsigned>(lasHeader.pointCount());
1001 if (nbOfPoints == 0) {
1008 CCVector3d bbMin(lasHeader.minX(), lasHeader.minY(), lasHeader.minZ());
1009 CCVector3d bbMax(lasHeader.maxX(), lasHeader.maxY(), lasHeader.maxZ());
1010 CCVector3d lasScale(lasHeader.scaleX(), lasHeader.scaleY(),
1011 lasHeader.scaleZ());
1012 CCVector3d lasOffset(lasHeader.offsetX(), lasHeader.offsetY(),
1013 lasHeader.offsetZ());
1015 const uint8_t pointFormat = lasHeader.pointFormat();
1016 CVLog::Print(
"[LAS] Point format: " + QString::number(pointFormat));
1022 QuickInfo file_info = lasReader.preview();
1026 if (pointFormat <= 5) {
1027 s_lasOpenDlg->classifOverlapCheckBox->setEnabled(
false);
1028 s_lasOpenDlg->classifOverlapCheckBox->setVisible(
false);
1031 std::vector<ExtraDimDescriptor> extraDims;
1032 if (
nullptr != layout) {
1033 for (
const pdal::Dimension::Id pdalId : layout->dims()) {
1045 case pdal::Dimension::Id::ScanChannel:
1046 case pdal::Dimension::Id::Infrared:
1047 case pdal::Dimension::Id::ClassFlags:
1048 case pdal::Dimension::Id::PointId:
1050 case pdal::Dimension::Id::Y:
1051 case pdal::Dimension::Id::Z:
1052 case pdal::Dimension::Id::Red:
1053 case pdal::Dimension::Id::Green:
1054 case pdal::Dimension::Id::Blue:
1060 dim.
name = layout->dimName(pdalId);
1061 dim.
dimType = layout->dimType(pdalId);
1062 extraDims.push_back(dim);
1065 QString::fromStdString(dim.
name));
1070 extraDims.shrink_to_fit();
1085 bool ignoreDefaultFields =
1086 s_lasOpenDlg->ignoreDefaultFieldsCheckBox->isChecked();
1091 bool loadColor = (loadRGBComponent[0] || loadRGBComponent[1] ||
1092 loadRGBComponent[2]);
1097 unsigned char colorCompBitShift = 0;
1098 bool forced8bitRgbMode =
s_lasOpenDlg->forced8bitRgbMode();
1102 std::string extraDimsArg;
1103 for (
unsigned i = 0; i < extraDims.size(); ++i) {
1105 extraDimsArg += extraDims[i].name +
"=" +
1106 interpretationName(extraDims[i].dimType) +
",";
1107 extraNamesToLoad.push_back(extraDims[i].
name);
1111 if (!extraNamesToLoad.empty()) {
1115 las_opts2.add(
"extra_dims", extraDimsArg);
1117 lasReader.addOptions(las_opts2);
1118 lasReader.prepare(
fields);
1121 std::vector<Id> extraDimensionsIds;
1122 for (std::string& dim : extraNamesToLoad) {
1123 extraDimensionsIds.push_back(layout->findDim(dim));
1128 QScopedPointer<ecvProgressDialog> pDlg(
nullptr);
1132 pDlg->setMethodTitle(QObject::tr(
"Open LAS file"));
1133 pDlg->setInfo(QObject::tr(
"Points: %L1").arg(nbOfPoints));
1140 PointViewSet pointViewSet;
1143 unsigned vertDim = 2;
1144 switch (
s_lasOpenDlg->tileDimComboBox->currentIndex()) {
1159 auto w =
static_cast<unsigned>(
s_lasOpenDlg->wTileSpinBox->value());
1160 auto h =
static_cast<unsigned>(
s_lasOpenDlg->hTileSpinBox->value());
1162 QString outputBaseName =
s_lasOpenDlg->outputPathLineEdit->text() +
1163 "/" + QFileInfo(
filename).baseName();
1164 if (!tiler.
init(w, h, vertDim, outputBaseName, bbMin, bbMax, table,
1169 auto prepareAndExecute = [&lasReader, &table]() -> PointViewSet {
1170 lasReader.prepare(table);
1171 lasReader.prepare(table);
1172 return lasReader.execute(table);
1178 pDlg->setMethodTitle(QObject::tr(
"LAS file"));
1180 QObject::tr(
"Please wait... reading in progress"));
1181 pDlg->setRange(0, 0);
1182 pDlg->setModal(
true);
1186 QFutureWatcher<PointViewSet> reader;
1187 QObject::connect(&reader, SIGNAL(finished()), pDlg.data(),
1189 reader.setFuture(QtConcurrent::run(prepareAndExecute));
1194 reader.waitForFinished();
1196 PointViewSet viewSet = reader.result();
1197 PointViewPtr pointView = *viewSet.begin();
1202 pDlg->setMethodTitle(QObject::tr(
"Tiling points"));
1203 pDlg->setInfo(QObject::tr(
"Points: %L1").arg(nbOfPoints));
1208 for (PointId idx = 0; idx < pointView->size(); ++idx) {
1218 pDlg->setMethodTitle(QObject::tr(
"LAS file"));
1220 QObject::tr(
"Please wait... writing in progress"));
1221 pDlg->setRange(0, 0);
1222 pDlg->setModal(
true);
1226 QFutureWatcher<void> writer;
1227 QObject::connect(&writer, SIGNAL(finished()), pDlg.data(),
1230 QtConcurrent::run([&tiler]() { tiler.
writeAll(); }));
1233 writer.waitForFinished();
1240 bool preserveCoordinateShift =
true;
1243 unsigned nbPointsRead = 0;
1245 StreamCallbackFilter f;
1246 f.setInput(lasReader);
1248 unsigned nbOfChunks =
1250 std::vector<LasCloudChunk> chunks(nbOfChunks,
LasCloudChunk());
1253 auto ccProcessOne = [&](PointRef&
point) {
1254 if (pDlg && pDlg->isCancelRequested()) {
1264 unsigned pointsToRead = nbOfPoints - nbPointsRead;
1273 if (preserveCoordinateShift) {
1278 SpatialReference srs = lasHeader.srs();
1280 QString wkt = QString::fromStdString(srs.getWKT());
1292 std::vector<LasField::Shared>& fieldsToLoad = pointChunk.
lasFields;
1295 if (nbPointsRead == 0) {
1299 point.getFieldAs<
int>(Id::Y)),
1301 point.getFieldAs<
int>(Id::Z)));
1306 bool useLasOffset =
false;
1309 if (lasOffsetXY.
norm2() != 0 &&
1317 useLasOffset =
true;
1318 Pshift = -lasOffsetXY;
1329 parameters, useLasOffset)) {
1330 if (preserveCoordinateShift) {
1334 "[LAS] Cloud has been recentered! Translation: "
1335 "(%.2f ; %.2f ; %.2f)",
1336 Pshift.
x, Pshift.
y, Pshift.
z);
1346 point.getFieldAs<
double>(Id::Y) + Pshift.
y),
1348 point.getFieldAs<
double>(Id::Z) + Pshift.
z));
1352 uint16_t
red = loadRGBComponent[0]
1353 ?
point.getFieldAs<uint16_t>(Id::Red)
1355 uint16_t
green = loadRGBComponent[1]
1356 ?
point.getFieldAs<uint16_t>(Id::Green)
1358 uint16_t
blue = loadRGBComponent[2]
1359 ?
point.getFieldAs<uint16_t>(Id::Blue)
1364 bool pushColor =
true;
1370 for (
unsigned i = 0; i + 1 < loadedCloud->
size();
1376 "[LAS]: Not enough memory, color field "
1377 "will be ignored!");
1391 if (!forced8bitRgbMode && colorCompBitShift == 0) {
1392 if ((
red & 0xFF00) || (
green & 0xFF00) ||
1396 "[LAS] Color components are coded on 16 "
1398 colorCompBitShift = 8;
1400 for (
unsigned i = 0; i + 1 < loadedCloud->
size();
1421 Id pdalId =
typeToId(field->type, pointFormat);
1423 switch (field->type) {
1427 value =
point.getFieldAs<
double>(extraField->
pdalId);
1435 value -= field->sf->getGlobalShift();
1439 if (pointFormat <= 5)
1440 value = (
point.getFieldAs<
int>(pdalId) &
1444 value =
point.getFieldAs<
int>(pdalId);
1447 if (pointFormat <= 5)
1448 value = (
point.getFieldAs<
int>(pdalId) & 32)
1453 value = (
point.getFieldAs<
int>(pdalId) & 1)
1460 if (pointFormat <= 5)
1461 value = (
point.getFieldAs<
int>(pdalId) & 64)
1466 value = (
point.getFieldAs<
int>(pdalId) & 2)
1473 if (pointFormat <= 5)
1474 value = (
point.getFieldAs<
int>(pdalId) & 128)
1479 value = (
point.getFieldAs<
int>(pdalId) & 4)
1486 if (pointFormat <= 5) {
1489 value = (
point.getFieldAs<
int>(pdalId) & 8)
1497 value =
point.getFieldAs<
double>(pdalId);
1501 auto s =
static_cast<ScalarType
>(value);
1502 field->sf->addElement(s);
1505 if (loadedCloud->
size() == 1) {
1506 field->firstValue = value;
1508 if (!ignoreDefaultFields || value != field->firstValue ||
1509 (field->firstValue != field->defaultValue &&
1510 field->firstValue >= field->minValue)) {
1516 double timeShift = 0.0;
1520 timeShift =
static_cast<int64_t
>(
1525 field->sf->setGlobalShift(timeShift);
1527 if (value < 1.0e5) {
1529 "[LAS] Time SF has been shifted to "
1530 "prevent a loss of accuracy (%.2f)",
1532 }
else if (timeShift > 0.0) {
1534 "[LAS] Time SF has been shifted "
1535 "but accuracy may not be preserved "
1540 "[LAS] Time SF has not been "
1541 "shifted. Accuracy may not be "
1544 field->firstValue = value;
1547 auto defaultValue =
static_cast<ScalarType
>(
1548 field->defaultValue);
1549 for (
unsigned i = 1; i < loadedCloud->
size(); ++i) {
1550 field->sf->emplace_back(defaultValue);
1552 auto s =
static_cast<ScalarType
>(value);
1553 field->sf->emplace_back(s);
1556 QString(
"[LAS] Not enough memory: '%1' "
1557 "field will be ignored!")
1559 field->sf->release();
1560 field->sf =
nullptr;
1570 f.setCallback(ccProcessOne);
1575 return callbackError;
1578 for (
auto& chunk : chunks) {
1579 chunk.addLasFieldsToCloud();
1583 if (loadedCloud->
size()) {
1584 bool thisChunkHasColors = chunk.
hasColors();
1586 if (loadColor && !thisChunkHasColors) {
1588 "[LAS] Color field was all black! We ignored "
1597 QString chunkName(
"unnamed - Cloud");
1604 chunkName += QString(
" #%1").arg(n + 1);
1606 loadedCloud->
setName(chunkName);
1609 QVariant(lasScale.
x));
1611 QVariant(lasScale.
y));
1613 QVariant(lasScale.
z));
1615 QVariant(lasOffset.
x));
1617 QVariant(lasOffset.
y));
1619 QVariant(lasOffset.
z));
1622 QVariant(lasHeader.globalEncoding()));
1624 const pdal::Uuid projectUUID = lasHeader.projectId();
1625 if (!projectUUID.isNull()) {
1628 QVariant(QString::fromStdString(
1629 projectUUID.toString())));
1634 QVariant(lasHeader.versionMajor()));
1637 QVariant(lasHeader.versionMinor()));
1639 QVariant(pointFormat));
1642 loadedCloud =
nullptr;
1646 loadedCloud =
nullptr;
1650 }
catch (
const pdal::pdal_error& p) {
1651 CVLog::Error(QString(
"PDAL exception: %1").arg(p.what()));
1653 }
catch (
const std::exception& e) {
1654 CVLog::Error(QString(
"PDAL generic exception: %1").arg(e.what()));
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)
std::vector< PCLPointField > fields
CC_FILE_ERROR
Typical I/O filter errors.
@ CC_FERR_CANCELED_BY_USER
@ CC_FERR_THIRD_PARTY_LIB_FAILURE
@ CC_FERR_THIRD_PARTY_LIB_EXCEPTION
@ CC_FERR_BAD_ENTITY_TYPE
@ CC_FERR_NOT_ENOUGH_MEMORY
static const char LAS_VERSION_MAJOR_META_DATA[]
static const char LAS_OFFSET_X_META_DATA[]
static const char LAS_SCALE_X_META_DATA[]
const char LAS_FIELD_NAMES[][28]
static const char LAS_OFFSET_Y_META_DATA[]
static const char LAS_PROJECT_UUID_META_DATA[]
static const char LAS_SCALE_Z_META_DATA[]
static const char LAS_SCALE_Y_META_DATA[]
static const char LAS_OFFSET_Z_META_DATA[]
static const char LAS_POINT_FORMAT_META_DATA[]
static const char LAS_GLOBAL_ENCODING_META_DATA[]
static const char LAS_VERSION_MINOR_META_DATA[]
QSharedPointer< LASOpenDlg > s_lasOpenDlg(nullptr)
QSharedPointer< LASSaveDlg > s_saveDlg(nullptr)
Semi persistent save dialog.
pdal::Dimension::Id typeToId(LAS_FIELDS sfType, uint8_t pointFormat)
static const char s_LAS_SRS_Key[]
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 constexpr float DEFAULT_PRIORITY
static bool HandleGlobalShift(const CCVector3d &P, CCVector3d &Pshift, bool &preserveCoordinateShift, LoadParameters &loadParameters, bool useInputCoordinatesShiftIfPossible=false)
Shortcut to the ecvGlobalShiftManager mechanism specific for files.
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.
CC_FILE_ERROR loadFile(const QString &filename, ccHObject &container, LoadParameters ¶meters) override
Loads one or more entities from a file.
Dialog to choose the LAS fields to load.
LASSaveDlg(QWidget *parent=nullptr)
void addEVLR(const QString &description)
bool doSaveEVLR(size_t index) const
Class describing the current tiling process.
std::vector< PointViewPtr > tilePointViews
std::vector< QString > fileNames
void addPoint(const PointViewPtr &buffer, unsigned pointIndex)
bool init(unsigned width, unsigned height, unsigned Zdim, const QString &absoluteBaseFilename, const CCVector3d &bbMin, const CCVector3d &bbMax, PointTableRef table, const LasHeader &header)
unsigned index(unsigned i, unsigned j) const
Type norm2() const
Returns vector square norm.
static ccColorScale::Shared GetDefaultScale(DEFAULT_SCALES scale=BGYR)
Returns a pre-defined color scale (static shortcut)
virtual bool hasColors() const
Returns whether colors are enabled or not.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual const ecvColor::Rgb & getPointColor(unsigned pointIndex) const =0
Returns color corresponding to a given point.
static ccGenericPointCloud * ToGenericPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccGenericPointCloud.
Hierarchical CLOUDVIEWER Object.
unsigned getChildrenNumber() const
Returns the number of children.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
void setMetaData(const QString &key, const QVariant &data)
Sets a meta-data element.
bool isA(CV_CLASS_ENUM type) const
QVariant getMetaData(const QString &key) const
Returns a given associated meta data.
bool hasMetaData(const QString &key) const
Returns whether a meta-data element with the given key exists or not.
virtual void setName(const QString &name)
Sets object name.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool hasDisplayedScalarField() const override
Returns whether an active scalar field is available or not.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
bool hasColors() const override
Returns whether colors are enabled or not.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
void setPointColor(size_t pointIndex, const ecvColor::Rgb &col)
Sets a particular point color.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
bool reserveThePointsTable(unsigned _numberOfPoints)
Reserves memory to store the points coordinates.
A scalar field associated to display-related parameters.
bool getOwnGlobalBB(CCVector3d &minCorner, CCVector3d &maxCorner) override
CCVector3d toGlobal3d(const Vector3Tpl< T > &Plocal) const
Returns the point back-projected into the original coordinates system.
bool isShifted() const
Returns whether the cloud is shifted or not.
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual unsigned size() const =0
Returns the number of points.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
bool oneStep()
Increments total progress value of a single unit.
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.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
unsigned size() const override
unsigned capacity() const
Returns cloud capacity (i.e. reserved size)
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)
unsigned char ColorCompType
Default color components type (R,G and B)
Q_DECLARE_METATYPE(ccPclPluginInterface *)
const unsigned CC_MAX_NUMBER_OF_POINTS_PER_CLOUD
constexpr const char * ScanDirectionFlag
constexpr const char * EdgeOfFlightLine
constexpr const char * NumberOfReturns
constexpr const char * UserData
constexpr const char * Classification
constexpr const char * ReturnNumber
constexpr const char * ScanAngleRank
constexpr const char * GpsTime
constexpr const char * Intensity
constexpr const char * PointSourceId
MiniVec< float, N > floor(const MiniVec< float, N > &a)
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
std::vector< std::string > StringList
constexpr Rgb black(0, 0, 0)
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb blue(0, 0, MAX)
constexpr Rgb green(0, MAX, 0)
#define fileChunkSize(nChunkSize)
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
void createFieldsToLoad(const IdList &extraFieldsToLoad, const StringList &extraNamesToLoad)
ccPointCloud * getLoadedCloud() const
std::vector< LasField::Shared > lasFields
bool reserveSize(unsigned nbPoints)
void addLasFieldsToCloud()
ccPointCloud * loadedCloud
static QString DesanitizeString(const QString &str)
static bool GetLASFields(ccPointCloud *cloud, std::vector< LasField > &fieldsToSave, uint8_t &minPointFormat)
Returns the (compliant) LAS fields in a point cloud.
static uint8_t UpdateMinPointFormat(uint8_t minPointFormat, bool withRGB, bool withFWF, bool allowLegacyFormats=true)
QSharedPointer< LasField > Shared
Shared type.