| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991 |
- //##########################################################################
- //# #
- //# CLOUDCOMPARE #
- //# #
- //# This program is free software; you can redistribute it and/or modify #
- //# it under the terms of the GNU General Public License as published by #
- //# the Free Software Foundation; version 2 or later of the License. #
- //# #
- //# This program is distributed in the hope that it will be useful, #
- //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
- //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
- //# GNU General Public License for more details. #
- //# #
- //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
- //# #
- //##########################################################################
- #include "ccVolumeCalcTool.h"
- #include "ui_volumeCalcDlg.h"
- //Local
- #include "ccPersistentSettings.h"
- #include "mainwindow.h"
- //qCC_db
- #include <ccPointCloud.h>
- #include <ccProgressDialog.h>
- #include <ccScalarField.h>
- //qCC_gl
- #include <ccGLWindowInterface.h>
- //Qt
- #include <QClipboard>
- #include <QMessageBox>
- #include <QSettings>
- //System
- #include <cassert>
- ccVolumeCalcTool::ccVolumeCalcTool(ccGenericPointCloud* cloud1, ccGenericPointCloud* cloud2, QWidget* parent/*=nullptr*/)
- : QDialog(parent, Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint)
- , cc2Point5DimEditor()
- , m_cloud1(cloud1)
- , m_cloud2(cloud2)
- , m_ui( new Ui::VolumeCalcDialog )
- {
- m_ui->setupUi(this);
- connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &ccVolumeCalcTool::saveSettingsAndAccept);
- connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &ccVolumeCalcTool::reject);
- connect(m_ui->gridStepDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccVolumeCalcTool::updateGridInfo);
- connect(m_ui->gridStepDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccVolumeCalcTool::gridOptionChanged);
- connect(m_ui->groundMaxEdgeLengthDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccVolumeCalcTool::gridOptionChanged);
- connect(m_ui->ceilMaxEdgeLengthDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccVolumeCalcTool::gridOptionChanged);
- connect(m_ui->groundEmptyValueDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccVolumeCalcTool::gridOptionChanged);
- connect(m_ui->ceilEmptyValueDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccVolumeCalcTool::gridOptionChanged);
- connect(m_ui->projDimComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccVolumeCalcTool::projectionDirChanged);
- connect(m_ui->updatePushButton, &QPushButton::clicked, this, &ccVolumeCalcTool::updateGridAndDisplay);
- connect(m_ui->heightProjectionComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccVolumeCalcTool::gridOptionChanged);
- connect(m_ui->fillGroundEmptyCellsComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccVolumeCalcTool::groundFillEmptyCellStrategyChanged);
- connect(m_ui->fillCeilEmptyCellsComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccVolumeCalcTool::ceilFillEmptyCellStrategyChanged);
- connect(m_ui->swapToolButton, &QToolButton::clicked, this, &ccVolumeCalcTool::swapRoles);
- connect(m_ui->groundComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccVolumeCalcTool::groundSourceChanged);
- connect(m_ui->ceilComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccVolumeCalcTool::ceilSourceChanged);
- connect(m_ui->clipboardPushButton, &QPushButton::clicked, this, &ccVolumeCalcTool::exportToClipboard);
- connect(m_ui->exportGridPushButton, &QPushButton::clicked, this, &ccVolumeCalcTool::exportGridAsCloud);
- connect(m_ui->precisionSpinBox, qOverload<int>(&QSpinBox::valueChanged), this, &ccVolumeCalcTool::setDisplayedNumberPrecision);
- if (m_cloud1 && !m_cloud2)
- {
- //the existing cloud is always the second by default
- std::swap(m_cloud1, m_cloud2);
- }
- assert(m_cloud2);
- //custom bbox editor
- ccBBox gridBBox = m_cloud1 ? m_cloud1->getOwnBB() : ccBBox();
- if (m_cloud2)
- {
- gridBBox += m_cloud2->getOwnBB();
- }
- if (gridBBox.isValid())
- {
- createBoundingBoxEditor(gridBBox, this);
- connect(m_ui->editGridToolButton, &QToolButton::clicked, this, &ccVolumeCalcTool::showGridBoxEditor);
- }
- else
- {
- m_ui->editGridToolButton->setEnabled(false);
- }
- m_ui->groundComboBox->addItem("Constant");
- m_ui->ceilComboBox->addItem("Constant");
- if (m_cloud1)
- {
- m_ui->groundComboBox->addItem(m_cloud1->getName());
- m_ui->ceilComboBox->addItem(m_cloud1->getName());
- }
- if (m_cloud2)
- {
- m_ui->groundComboBox->addItem(m_cloud2->getName());
- m_ui->ceilComboBox->addItem(m_cloud2->getName());
- }
- assert(m_ui->groundComboBox->count() >= 2);
- m_ui->groundComboBox->setCurrentIndex(m_ui->groundComboBox->count()-2);
- m_ui->ceilComboBox->setCurrentIndex(m_ui->ceilComboBox->count()-1);
- //add window
- create2DView(m_ui->mapFrame);
- if (m_glWindow)
- {
- ccGui::ParamStruct params = m_glWindow->getDisplayParameters();
- params.colorScaleShowHistogram = false;
- params.displayedNumPrecision = m_ui->precisionSpinBox->value();
- m_glWindow->setDisplayParameters(params, true);
- }
- loadSettings();
- updateGridInfo();
- gridIsUpToDate(false);
- }
- ccVolumeCalcTool::~ccVolumeCalcTool()
- {
- delete m_ui;
- }
- void ccVolumeCalcTool::setDisplayedNumberPrecision(int precision)
- {
- //update window
- if (m_glWindow)
- {
- ccGui::ParamStruct params = m_glWindow->getDisplayParameters();
- params.displayedNumPrecision = precision;
- m_glWindow->setDisplayParameters(params, true);
- m_glWindow->redraw(true, false);
- }
- //update report
- if (m_ui->clipboardPushButton->isEnabled())
- {
- outputReport(m_lastReport);
- }
- }
- void ccVolumeCalcTool::groundSourceChanged(int)
- {
- m_ui->fillGroundEmptyCellsComboBox->setEnabled(m_ui->groundComboBox->currentIndex() > 0);
- groundFillEmptyCellStrategyChanged(-1);
- }
- void ccVolumeCalcTool::ceilSourceChanged(int)
- {
- m_ui->fillCeilEmptyCellsComboBox->setEnabled(m_ui->ceilComboBox->currentIndex() > 0);
- ceilFillEmptyCellStrategyChanged(-1);
- }
- void ccVolumeCalcTool::swapRoles()
- {
- int sourceIndex = m_ui->ceilComboBox->currentIndex();
- int emptyCellStrat = m_ui->fillCeilEmptyCellsComboBox->currentIndex();
- double emptyCellValue = m_ui->ceilEmptyValueDoubleSpinBox->value();
- double maxEdgeLength = m_ui->ceilMaxEdgeLengthDoubleSpinBox->value();
- m_ui->ceilComboBox->setCurrentIndex(m_ui->groundComboBox->currentIndex());
- m_ui->fillCeilEmptyCellsComboBox->setCurrentIndex(m_ui->fillGroundEmptyCellsComboBox->currentIndex());
- m_ui->ceilEmptyValueDoubleSpinBox->setValue(m_ui->groundEmptyValueDoubleSpinBox->value());
- m_ui->ceilEmptyValueDoubleSpinBox->setValue(m_ui->groundMaxEdgeLengthDoubleSpinBox->value());
- m_ui->groundComboBox->setCurrentIndex(sourceIndex);
- m_ui->fillGroundEmptyCellsComboBox->setCurrentIndex(emptyCellStrat);
- m_ui->groundEmptyValueDoubleSpinBox->setValue(emptyCellValue);
- m_ui->groundMaxEdgeLengthDoubleSpinBox->setValue(maxEdgeLength);
-
- gridIsUpToDate(false);
- }
- bool ccVolumeCalcTool::showGridBoxEditor()
- {
- if (cc2Point5DimEditor::showGridBoxEditor())
- {
- updateGridInfo();
- return true;
- }
- return false;
- }
- void ccVolumeCalcTool::updateGridInfo()
- {
- m_ui->gridWidthLabel->setText(getGridSizeAsString());
- }
- double ccVolumeCalcTool::getGridStep() const
- {
- return m_ui->gridStepDoubleSpinBox->value();
- }
- unsigned char ccVolumeCalcTool::getProjectionDimension() const
- {
- int dim = m_ui->projDimComboBox->currentIndex();
- assert(dim >= 0 && dim < 3);
- return static_cast<unsigned char>(dim);
- }
- void ccVolumeCalcTool::sfProjectionTypeChanged(int index)
- {
- Q_UNUSED( index )
-
- gridIsUpToDate(false);
- }
- void ccVolumeCalcTool::projectionDirChanged(int dir)
- {
- Q_UNUSED( dir )
-
- updateGridInfo();
- gridIsUpToDate(false);
- }
- void ccVolumeCalcTool::groundFillEmptyCellStrategyChanged(int)
- {
- ccRasterGrid::EmptyCellFillOption fillEmptyCellsStrategy = getFillEmptyCellsStrategy(m_ui->fillGroundEmptyCellsComboBox);
- m_ui->groundEmptyValueDoubleSpinBox->setEnabled( (m_ui->groundComboBox->currentIndex() == 0)
- || (fillEmptyCellsStrategy == ccRasterGrid::FILL_CUSTOM_HEIGHT) );
- m_ui->groundMaxEdgeLengthDoubleSpinBox->setEnabled(fillEmptyCellsStrategy == ccRasterGrid::INTERPOLATE_DELAUNAY);
- gridIsUpToDate(false);
- }
- void ccVolumeCalcTool::ceilFillEmptyCellStrategyChanged(int)
- {
- ccRasterGrid::EmptyCellFillOption fillEmptyCellsStrategy = getFillEmptyCellsStrategy(m_ui->fillCeilEmptyCellsComboBox);
- m_ui->ceilEmptyValueDoubleSpinBox->setEnabled( (m_ui->ceilComboBox->currentIndex() == 0)
- || (fillEmptyCellsStrategy == ccRasterGrid::FILL_CUSTOM_HEIGHT) );
- m_ui->ceilMaxEdgeLengthDoubleSpinBox->setEnabled(fillEmptyCellsStrategy == ccRasterGrid::INTERPOLATE_DELAUNAY);
- gridIsUpToDate(false);
- }
- void ccVolumeCalcTool::gridOptionChanged()
- {
- gridIsUpToDate(false);
- }
- ccRasterGrid::ProjectionType ccVolumeCalcTool::getTypeOfProjection() const
- {
- switch (m_ui->heightProjectionComboBox->currentIndex())
- {
- case 0:
- return ccRasterGrid::PROJ_MINIMUM_VALUE;
- case 1:
- return ccRasterGrid::PROJ_AVERAGE_VALUE;
- case 2:
- return ccRasterGrid::PROJ_MAXIMUM_VALUE;
- default:
- //shouldn't be possible for this option!
- assert(false);
- }
- return ccRasterGrid::INVALID_PROJECTION_TYPE;
- }
- void ccVolumeCalcTool::loadSettings()
- {
- QSettings settings;
- settings.beginGroup(ccPS::VolumeCalculation());
- int projType = settings.value("ProjectionType", m_ui->heightProjectionComboBox->currentIndex()).toInt();
- int projDim = settings.value("ProjectionDim", m_ui->projDimComboBox->currentIndex()).toInt();
- int groundFillStrategy = settings.value("gFillStrategy", m_ui->fillGroundEmptyCellsComboBox->currentIndex()).toInt();
- int ceilFillStrategy = settings.value("cFillStrategy", m_ui->fillCeilEmptyCellsComboBox->currentIndex()).toInt();
- double step = settings.value("GridStep", m_ui->gridStepDoubleSpinBox->value()).toDouble();
- double groundEmptyHeight = settings.value("gEmptyCellsHeight", m_ui->groundEmptyValueDoubleSpinBox->value()).toDouble();
- double groundMaxEdgeLength = settings.value("gMaxEdgeLength", m_ui->groundMaxEdgeLengthDoubleSpinBox->value()).toDouble();
- double ceilEmptyHeight = settings.value("cEmptyCellsHeight", m_ui->ceilEmptyValueDoubleSpinBox->value()).toDouble();
- double ceilMaxEdgeLength = settings.value("cMaxEdgeLength", m_ui->ceilMaxEdgeLengthDoubleSpinBox->value()).toDouble();
- int precision = settings.value("NumPrecision", m_ui->precisionSpinBox->value()).toInt();
- settings.endGroup();
- m_ui->gridStepDoubleSpinBox->setValue(step);
- m_ui->heightProjectionComboBox->setCurrentIndex(projType);
- m_ui->fillGroundEmptyCellsComboBox->setCurrentIndex(groundFillStrategy);
- m_ui->fillCeilEmptyCellsComboBox->setCurrentIndex(ceilFillStrategy);
- m_ui->groundEmptyValueDoubleSpinBox->setValue(groundEmptyHeight);
- m_ui->groundMaxEdgeLengthDoubleSpinBox->setValue(groundMaxEdgeLength);
- m_ui->ceilEmptyValueDoubleSpinBox->setValue(ceilEmptyHeight);
- m_ui->ceilMaxEdgeLengthDoubleSpinBox->setValue(ceilMaxEdgeLength);
- m_ui->projDimComboBox->setCurrentIndex(projDim);
- m_ui->precisionSpinBox->setValue(precision);
- }
- void ccVolumeCalcTool::saveSettingsAndAccept()
- {
- saveSettings();
- accept();
- }
- void ccVolumeCalcTool::saveSettings()
- {
- QSettings settings;
- settings.beginGroup(ccPS::VolumeCalculation());
- settings.setValue("ProjectionType", m_ui->heightProjectionComboBox->currentIndex());
- settings.setValue("ProjectionDim", m_ui->projDimComboBox->currentIndex());
- settings.setValue("gFillStrategy", m_ui->fillGroundEmptyCellsComboBox->currentIndex());
- settings.setValue("cFillStrategy", m_ui->fillCeilEmptyCellsComboBox->currentIndex());
- settings.setValue("GridStep", m_ui->gridStepDoubleSpinBox->value());
- settings.setValue("gEmptyCellsHeight", m_ui->groundEmptyValueDoubleSpinBox->value());
- settings.setValue("gMaxEdgeLength", m_ui->groundMaxEdgeLengthDoubleSpinBox->value());
- settings.setValue("cEmptyCellsHeight", m_ui->ceilEmptyValueDoubleSpinBox->value());
- settings.setValue("cMaxEdgeLength", m_ui->ceilMaxEdgeLengthDoubleSpinBox->value());
- settings.setValue("NumPrecision", m_ui->precisionSpinBox->value());
- settings.endGroup();
- }
- void ccVolumeCalcTool::gridIsUpToDate(bool state)
- {
- if (state)
- {
- //standard button
- m_ui->updatePushButton->setStyleSheet(QString());
- }
- else
- {
- //red button
- m_ui->updatePushButton->setStyleSheet("color: white; background-color:red;");
- }
- m_ui->updatePushButton->setDisabled(state);
- m_ui->clipboardPushButton->setEnabled(state);
- m_ui->exportGridPushButton->setEnabled(state);
- if (!state)
- {
- m_ui->spareseWarningLabel->hide();
- m_ui->reportPlainTextEdit->setPlainText("Update the grid first");
- }
- }
- ccPointCloud* ccVolumeCalcTool::ConvertGridToCloud( ccRasterGrid& grid,
- const ccBBox& gridBox,
- unsigned char vertDim,
- bool exportToOriginalCS)
- {
- assert(gridBox.isValid());
- assert(vertDim < 3);
- ccPointCloud* rasterCloud = nullptr;
- try
- {
- //we only compute the default 'height' layer
- std::vector<ccRasterGrid::ExportableFields> exportedStatistics(1);
- exportedStatistics.front() = ccRasterGrid::PER_CELL_VALUE;
- rasterCloud = grid.convertToCloud( true,
- false,
- exportedStatistics,
- false,
- false,
- false,
- false,
- nullptr,
- vertDim,
- gridBox,
- 0,
- exportToOriginalCS,
- false);
- if (rasterCloud && rasterCloud->hasScalarFields())
- {
- rasterCloud->showSF(true);
- rasterCloud->setCurrentDisplayedScalarField(0);
- ccScalarField* sf = static_cast<ccScalarField*>(rasterCloud->getScalarField(0));
- assert(sf);
- sf->setName("Relative height");
- sf->setSymmetricalScale(sf->getMin() < 0 && sf->getMax() > 0);
- rasterCloud->showSFColorsScale(true);
- }
- }
- catch (const std::bad_alloc&)
- {
- ccLog::Warning("[ConvertGridToCloud] Not enough memory!");
- if (rasterCloud)
- {
- delete rasterCloud;
- rasterCloud = nullptr;
- }
- }
- return rasterCloud;
- }
- ccPointCloud* ccVolumeCalcTool::convertGridToCloud(bool exportToOriginalCS) const
- {
- ccPointCloud* rasterCloud = nullptr;
- try
- {
- //we only compute the default 'height' layer
- std::vector<ccRasterGrid::ExportableFields> exportedStatistics(1);
- exportedStatistics.front() = ccRasterGrid::PER_CELL_VALUE;
- rasterCloud = cc2Point5DimEditor::convertGridToCloud( true,
- false,
- exportedStatistics,
- false,
- false,
- false,
- false,
- nullptr,
- 0.0,
- exportToOriginalCS,
- false,
- nullptr );
- if (rasterCloud)
- {
- if (rasterCloud->hasScalarFields())
- {
- rasterCloud->showSF(true);
- rasterCloud->setCurrentDisplayedScalarField(0);
- ccScalarField* sf = static_cast<ccScalarField*>(rasterCloud->getScalarField(0));
- assert(sf);
- sf->setName("Relative height");
- sf->setSymmetricalScale(sf->getMin() < 0 && sf->getMax() > 0);
- rasterCloud->showSFColorsScale(true);
- }
- //keep Global Shift & Scale
- auto ground = getGroundCloud();
- auto ceil = getCeilCloud();
- if (ground.first && ground.first->isShifted())
- {
- rasterCloud->copyGlobalShiftAndScale(*ground.first);
- }
- else if (ceil.first && ceil.first->isShifted())
- {
- rasterCloud->copyGlobalShiftAndScale(*ceil.first);
- }
- }
- }
- catch (const std::bad_alloc&)
- {
- ccLog::Error("Not enough memory!");
- if (rasterCloud)
- {
- delete rasterCloud;
- rasterCloud = nullptr;
- }
- }
- return rasterCloud;
- }
- void ccVolumeCalcTool::updateGridAndDisplay()
- {
- bool success = updateGrid();
- if (success && m_glWindow)
- {
- //convert grid to point cloud
- if (m_rasterCloud)
- {
- m_glWindow->removeFromOwnDB(m_rasterCloud);
- delete m_rasterCloud;
- m_rasterCloud = nullptr;
- }
- m_rasterCloud = convertGridToCloud(false);
- if (m_rasterCloud)
- {
- m_glWindow->addToOwnDB(m_rasterCloud);
- ccBBox box = m_rasterCloud->getDisplayBB_recursive(false, m_glWindow);
- update2DDisplayZoom(box);
- }
- else
- {
- ccLog::Error("Not enough memory!");
- m_glWindow->redraw();
- }
- }
- gridIsUpToDate(success);
- }
- QString ccVolumeCalcTool::ReportInfo::toText(int precision) const
- {
- QLocale locale(QLocale::English);
- QStringList reportText;
- reportText << QString("Volume: %1").arg(locale.toString(volume, 'f', precision));
- reportText << QString("Surface: %1").arg(locale.toString(surface, 'f', precision));
- reportText << QString("----------------------");
- reportText << QString("Added volume: (+)%1").arg(locale.toString(addedVolume, 'f', precision));
- reportText << QString("Removed volume: (-)%1").arg(locale.toString(removedVolume, 'f', precision));
- reportText << QString("----------------------");
- reportText << QString("Matching cells: %1%").arg(matchingPrecent, 0, 'f', 1);
- reportText << QString("Non-matching cells:");
- reportText << QString(" ground = %1%").arg(groundNonMatchingPercent, 0, 'f', 1);
- reportText << QString(" ceil = %1%").arg(ceilNonMatchingPercent, 0, 'f', 1);
- reportText << QString("Average neighbors per cell: %1 / 8.0").arg(averageNeighborsPerCell, 0, 'f', 1);
-
- return reportText.join("\n");
- }
- void ccVolumeCalcTool::outputReport(const ReportInfo& info)
- {
- int precision = m_ui->precisionSpinBox->value();
- m_ui->reportPlainTextEdit->setPlainText(info.toText(precision));
- //below 7 neighbors per cell, at least one of the cloud is very sparse!
- m_ui->spareseWarningLabel->setVisible(info.averageNeighborsPerCell < 7.0f);
- m_lastReport = info;
- m_ui->clipboardPushButton->setEnabled(true);
- }
- bool SendError(const QString& message, QWidget* parentWidget)
- {
- if (parentWidget)
- {
- ccLog::Error(message);
- }
- else
- {
- ccLog::Warning("[Volume] " + message);
- }
- return false;
- }
- bool ccVolumeCalcTool::ComputeVolume( ccRasterGrid& grid,
- ccGenericPointCloud* ground,
- ccGenericPointCloud* ceil,
- const ccBBox& gridBox,
- unsigned char vertDim,
- double gridStep,
- unsigned gridWidth,
- unsigned gridHeight,
- ccRasterGrid::ProjectionType projectionType,
- ccRasterGrid::EmptyCellFillOption groundEmptyCellFillStrategy,
- double groundMaxEdgeLength,
- ccRasterGrid::EmptyCellFillOption ceilEmptyCellFillStrategy,
- double ceilMaxEdgeLength,
- ccVolumeCalcTool::ReportInfo& reportInfo,
- double groundHeight = std::numeric_limits<double>::quiet_NaN(),
- double ceilHeight = std::numeric_limits<double>::quiet_NaN(),
- QWidget* parentWidget/*=nullptr*/)
- {
- if ( gridStep <= 1.0e-8
- || gridWidth == 0
- || gridHeight == 0
- || vertDim > 2)
- {
- assert(false);
- ccLog::Warning("[Volume] Invalid input parameters");
- return false;
- }
- if (!ground && !ceil)
- {
- assert(false);
- ccLog::Warning("[Volume] No valid input cloud");
- return false;
- }
- if (!gridBox.isValid())
- {
- ccLog::Warning("[Volume] Invalid bounding-box");
- return false;
- }
- //grid size
- unsigned gridTotalSize = gridWidth * gridHeight;
- if (gridTotalSize == 1)
- {
- if (parentWidget && QMessageBox::question(parentWidget, "Unexpected grid size", "The generated grid will only have 1 cell! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
- return false;
- }
- else if (gridTotalSize > 10000000)
- {
- if (parentWidget && QMessageBox::question(parentWidget, "Big grid size", "The generated grid will have more than 10.000.000 cells! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
- return false;
- }
- //memory allocation
- CCVector3d minCorner = gridBox.minCorner();
- if (!grid.init(gridWidth, gridHeight, gridStep, minCorner))
- {
- //not enough memory
- return SendError("Not enough memory", parentWidget);
- }
- //progress dialog
- QScopedPointer<ccProgressDialog> pDlg(nullptr);
- if (parentWidget)
- {
- pDlg.reset(new ccProgressDialog(true, parentWidget));
- }
- ccRasterGrid groundRaster;
- if (ground)
- {
- if (!groundRaster.init(gridWidth, gridHeight, gridStep, minCorner))
- {
- //not enough memory
- return SendError("Not enough memory", parentWidget);
- }
- ccRasterGrid::InterpolationType interpolationType = ccRasterGrid::InterpolationTypeFromEmptyCellFillOption(groundEmptyCellFillStrategy);
- ccRasterGrid::DelaunayInterpolationParams dInterpParams;
- void* interpolationParams = nullptr;
- switch (interpolationType)
- {
- case ccRasterGrid::InterpolationType::DELAUNAY:
- dInterpParams.maxEdgeLength = groundMaxEdgeLength;
- interpolationParams = (void*)&dInterpParams;
- break;
- case ccRasterGrid::InterpolationType::KRIGING:
- // not supported yet
- assert(false);
- break;
- default:
- // do nothing
- break;
- }
- if (groundRaster.fillWith( ground,
- vertDim,
- projectionType,
- interpolationType,
- interpolationParams,
- ccRasterGrid::INVALID_PROJECTION_TYPE,
- pDlg.data()))
- {
- groundRaster.fillEmptyCells(groundEmptyCellFillStrategy, groundHeight);
- ccLog::Print(QString("[Volume] Ground raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(groundRaster.width).arg(groundRaster.height).arg(groundRaster.minHeight).arg(groundRaster.maxHeight));
- }
- else
- {
- return false;
- }
- }
- //ceil
- ccRasterGrid ceilRaster;
- if (ceil)
- {
- if (!ceilRaster.init(gridWidth, gridHeight, gridStep, minCorner))
- {
- //not enough memory
- return SendError("Not enough memory", parentWidget);
- }
- ccRasterGrid::InterpolationType interpolationType = ccRasterGrid::InterpolationTypeFromEmptyCellFillOption(ceilEmptyCellFillStrategy);
- ccRasterGrid::DelaunayInterpolationParams dInterpParams;
- void* interpolationParams = nullptr;
- switch (interpolationType)
- {
- case ccRasterGrid::InterpolationType::DELAUNAY:
- dInterpParams.maxEdgeLength = ceilMaxEdgeLength;
- interpolationParams = (void*)&dInterpParams;
- break;
- case ccRasterGrid::InterpolationType::KRIGING:
- // not supported yet
- assert(false);
- break;
- default:
- // do nothing
- break;
- }
- if (ceilRaster.fillWith(ceil,
- vertDim,
- projectionType,
- interpolationType,
- interpolationParams,
- ccRasterGrid::INVALID_PROJECTION_TYPE,
- pDlg.data()))
- {
- ceilRaster.fillEmptyCells(ceilEmptyCellFillStrategy, ceilHeight);
- ccLog::Print(QString("[Volume] Ceil raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(ceilRaster.width).arg(ceilRaster.height).arg(ceilRaster.minHeight).arg(ceilRaster.maxHeight));
- }
- else
- {
- return false;
- }
- }
- //update grid and compute volume
- {
- if (pDlg)
- {
- pDlg->setMethodTitle(QObject::tr("Volume computation"));
- pDlg->setInfo(QObject::tr("Cells: %1 x %2").arg(grid.width).arg(grid.height));
- pDlg->start();
- pDlg->show();
- QCoreApplication::processEvents();
- }
- CCCoreLib::NormalizedProgress nProgress(pDlg.data(), grid.width * grid.height);
-
- size_t ceilNonMatchingCount = 0;
- size_t groundNonMatchingCount = 0;
- size_t cellCount = 0;
- //at least one of the grid is based on a cloud
- grid.nonEmptyCellCount = 0;
- for (unsigned i = 0; i < grid.height; ++i)
- {
- for (unsigned j = 0; j < grid.width; ++j)
- {
- ccRasterCell& cell = grid.rows[i][j];
- bool validGround = true;
- cell.minHeight = groundHeight;
- if (ground)
- {
- cell.minHeight = groundRaster.rows[i][j].h;
- validGround = std::isfinite(cell.minHeight);
- }
- bool validCeil = true;
- cell.maxHeight = ceilHeight;
- if (ceil)
- {
- cell.maxHeight = ceilRaster.rows[i][j].h;
- validCeil = std::isfinite(cell.maxHeight);
- }
- if (validGround && validCeil)
- {
- cell.h = cell.maxHeight - cell.minHeight;
- cell.nbPoints = 1;
- reportInfo.volume += cell.h;
- if (cell.h < 0)
- {
- reportInfo.removedVolume -= cell.h;
- }
- else if (cell.h > 0)
- {
- reportInfo.addedVolume += cell.h;
- }
- reportInfo.surface += 1.0;
- ++grid.nonEmptyCellCount; // matching count
- ++cellCount;
- }
- else
- {
- if (validGround)
- {
- ++cellCount;
- ++groundNonMatchingCount;
- }
- else if (validCeil)
- {
- ++cellCount;
- ++ceilNonMatchingCount;
- }
- cell.h = std::numeric_limits<double>::quiet_NaN();
- cell.nbPoints = 0;
- }
- if (pDlg && !nProgress.oneStep())
- {
- ccLog::Warning("[Volume] Process cancelled by the user");
- return false;
- }
- }
- }
- grid.validCellCount = grid.nonEmptyCellCount;
- //count the average number of valid neighbors
- {
- size_t validNeighborsCount = 0;
- size_t count = 0;
- for (unsigned i = 1; i < grid.height - 1; ++i)
- {
- for (unsigned j = 1; j < grid.width - 1; ++j)
- {
- ccRasterCell& cell = grid.rows[i][j];
- if (std::isfinite(cell.h))
- {
- for (unsigned k = i - 1; k <= i + 1; ++k)
- {
- for (unsigned l = j - 1; l <= j + 1; ++l)
- {
- if (k != i || l != j)
- {
- ccRasterCell& otherCell = grid.rows[k][l];
- if (std::isfinite(otherCell.h))
- {
- ++validNeighborsCount;
- }
- }
- }
- }
- ++count;
- }
- }
- }
- if (count)
- {
- reportInfo.averageNeighborsPerCell = static_cast<double>(validNeighborsCount) / count;
- }
- }
- reportInfo.matchingPrecent = static_cast<float>(grid.validCellCount * 100) / cellCount;
- reportInfo.groundNonMatchingPercent = static_cast<float>(groundNonMatchingCount * 100) / cellCount;
- reportInfo.ceilNonMatchingPercent = static_cast<float>(ceilNonMatchingCount * 100) / cellCount;
- float cellArea = static_cast<float>(grid.gridStep * grid.gridStep);
- reportInfo.volume *= cellArea;
- reportInfo.addedVolume *= cellArea;
- reportInfo.removedVolume *= cellArea;
- reportInfo.surface *= cellArea;
- }
- grid.setValid(true);
- return true;
- }
- std::pair<ccGenericPointCloud*, double> ccVolumeCalcTool::getGroundCloud() const
- {
- ccGenericPointCloud* groundCloud = nullptr;
- double groundHeight = std::numeric_limits<double>::quiet_NaN();
- switch (m_ui->groundComboBox->currentIndex())
- {
- case 0:
- groundHeight = m_ui->groundEmptyValueDoubleSpinBox->value();
- break;
- case 1:
- groundCloud = m_cloud1 ? m_cloud1 : m_cloud2;
- break;
- case 2:
- groundCloud = m_cloud2;
- break;
- default:
- assert(false);
- break;
- }
- return { groundCloud, groundHeight };
- }
- std::pair<ccGenericPointCloud*, double> ccVolumeCalcTool::getCeilCloud() const
- {
- ccGenericPointCloud* ceilCloud = nullptr;
- double ceilHeight = std::numeric_limits<double>::quiet_NaN();
- switch (m_ui->ceilComboBox->currentIndex())
- {
- case 0:
- ceilHeight = m_ui->ceilEmptyValueDoubleSpinBox->value();
- break;
- case 1:
- ceilCloud = m_cloud1 ? m_cloud1 : m_cloud2;
- break;
- case 2:
- ceilCloud = m_cloud2;
- break;
- default:
- assert(false);
- break;
- }
- return { ceilCloud, ceilHeight };
- }
- bool ccVolumeCalcTool::updateGrid()
- {
- if (!m_cloud2)
- {
- assert(false);
- return false;
- }
- //cloud bounding-box --> grid size
- ccBBox box = getCustomBBox();
- if (!box.isValid())
- {
- return false;
- }
- unsigned gridWidth = 0;
- unsigned gridHeight = 0;
- if (!getGridSize(gridWidth, gridHeight))
- {
- return false;
- }
- //grid step
- double gridStep = getGridStep();
- assert(gridStep != 0);
- //ground
- auto ground = getGroundCloud();
- if (nullptr == ground.first && std::isnan(ground.second))
- {
- assert(false);
- return false;
- }
- //ceil
- auto ceil = getCeilCloud();
- if (nullptr == ceil.first && std::isnan(ceil.second))
- {
- assert(false);
- return false;
- }
- ccVolumeCalcTool::ReportInfo reportInfo;
- if (ComputeVolume( m_grid,
- ground.first,
- ceil.first,
- box,
- getProjectionDimension(),
- gridStep,
- gridWidth,
- gridHeight,
- getTypeOfProjection(),
- getFillEmptyCellsStrategy(m_ui->fillGroundEmptyCellsComboBox),
- m_ui->groundMaxEdgeLengthDoubleSpinBox->value(),
- getFillEmptyCellsStrategy(m_ui->fillCeilEmptyCellsComboBox),
- m_ui->ceilMaxEdgeLengthDoubleSpinBox->value(),
- reportInfo,
- ground.second,
- ceil.second,
- this))
- {
- outputReport(reportInfo);
- return true;
- }
- else
- {
- return false;
- }
- }
- void ccVolumeCalcTool::exportToClipboard() const
- {
- QClipboard* clipboard = QApplication::clipboard();
- if (clipboard)
- {
- clipboard->setText(m_ui->reportPlainTextEdit->toPlainText());
- }
- }
- void ccVolumeCalcTool::exportGridAsCloud() const
- {
- if (!m_grid.isValid())
- {
- assert(false);
- }
- ccPointCloud* rasterCloud = convertGridToCloud(true);
- if (!rasterCloud)
- {
- //error message should have already been issued
- return;
- }
-
- rasterCloud->setName("Height difference " + rasterCloud->getName());
- ccGenericPointCloud* originCloud = (m_cloud1 ? m_cloud1 : m_cloud2);
- assert(originCloud);
- if (originCloud)
- {
- if (originCloud->getParent())
- {
- originCloud->getParent()->addChild(rasterCloud);
- }
- rasterCloud->setDisplay(originCloud->getDisplay());
- }
- MainWindow* mainWindow = MainWindow::TheInstance();
- if (mainWindow)
- {
- mainWindow->addToDB(rasterCloud);
- ccLog::Print(QString("[Volume] Cloud '%1' successfully exported").arg(rasterCloud->getName()));
- }
- else
- {
- assert(false);
- delete rasterCloud;
- }
- }
|