ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvVolumeCalcTool.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 "ecvVolumeCalcTool.h"
9 
10 // Local
11 #include "MainWindow.h"
13 #include "ecvCommon.h"
14 #include "ecvIsolines.h"
15 #include "ecvPersistentSettings.h"
16 
17 // CV_DB_LIB
18 #include <ecvDisplayTools.h>
19 #include <ecvGenericPointCloud.h>
20 #include <ecvMesh.h>
21 #include <ecvPointCloud.h>
22 #include <ecvPolyline.h>
23 #include <ecvProgressDialog.h>
24 #include <ecvScalarField.h>
25 
26 // CV_CORE_LIB
27 #include <Delaunay2dMesh.h>
28 #include <PointProjectionTools.h>
29 
30 // Qt
31 #include <QApplication>
32 #include <QClipboard>
33 #include <QComboBox>
34 #include <QLocale>
35 #include <QMessageBox>
36 #include <QPushButton>
37 #include <QSettings>
38 
39 // System
40 #include <assert.h>
41 
43  ccGenericPointCloud* cloud2,
44  QWidget* parent /*=0*/)
45  : QDialog(parent, Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
47  Ui::VolumeCalcDialog(),
48  m_cloud1(cloud1),
49  m_cloud2(cloud2) {
50  setupUi(this);
51 
52  connect(buttonBox, &QDialogButtonBox::accepted, this,
54  connect(buttonBox, &QDialogButtonBox::rejected, this,
55  &ccVolumeCalcTool::reject);
56  connect(gridStepDoubleSpinBox,
57  static_cast<void (QDoubleSpinBox::*)(double)>(
58  &QDoubleSpinBox::valueChanged),
60  connect(gridStepDoubleSpinBox,
61  static_cast<void (QDoubleSpinBox::*)(double)>(
62  &QDoubleSpinBox::valueChanged),
64  connect(groundEmptyValueDoubleSpinBox,
65  static_cast<void (QDoubleSpinBox::*)(double)>(
66  &QDoubleSpinBox::valueChanged),
68  connect(ceilEmptyValueDoubleSpinBox,
69  static_cast<void (QDoubleSpinBox::*)(double)>(
70  &QDoubleSpinBox::valueChanged),
72  connect(projDimComboBox,
73  static_cast<void (QComboBox::*)(int)>(
74  &QComboBox::currentIndexChanged),
76  connect(updatePushButton, &QPushButton::clicked, this,
78  connect(heightProjectionComboBox,
79  static_cast<void (QComboBox::*)(int)>(
80  &QComboBox::currentIndexChanged),
82  connect(fillGroundEmptyCellsComboBox,
83  static_cast<void (QComboBox::*)(int)>(
84  &QComboBox::currentIndexChanged),
86  connect(fillCeilEmptyCellsComboBox,
87  static_cast<void (QComboBox::*)(int)>(
88  &QComboBox::currentIndexChanged),
90  connect(swapToolButton, &QToolButton::clicked, this,
92  connect(groundComboBox,
93  static_cast<void (QComboBox::*)(int)>(
94  &QComboBox::currentIndexChanged),
96  connect(ceilComboBox,
97  static_cast<void (QComboBox::*)(int)>(
98  &QComboBox::currentIndexChanged),
100  connect(clipboardPushButton, &QPushButton::clicked, this,
102  connect(exportGridPushButton, &QPushButton::clicked, this,
104  connect(precisionSpinBox,
105  static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
107 
108  if (m_cloud1 && !m_cloud2) {
109  // the existing cloud is always the second by default
111  }
112  assert(m_cloud2);
113 
114  // custom bbox editor
115  ccBBox gridBBox = m_cloud1 ? m_cloud1->getOwnBB() : ccBBox();
116  if (m_cloud2) {
117  gridBBox += m_cloud2->getOwnBB();
118  }
119  if (gridBBox.isValid()) {
120  createBoundingBoxEditor(gridBBox, this);
121  connect(editGridToolButton, &QToolButton::clicked, this,
123  } else {
124  editGridToolButton->setEnabled(false);
125  }
126 
127  groundComboBox->addItem("Constant");
128  ceilComboBox->addItem("Constant");
129  if (m_cloud1) {
130  groundComboBox->addItem(m_cloud1->getName());
131  ceilComboBox->addItem(m_cloud1->getName());
132  }
133  if (m_cloud2) {
134  groundComboBox->addItem(m_cloud2->getName());
135  ceilComboBox->addItem(m_cloud2->getName());
136  }
137  assert(groundComboBox->count() >= 2);
138  groundComboBox->setCurrentIndex(groundComboBox->count() - 2);
139  ceilComboBox->setCurrentIndex(ceilComboBox->count() - 1);
140 
141  // add window
142  create2DView(mapFrame);
145  params.colorScaleShowHistogram = false;
146  params.displayedNumPrecision = precisionSpinBox->value();
148  }
149 
150  loadSettings();
151 
152  updateGridInfo();
153 
154  gridIsUpToDate(false);
155 }
156 
158 
160  // update window
163  params.displayedNumPrecision = precision;
166  }
167 
168  // update report
169  if (clipboardPushButton->isEnabled()) {
171  }
172 }
173 
175  fillGroundEmptyCellsComboBox->setEnabled(groundComboBox->currentIndex() >
176  0);
178 }
179 
181  fillCeilEmptyCellsComboBox->setEnabled(ceilComboBox->currentIndex() > 0);
183 }
184 
186  int sourceIndex = ceilComboBox->currentIndex();
187  int emptyCellStrat = fillCeilEmptyCellsComboBox->currentIndex();
188  double emptyCellValue = ceilEmptyValueDoubleSpinBox->value();
189 
190  ceilComboBox->setCurrentIndex(groundComboBox->currentIndex());
191  fillCeilEmptyCellsComboBox->setCurrentIndex(
192  fillGroundEmptyCellsComboBox->currentIndex());
193  ceilEmptyValueDoubleSpinBox->setValue(
194  groundEmptyValueDoubleSpinBox->value());
195 
196  groundComboBox->setCurrentIndex(sourceIndex);
197  fillGroundEmptyCellsComboBox->setCurrentIndex(emptyCellStrat);
198  groundEmptyValueDoubleSpinBox->setValue(emptyCellValue);
199 
200  gridIsUpToDate(false);
201 }
202 
205  updateGridInfo();
206  return true;
207  }
208 
209  return false;
210 }
211 
213  gridWidthLabel->setText(getGridSizeAsString());
214 }
215 
217  return gridStepDoubleSpinBox->value();
218 }
219 
221  int dim = projDimComboBox->currentIndex();
222  assert(dim >= 0 && dim < 3);
223 
224  return static_cast<unsigned char>(dim);
225 }
226 
228  gridIsUpToDate(false);
229 }
230 
232  updateGridInfo();
233  gridIsUpToDate(false);
234 }
235 
237  ccRasterGrid::EmptyCellFillOption fillEmptyCellsStrategy =
238  getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox);
239 
240  groundEmptyValueDoubleSpinBox->setEnabled(
241  groundComboBox->currentIndex() == 0 ||
242  fillEmptyCellsStrategy == ccRasterGrid::FILL_CUSTOM_HEIGHT);
243  gridIsUpToDate(false);
244 }
245 
247  ccRasterGrid::EmptyCellFillOption fillEmptyCellsStrategy =
248  getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox);
249 
250  ceilEmptyValueDoubleSpinBox->setEnabled(
251  ceilComboBox->currentIndex() == 0 ||
252  fillEmptyCellsStrategy == ccRasterGrid::FILL_CUSTOM_HEIGHT);
253  gridIsUpToDate(false);
254 }
255 
257 
259  switch (heightProjectionComboBox->currentIndex()) {
260  case 0:
262  case 1:
264  case 2:
266  default:
267  // shouldn't be possible for this option!
268  assert(false);
269  }
270 
272 }
273 
275  QSettings settings;
276  settings.beginGroup(ecvPS::VolumeCalculation());
277  int projType = settings.value("ProjectionType",
278  heightProjectionComboBox->currentIndex())
279  .toInt();
280  int projDim =
281  settings.value("ProjectionDim", projDimComboBox->currentIndex())
282  .toInt();
283  int groundFillStrategy =
284  settings.value("gFillStrategy",
285  fillGroundEmptyCellsComboBox->currentIndex())
286  .toInt();
287  int ceilFillStrategy =
288  settings.value("cFillStrategy",
289  fillCeilEmptyCellsComboBox->currentIndex())
290  .toInt();
291  double step = settings.value("GridStep", gridStepDoubleSpinBox->value())
292  .toDouble();
293  double groundEmptyHeight =
294  settings.value("gEmptyCellsHeight",
295  groundEmptyValueDoubleSpinBox->value())
296  .toDouble();
297  double ceilEmptyHeight =
298  settings.value("cEmptyCellsHeight",
299  ceilEmptyValueDoubleSpinBox->value())
300  .toDouble();
301  int precision =
302  settings.value("NumPrecision", precisionSpinBox->value()).toInt();
303  settings.endGroup();
304 
305  gridStepDoubleSpinBox->setValue(step);
306  heightProjectionComboBox->setCurrentIndex(projType);
307  fillGroundEmptyCellsComboBox->setCurrentIndex(groundFillStrategy);
308  fillCeilEmptyCellsComboBox->setCurrentIndex(ceilFillStrategy);
309  groundEmptyValueDoubleSpinBox->setValue(groundEmptyHeight);
310  ceilEmptyValueDoubleSpinBox->setValue(ceilEmptyHeight);
311  projDimComboBox->setCurrentIndex(projDim);
312  precisionSpinBox->setValue(precision);
313 }
314 
316  saveSettings();
317  accept();
318 }
319 
321  QSettings settings;
322  settings.beginGroup(ecvPS::VolumeCalculation());
323  settings.setValue("ProjectionType",
324  heightProjectionComboBox->currentIndex());
325  settings.setValue("ProjectionDim", projDimComboBox->currentIndex());
326  settings.setValue("gFillStrategy",
327  fillGroundEmptyCellsComboBox->currentIndex());
328  settings.setValue("cFillStrategy",
329  fillCeilEmptyCellsComboBox->currentIndex());
330  settings.setValue("GridStep", gridStepDoubleSpinBox->value());
331  settings.setValue("gEmptyCellsHeight",
332  groundEmptyValueDoubleSpinBox->value());
333  settings.setValue("cEmptyCellsHeight",
334  ceilEmptyValueDoubleSpinBox->value());
335  settings.setValue("NumPrecision", precisionSpinBox->value());
336  settings.endGroup();
337 }
338 
340  if (state) {
341  // standard button
342  updatePushButton->setStyleSheet(QString());
343  } else {
344  // red button
345  updatePushButton->setStyleSheet("color: white; background-color:red;");
346  }
347  updatePushButton->setDisabled(state);
348  clipboardPushButton->setEnabled(state);
349  exportGridPushButton->setEnabled(state);
350  if (!state) {
351  spareseWarningLabel->hide();
352  reportPlainTextEdit->setPlainText("Update the grid first");
353  }
354 }
355 
357  const ccBBox& gridBox,
358  unsigned char vertDim,
359  bool exportToOriginalCS) {
360  assert(gridBox.isValid());
361  assert(vertDim < 3);
362 
363  ccPointCloud* rasterCloud = 0;
364  try {
365  // we only compute the default 'height' layer
366  std::vector<ccRasterGrid::ExportableFields> exportedFields;
367  exportedFields.push_back(ccRasterGrid::PER_CELL_VALUE);
368 
369  rasterCloud = grid.convertToCloud(
370  exportedFields, false, false, false, false, 0, vertDim, gridBox,
371  false, std::numeric_limits<double>::quiet_NaN(),
372  exportToOriginalCS);
373 
374  if (rasterCloud && rasterCloud->hasScalarFields()) {
375  rasterCloud->showSF(true);
376  rasterCloud->setCurrentDisplayedScalarField(0);
377  ccScalarField* sf =
378  static_cast<ccScalarField*>(rasterCloud->getScalarField(0));
379  assert(sf);
380  sf->setName("Relative height");
381  sf->setSymmetricalScale(sf->getMin() < 0 && sf->getMax() > 0);
382  rasterCloud->showSFColorsScale(true);
383  }
384  } catch (const std::bad_alloc&) {
385  CVLog::Warning("[ConvertGridToCloud] Not enough memory!");
386  if (rasterCloud) {
387  delete rasterCloud;
388  rasterCloud = 0;
389  }
390  }
391 
392  return rasterCloud;
393 }
394 
396  bool exportToOriginalCS) const {
397  ccPointCloud* rasterCloud = 0;
398  try {
399  // we only compute the default 'height' layer
400  std::vector<ccRasterGrid::ExportableFields> exportedFields;
401  exportedFields.push_back(ccRasterGrid::PER_CELL_VALUE);
403  exportedFields, false, false, false, false, 0, false,
404  std::numeric_limits<double>::quiet_NaN(), exportToOriginalCS);
405 
406  if (rasterCloud && rasterCloud->hasScalarFields()) {
407  rasterCloud->showSF(true);
408  rasterCloud->setCurrentDisplayedScalarField(0);
409  ccScalarField* sf =
410  static_cast<ccScalarField*>(rasterCloud->getScalarField(0));
411  assert(sf);
412  sf->setName("Relative height");
413  sf->setSymmetricalScale(sf->getMin() < 0 && sf->getMax() > 0);
414  rasterCloud->showSFColorsScale(true);
415  }
416  } catch (const std::bad_alloc&) {
417  CVLog::Error("Not enough memory!");
418  if (rasterCloud) {
419  delete rasterCloud;
420  rasterCloud = 0;
421  }
422  }
423 
424  return rasterCloud;
425 }
426 
428  bool success = updateGrid();
429  if (success && ecvDisplayTools::GetMainWindow()) {
430  // convert grid to point cloud
431  if (m_rasterCloud) {
433  delete m_rasterCloud;
434  m_rasterCloud = 0;
435  }
436 
438  if (m_rasterCloud) {
441  update2DDisplayZoom(box);
442  } else {
443  CVLog::Error("Not enough memory!");
445  }
446  }
447 
448  gridIsUpToDate(success);
449 }
450 
451 QString ccVolumeCalcTool::ReportInfo::toText(int precision) const {
452  QLocale locale(QLocale::English);
453 
454  QStringList reportText;
455  reportText << QString("Volume: %1")
456  .arg(locale.toString(volume, 'f', precision));
457  reportText << QString("Surface: %1")
458  .arg(locale.toString(surface, 'f', precision));
459  reportText << QString("----------------------");
460  reportText << QString("Added volume: (+)%1")
461  .arg(locale.toString(addedVolume, 'f', precision));
462  reportText << QString("Removed volume: (-)%1")
463  .arg(locale.toString(removedVolume, 'f', precision));
464  reportText << QString("----------------------");
465  reportText
466  << QString("Matching cells: %1%").arg(matchingPrecent, 0, 'f', 1);
467  reportText << QString("Non-matching cells:");
468  reportText << QString(" ground = %1%")
469  .arg(groundNonMatchingPercent, 0, 'f', 1);
470  reportText
471  << QString(" ceil = %1%").arg(ceilNonMatchingPercent, 0, 'f', 1);
472  reportText << QString("Average neighbors per cell: %1 / 8.0")
473  .arg(averageNeighborsPerCell, 0, 'f', 1);
474 
475  return reportText.join("\n");
476 }
477 
479  int precision = precisionSpinBox->value();
480 
481  reportPlainTextEdit->setPlainText(info.toText(precision));
482 
483  // below 7 neighbors per cell, at least one of the cloud is very sparse!
484  spareseWarningLabel->setVisible(info.averageNeighborsPerCell < 7.0f);
485 
486  m_lastReport = info;
487  clipboardPushButton->setEnabled(true);
488 }
489 
490 bool SendError(const QString& message, QWidget* parentWidget) {
491  if (parentWidget) {
492  CVLog::Error(message);
493  } else {
494  CVLog::Warning("[Volume] " + message);
495  }
496  return false;
497 }
498 
500  ccRasterGrid& grid,
501  ccGenericPointCloud* ground,
503  const ccBBox& gridBox,
504  unsigned char vertDim,
505  double gridStep,
506  unsigned gridWidth,
507  unsigned gridHeight,
508  ccRasterGrid::ProjectionType projectionType,
509  ccRasterGrid::EmptyCellFillOption groundEmptyCellFillStrategy,
510  ccRasterGrid::EmptyCellFillOption ceilEmptyCellFillStrategy,
511  ccVolumeCalcTool::ReportInfo& reportInfo,
512  double groundHeight = std::numeric_limits<double>::quiet_NaN(),
513  double ceilHeight = std::numeric_limits<double>::quiet_NaN(),
514  QWidget* parentWidget /*=0*/) {
515  if (gridStep <= 1.0e-8 || gridWidth == 0 || gridHeight == 0 ||
516  vertDim > 2) {
517  assert(false);
518  CVLog::Warning("[Volume] Invalid input parameters");
519  return false;
520  }
521 
522  if (!ground && !ceil) {
523  assert(false);
524  CVLog::Warning("[Volume] No valid input cloud");
525  return false;
526  }
527 
528  if (!gridBox.isValid()) {
529  CVLog::Warning("[Volume] Invalid bounding-box");
530  return false;
531  }
532 
533  // grid size
534  unsigned gridTotalSize = gridWidth * gridHeight;
535  if (gridTotalSize == 1) {
536  if (parentWidget &&
537  QMessageBox::question(parentWidget, "Unexpected grid size",
538  "The generated grid will only have 1 cell! "
539  "Do you want to proceed anyway?",
540  QMessageBox::Yes,
541  QMessageBox::No) == QMessageBox::No)
542  return false;
543  } else if (gridTotalSize > 10000000) {
544  if (parentWidget &&
545  QMessageBox::question(
546  parentWidget, "Big grid size",
547  "The generated grid will have more than 10.000.000 cells! "
548  "Do you want to proceed anyway?",
549  QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
550  return false;
551  }
552 
553  // memory allocation
554  CCVector3d minCorner = CCVector3d::fromArray(gridBox.minCorner().u);
555  if (!grid.init(gridWidth, gridHeight, gridStep, minCorner)) {
556  // not enough memory
557  return SendError("Not enough memory", parentWidget);
558  }
559 
560  // progress dialog
561  QScopedPointer<ecvProgressDialog> pDlg(0);
562  if (parentWidget) {
563  pDlg.reset(new ecvProgressDialog(true, parentWidget));
564  }
565 
566  ccRasterGrid groundRaster;
567  if (ground) {
568  if (!groundRaster.init(gridWidth, gridHeight, gridStep, minCorner)) {
569  // not enough memory
570  return SendError("Not enough memory", parentWidget);
571  }
572 
573  if (groundRaster.fillWith(ground, vertDim, projectionType,
574  groundEmptyCellFillStrategy ==
577  pDlg.data())) {
578  groundRaster.fillEmptyCells(groundEmptyCellFillStrategy,
579  groundHeight);
580  CVLog::Print(QString("[Volume] Ground raster grid: size: %1 x %2 / "
581  "heights: [%3 ; %4]")
582  .arg(groundRaster.width)
583  .arg(groundRaster.height)
584  .arg(groundRaster.minHeight)
585  .arg(groundRaster.maxHeight));
586  } else {
587  return false;
588  }
589  }
590 
591  // ceil
592  ccRasterGrid ceilRaster;
593  if (ceil) {
594  if (!ceilRaster.init(gridWidth, gridHeight, gridStep, minCorner)) {
595  // not enough memory
596  return SendError("Not enough memory", parentWidget);
597  }
598 
599  if (ceilRaster.fillWith(ceil, vertDim, projectionType,
600  ceilEmptyCellFillStrategy ==
603  pDlg.data())) {
604  ceilRaster.fillEmptyCells(ceilEmptyCellFillStrategy, ceilHeight);
605  CVLog::Print(QString("[Volume] Ceil raster grid: size: %1 x %2 / "
606  "heights: [%3 ; %4]")
607  .arg(ceilRaster.width)
608  .arg(ceilRaster.height)
609  .arg(ceilRaster.minHeight)
610  .arg(ceilRaster.maxHeight));
611  } else {
612  return false;
613  }
614  }
615 
616  // update grid and compute volume
617  {
618  if (pDlg) {
619  pDlg->setMethodTitle(QObject::tr("Volume computation"));
620  pDlg->setInfo(QObject::tr("Cells: %1 x %2")
621  .arg(grid.width)
622  .arg(grid.height));
623  pDlg->start();
624  pDlg->show();
625  QCoreApplication::processEvents();
626  }
628  grid.width * grid.height);
629 
630  size_t ceilNonMatchingCount = 0;
631  size_t groundNonMatchingCount = 0;
632  size_t cellCount = 0;
633 
634  // at least one of the grid is based on a cloud
635  grid.nonEmptyCellCount = 0;
636  for (unsigned i = 0; i < grid.height; ++i) {
637  for (unsigned j = 0; j < grid.width; ++j) {
638  ccRasterCell& cell = grid.rows[i][j];
639 
640  bool validGround = true;
641  cell.minHeight = groundHeight;
642  if (ground) {
643  cell.minHeight = groundRaster.rows[i][j].h;
644  validGround = std::isfinite(cell.minHeight);
645  }
646 
647  bool validCeil = true;
648  cell.maxHeight = ceilHeight;
649  if (ceil) {
650  cell.maxHeight = ceilRaster.rows[i][j].h;
651  validCeil = std::isfinite(cell.maxHeight);
652  }
653 
654  if (validGround && validCeil) {
655  cell.h = cell.maxHeight - cell.minHeight;
656  cell.nbPoints = 1;
657 
658  reportInfo.volume += cell.h;
659  if (cell.h < 0) {
660  reportInfo.removedVolume -= cell.h;
661  } else if (cell.h > 0) {
662  reportInfo.addedVolume += cell.h;
663  }
664  reportInfo.surface += 1.0;
665  ++grid.nonEmptyCellCount; // matching count
666  ++cellCount;
667  } else {
668  if (validGround) {
669  ++cellCount;
670  ++groundNonMatchingCount;
671  } else if (validCeil) {
672  ++cellCount;
673  ++ceilNonMatchingCount;
674  }
675  cell.h = std::numeric_limits<double>::quiet_NaN();
676  cell.nbPoints = 0;
677  }
678 
679  cell.avgHeight = (groundHeight + ceilHeight) / 2;
680  cell.stdDevHeight = 0;
681 
682  if (pDlg && !nProgress.oneStep()) {
683  CVLog::Warning("[Volume] Process cancelled by the user");
684  return false;
685  }
686  }
687  }
688  grid.validCellCount = grid.nonEmptyCellCount;
689 
690  // count the average number of valid neighbors
691  {
692  size_t validNeighborsCount = 0;
693  size_t count = 0;
694  for (unsigned i = 1; i < grid.height - 1; ++i) {
695  for (unsigned j = 1; j < grid.width - 1; ++j) {
696  ccRasterCell& cell = grid.rows[i][j];
697  if (cell.h == cell.h) {
698  for (unsigned k = i - 1; k <= i + 1; ++k) {
699  for (unsigned l = j - 1; l <= j + 1; ++l) {
700  if (k != i || l != j) {
701  ccRasterCell& otherCell = grid.rows[k][l];
702  if (std::isfinite(otherCell.h)) {
703  ++validNeighborsCount;
704  }
705  }
706  }
707  }
708 
709  ++count;
710  }
711  }
712  }
713 
714  if (count) {
715  reportInfo.averageNeighborsPerCell =
716  static_cast<double>(validNeighborsCount) / count;
717  }
718  }
719 
720  reportInfo.matchingPrecent =
721  static_cast<float>(grid.validCellCount * 100) / cellCount;
722  reportInfo.groundNonMatchingPercent =
723  static_cast<float>(groundNonMatchingCount * 100) / cellCount;
724  reportInfo.ceilNonMatchingPercent =
725  static_cast<float>(ceilNonMatchingCount * 100) / cellCount;
726  float cellArea = static_cast<float>(grid.gridStep * grid.gridStep);
727  reportInfo.volume *= cellArea;
728  reportInfo.addedVolume *= cellArea;
729  reportInfo.removedVolume *= cellArea;
730  reportInfo.surface *= cellArea;
731  }
732 
733  grid.setValid(true);
734 
735  return true;
736 }
737 
739  if (!m_cloud2) {
740  assert(false);
741  return false;
742  }
743 
744  // cloud bounding-box --> grid size
745  ccBBox box = getCustomBBox();
746  if (!box.isValid()) {
747  return false;
748  }
749 
750  unsigned gridWidth = 0, gridHeight = 0;
751  if (!getGridSize(gridWidth, gridHeight)) {
752  return false;
753  }
754 
755  // grid step
756  double gridStep = getGridStep();
757  assert(gridStep != 0);
758 
759  // ground
760  ccGenericPointCloud* groundCloud = 0;
761  double groundHeight = 0;
762  switch (groundComboBox->currentIndex()) {
763  case 0:
764  groundHeight = groundEmptyValueDoubleSpinBox->value();
765  break;
766  case 1:
767  groundCloud = m_cloud1 ? m_cloud1 : m_cloud2;
768  break;
769  case 2:
770  groundCloud = m_cloud2;
771  break;
772  default:
773  assert(false);
774  return false;
775  }
776 
777  // ceil
778  ccGenericPointCloud* ceilCloud = 0;
779  double ceilHeight = 0;
780  switch (ceilComboBox->currentIndex()) {
781  case 0:
782  ceilHeight = ceilEmptyValueDoubleSpinBox->value();
783  break;
784  case 1:
785  ceilCloud = m_cloud1 ? m_cloud1 : m_cloud2;
786  break;
787  case 2:
788  ceilCloud = m_cloud2;
789  break;
790  default:
791  assert(false);
792  return false;
793  }
794 
795  ccVolumeCalcTool::ReportInfo reportInfo;
796 
797  if (ComputeVolume(m_grid, groundCloud, ceilCloud, box,
798  getProjectionDimension(), gridStep, gridWidth, gridHeight,
800  getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox),
801  getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox),
802  reportInfo, groundHeight, ceilHeight, this)) {
803  outputReport(reportInfo);
804  return true;
805  } else {
806  return false;
807  }
808 }
809 
811  QClipboard* clipboard = QApplication::clipboard();
812  if (clipboard) {
813  clipboard->setText(reportPlainTextEdit->toPlainText());
814  }
815 }
816 
818  if (!m_grid.isValid()) {
819  assert(false);
820  }
821 
822  ccPointCloud* rasterCloud = convertGridToCloud(true);
823  if (!rasterCloud) {
824  // error message should have already been issued
825  return;
826  }
827 
828  rasterCloud->setName("Height difference " + rasterCloud->getName());
829  ccGenericPointCloud* originCloud = (m_cloud1 ? m_cloud1 : m_cloud2);
830  assert(originCloud);
831  if (originCloud) {
832  if (originCloud->getParent()) {
833  originCloud->getParent()->addChild(rasterCloud);
834  }
835  // rasterCloud->setDisplay(originCloud->getDisplay());
836  }
837 
838  MainWindow* mainWindow = MainWindow::TheInstance();
839  if (mainWindow) {
840  mainWindow->addToDB(rasterCloud);
841  CVLog::Print(QString("[Volume] Cloud '%1' successfully exported")
842  .arg(rasterCloud->getName()));
843  } else {
844  assert(false);
845  delete rasterCloud;
846  }
847 }
int count
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
static MainWindow * TheInstance()
Returns the unique instance of this object.
void addToDB(const QStringList &filenames, QString fileFilter=QString(), bool displayDialog=true)
Type u[3]
Definition: CVGeom.h:139
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
2.5D data editor (generic interface)
virtual ccBBox getCustomBBox() const
Returns custom bbox.
ccRasterGrid::EmptyCellFillOption getFillEmptyCellsStrategy(QComboBox *comboBox) const
Returns the empty cell strategy (for a given combo-box)
void createBoundingBoxEditor(const ccBBox &gridBBox, QWidget *parent)
Creates the bounding-box editor.
ccPointCloud * convertGridToCloud(const std::vector< ccRasterGrid::ExportableFields > &exportedFields, bool interpolateSF, bool interpolateColors, bool resampleInputCloudXY, bool resampleInputCloudZ, ccGenericPointCloud *inputCloud, bool fillEmptyCells, double emptyCellsHeight, bool exportToOriginalCS) const
Shortcut to ccRasterGrid::convertToCloud.
ccRasterGrid m_grid
Raster grid.
virtual void update2DDisplayZoom(ccBBox &box)
Updates the 2D display zoom.
ccPointCloud * m_rasterCloud
'Raster' cloud
virtual bool showGridBoxEditor()
Show grid box editor and update.
virtual QString getGridSizeAsString() const
Returns the grid size as a string.
void create2DView(QFrame *parentFrame)
Creates the 2D view.
virtual bool getGridSize(unsigned &width, unsigned &height) const
Returns the grid size.
Bounding box structure.
Definition: ecvBBox.h:25
virtual void showSF(bool state)
Sets active scalarfield visibility.
A 3D cloud interface with associated features (color, normals, octree, etc.)
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
virtual ccBBox getDisplayBB_recursive(bool relative)
Returns the bounding-box of this entity and it's children WHEN DISPLAYED.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
void showSFColorsScale(bool state)
Sets whether color scale should be displayed or not.
bool hasScalarFields() const override
Returns whether one or more scalar fields are instantiated.
A scalar field associated to display-related parameters.
void setSymmetricalScale(bool state)
Sets whether the color scale should be symmetrical or not.
ccGenericPointCloud * m_cloud2
Second associated cloud.
virtual void gridIsUpToDate(bool state) override
Declares whether the grid is up-to-date or not.
virtual bool showGridBoxEditor() override
void projectionDirChanged(int)
Called when the projection direction changes.
static ccPointCloud * ConvertGridToCloud(ccRasterGrid &grid, const ccBBox &gridBox, unsigned char vertDim, bool exportToOriginalCS)
Converts a (volume) grid to a point cloud.
void loadSettings()
Load persistent settings.
void setDisplayedNumberPrecision(int)
Sets the displayed number precision.
void ceilFillEmptyCellStrategyChanged(int)
Called when the (ceil) empty cell filling strategy changes.
virtual ccRasterGrid::ProjectionType getTypeOfProjection() const override
Returns type of projection.
void sfProjectionTypeChanged(int)
Called when the SF projection type changes.
static bool 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, ccRasterGrid::EmptyCellFillOption ceilEmptyCellFillStrategy, ccVolumeCalcTool::ReportInfo &reportInfo, double groundHeight, double ceilHeight, QWidget *parentWidget=0)
Static accessor.
void updateGridInfo()
Updates the gid info.
virtual unsigned char getProjectionDimension() const override
Returns projection dimension.
void updateGridAndDisplay()
Update the grid and the 2D display.
void outputReport(const ReportInfo &info)
Outputs the report.
~ccVolumeCalcTool()
Destructor.
void saveSettings()
Save persistent settings and 'accept' dialog.
ccVolumeCalcTool(ccGenericPointCloud *cloud1, ccGenericPointCloud *cloud2, QWidget *parent=0)
Default constructor.
ReportInfo m_lastReport
Last report.
void gridOptionChanged()
Called when the an option of the grid generation has changed.
bool updateGrid()
Updates the grid.
void groundFillEmptyCellStrategyChanged(int)
Called when the (ground) empty cell filling strategy changes.
void ceilSourceChanged(int)
Ceil source changed.
virtual double getGridStep() const override
Returns projection grid step.
ccPointCloud * convertGridToCloud(bool exportToOriginalCS) const
Converts the grid to a point cloud.
void groundSourceChanged(int)
Ground source changed.
void exportToClipboard() const
Exports info to clipboard.
ccGenericPointCloud * m_cloud1
First associated cloud.
void saveSettingsAndAccept()
Accepts the dialog and save settings.
void exportGridAsCloud() const
Exports the grid as a point cloud.
void swapRoles()
Swap roles.
const Vector3Tpl< T > & minCorner() const
Returns min corner (const)
Definition: BoundingBox.h:154
bool isValid() const
Returns whether bounding box is valid or not.
Definition: BoundingBox.h:203
bool oneStep()
Increments total progress value of a single unit.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
ScalarType getMin() const
Returns the minimum value.
Definition: ScalarField.h:72
void setName(const char *name)
Sets scalar field name.
Definition: ScalarField.cpp:22
ScalarType getMax() const
Returns the maximum value.
Definition: ScalarField.h:74
static void RemoveFromOwnDB(ccHObject *obj)
Removes an entity from window own DB.
static void AddToOwnDB(ccHObject *obj, bool noDependency=true)
Adds an entity to window own DB.
static const ecvGui::ParamStruct & GetDisplayParameters()
Returns current parameters for this display (const version)
static void SetDisplayParameters(const ecvGui::ParamStruct &params)
Sets current parameters for this display.
static QMainWindow * GetMainWindow()
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
static const QString VolumeCalculation()
Graphical progress indicator (thread-safe)
bool SendError(const QString &message, QWidget *parentWidget)
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
cloudViewer::NormalizedProgress * nProgress
Raster grid cell.
Definition: ecvRasterGrid.h:22
double avgHeight
Average height value.
Definition: ecvRasterGrid.h:37
double h
Height value.
Definition: ecvRasterGrid.h:35
unsigned nbPoints
Number of points projected in this cell.
Definition: ecvRasterGrid.h:45
PointCoordinateType maxHeight
Max height value.
Definition: ecvRasterGrid.h:43
PointCoordinateType minHeight
Min height value.
Definition: ecvRasterGrid.h:41
double stdDevHeight
Height std.dev.
Definition: ecvRasterGrid.h:39
Raster grid type.
Definition: ecvRasterGrid.h:53
unsigned validCellCount
Number of VALID cells.
unsigned nonEmptyCellCount
Number of NON-EMPTY cells.
void setValid(bool state)
Sets valid.
ProjectionType
Types of projection.
double gridStep
Grid step ('pixel' size)
unsigned height
Number of rows.
bool init(unsigned w, unsigned h, double gridStep, const CCVector3d &minCorner)
Initializes / resets the grid.
bool fillWith(ccGenericPointCloud *cloud, unsigned char projectionDimension, ProjectionType projectionType, bool interpolateEmptyCells, ProjectionType sfInterpolation=INVALID_PROJECTION_TYPE, ecvProgressDialog *progressDialog=nullptr)
Fills the grid with a point cloud.
std::vector< Row > rows
All cells.
double maxHeight
Max height (computed on the NON-EMPTY or INTERPOLATED cells)
ccPointCloud * convertToCloud(const std::vector< ExportableFields > &exportedFields, bool interpolateSF, bool interpolateColors, bool resampleInputCloudXY, bool resampleInputCloudZ, ccGenericPointCloud *inputCloud, unsigned char Z, const ccBBox &box, bool fillEmptyCells, double emptyCellsHeight, bool exportToOriginalCS) const
Converts the grid to a cloud with scalar field(s)
bool isValid() const
Returns whether the grid is 'valid' or not.
unsigned width
Number of columns.
EmptyCellFillOption
Option for handling empty cells.
double minHeight
Min height (computed on the NON-EMPTY or INTERPOLATED cells)
void fillEmptyCells(EmptyCellFillOption fillEmptyCellsStrategy, double customCellHeight=0)
Fills the empty cell (for all strategies but 'INTERPOLATE_DELAUNAY')
QString toText(int precision=6) const
GUI parameters.