| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899 |
- //local
- #include "ccRasterizeTool.h"
- //Qt
- #include <QString>
- #include <QMessageBox>
- //qCC_db
- #include "ccCommandRaster.h"
- #include <ccMesh.h>
- #include <ccProgressDialog.h>
- #include <ccVolumeCalcTool.h>
- #include <QDateTime>
- //shared commands
- constexpr char COMMAND_GRID_VERT_DIR[] = "VERT_DIR";
- constexpr char COMMAND_GRID_STEP[] = "GRID_STEP";
- constexpr char COMMAND_GRID_OUTPUT_CLOUD[] = "OUTPUT_CLOUD";
- constexpr char COMMAND_GRID_OUTPUT_MESH[] = "OUTPUT_MESH";
- constexpr char COMMAND_GRID_OUTPUT_RASTER_Z[] = "OUTPUT_RASTER_Z";
- constexpr char COMMAND_GRID_OUTPUT_RASTER_Z_AND_SF[] = "OUTPUT_RASTER_Z_AND_SF";
- constexpr char COMMAND_GRID_OUTPUT_RASTER_RGB[] = "OUTPUT_RASTER_RGB";
- //Rasterize specific commands
- constexpr char COMMAND_RASTERIZE[] = "RASTERIZE";
- constexpr char COMMAND_RASTER_CUSTOM_HEIGHT[] = "CUSTOM_HEIGHT";
- constexpr char COMMAND_RASTER_FILL_EMPTY_CELLS[] = "EMPTY_FILL";
- constexpr char COMMAND_RASTER_FILL_MIN_HEIGHT[] = "MIN_H";
- constexpr char COMMAND_RASTER_FILL_MAX_HEIGHT[] = "MAX_H";
- constexpr char COMMAND_RASTER_FILL_CUSTOM_HEIGHT[] = "CUSTOM_H";
- constexpr char COMMAND_RASTER_FILL_INTERPOLATE[] = "INTERP";
- constexpr char COMMAND_RASTER_FILL_KRIGING[] = "KRIGING";
- constexpr char COMMAND_RASTER_FILL_KRIGING_KNN[] = "KRIGING_KNN";
- constexpr char COMMAND_RASTER_PROJ_TYPE[] = "PROJ";
- constexpr char COMMAND_RASTER_SF_PROJ_TYPE[] = "SF_PROJ";
- constexpr char COMMAND_RASTER_INTERP_MAX_EDGE_LENGTH[] = "MAX_EDGE_LENGTH";
- constexpr char COMMAND_RASTER_PROJ_MIN[] = "MIN";
- constexpr char COMMAND_RASTER_PROJ_MAX[] = "MAX";
- constexpr char COMMAND_RASTER_PROJ_AVG[] = "AVG";
- constexpr char COMMAND_RASTER_PROJ_MED[] = "MED";
- constexpr char COMMAND_RASTER_PROJ_INVERSE_VAR[] = "INV_VAR";
- constexpr char COMMAND_RASTER_RESAMPLE[] = "RESAMPLE";
- //2.5D Volume calculation specific commands
- constexpr char COMMAND_VOLUME[] = "VOLUME";
- constexpr char COMMAND_VOLUME_GROUND_IS_FIRST[] = "GROUND_IS_FIRST";
- constexpr char COMMAND_VOLUME_CONST_HEIGHT[] = "CONST_HEIGHT";
- static bool ReadProjectionType(ccCommandLineInterface& cmd, ccRasterGrid::ProjectionType& projType, QString& stdDevSFDesc)
- {
- QString option = cmd.arguments().takeFirst().toUpper();
- stdDevSFDesc.clear();
- if (option == COMMAND_RASTER_PROJ_MIN)
- {
- projType = ccRasterGrid::PROJ_MINIMUM_VALUE;
- }
- else if (option == COMMAND_RASTER_PROJ_MAX)
- {
- projType = ccRasterGrid::PROJ_MAXIMUM_VALUE;
- }
- else if (option == COMMAND_RASTER_PROJ_AVG)
- {
- projType = ccRasterGrid::PROJ_AVERAGE_VALUE;
- }
- else if (option == COMMAND_RASTER_PROJ_MED)
- {
- projType = ccRasterGrid::PROJ_MEDIAN_VALUE;
- }
- else if (option == COMMAND_RASTER_PROJ_INVERSE_VAR)
- {
- projType = ccRasterGrid::PROJ_INVERSE_VAR_VALUE;
- // we expect the std. dev. SF index as well
- if (cmd.arguments().size() != 0)
- {
- stdDevSFDesc = cmd.arguments().takeFirst();
- }
- else
- {
- cmd.error(QString("Expecting the std. dev. SF index after %1").arg(option));
- return false;
- }
- }
- else
- {
- assert(false);
- cmd.error(QString("Unknown projection type: %1").arg(option));
- return false;
- }
- return true;
- }
- static ccRasterGrid::EmptyCellFillOption GetEmptyCellFillingStrategy(QString option, ccCommandLineInterface& cmd)
- {
- if (option == COMMAND_RASTER_FILL_MIN_HEIGHT)
- {
- return ccRasterGrid::FILL_MINIMUM_HEIGHT;
- }
- else if (option == COMMAND_RASTER_FILL_MAX_HEIGHT)
- {
- return ccRasterGrid::FILL_MAXIMUM_HEIGHT;
- }
- else if (option == COMMAND_RASTER_FILL_CUSTOM_HEIGHT)
- {
- return ccRasterGrid::FILL_CUSTOM_HEIGHT;
- }
- else if (option == COMMAND_RASTER_FILL_INTERPOLATE)
- {
- return ccRasterGrid::INTERPOLATE_DELAUNAY;
- }
- else if (option == COMMAND_RASTER_FILL_KRIGING)
- {
- return ccRasterGrid::KRIGING;
- }
- else
- {
- assert(false);
- cmd.warning(QString("Unknown empty cell filling strategy: %1 (defaulting to 'leave empty')").arg(option));
- return ccRasterGrid::LEAVE_EMPTY;
- }
- }
- CommandRasterize::CommandRasterize()
- : ccCommandLineInterface::Command("Rasterize", COMMAND_RASTERIZE)
- {}
- bool CommandRasterize::process(ccCommandLineInterface &cmd)
- {
- cmd.print("[RASTERIZE]");
- //look for local options
- double gridStep = 0;
- bool outputCloud = false;
- bool outputRasterZ = false;
- bool outputRasterSFs = false;
- bool outputRasterRGB = false;
- bool outputMesh = false;
- bool resample = false;
- double customHeight = std::numeric_limits<double>::quiet_NaN();
- int vertDir = 2;
- ccRasterGrid::ProjectionType projectionType = ccRasterGrid::PROJ_AVERAGE_VALUE;
- ccRasterGrid::ProjectionType sfProjectionType = ccRasterGrid::PROJ_AVERAGE_VALUE;
- ccRasterGrid::EmptyCellFillOption emptyCellFillStrategy = ccRasterGrid::LEAVE_EMPTY;
- ccRasterGrid::DelaunayInterpolationParams dInterpParams;
- ccRasterGrid::KrigingParams krigingParams;
- {
- // force auto-guess
- krigingParams.autoGuess = true;
- }
- QString projStdDevSFDesc, sfProjStdDevSFDesc;
- while (!cmd.arguments().empty())
- {
- QString argument = cmd.arguments().front();
- if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_CLOUD))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- if (outputMesh)
- {
- cmd.warning("Can't output the grid as a mesh AND a cloud at the same time");
- }
- else
- {
- outputCloud = true;
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_MESH))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- if (outputCloud)
- {
- cmd.warning("Can't output the grid as a mesh AND a cloud at the same time");
- }
- else
- {
- outputMesh = true;
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_RASTER_Z))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- outputRasterZ = true;
- outputRasterSFs = false;
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_RASTER_Z_AND_SF))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- outputRasterZ = true;
- outputRasterSFs = true;
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_RASTER_RGB))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- outputRasterRGB = true;
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_STEP))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- bool ok;
- gridStep = cmd.arguments().takeFirst().toDouble(&ok);
- if (!ok || gridStep <= 0)
- {
- return cmd.error(QString("Invalid grid step value! (after %1)").arg(COMMAND_GRID_STEP));
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_CUSTOM_HEIGHT))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- bool ok;
- customHeight = cmd.arguments().takeFirst().toDouble(&ok);
- if (!ok)
- {
- return cmd.error(QString("Invalid custom height value! (after %1)").arg(COMMAND_RASTER_CUSTOM_HEIGHT));
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_VERT_DIR))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- bool ok;
- vertDir = cmd.arguments().takeFirst().toInt(&ok);
- if (!ok || vertDir < 0 || vertDir > 2)
- {
- return cmd.error(QString("Invalid vert. direction! (after %1)").arg(COMMAND_GRID_VERT_DIR));
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_FILL_EMPTY_CELLS))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- emptyCellFillStrategy = GetEmptyCellFillingStrategy(cmd.arguments().takeFirst().toUpper(), cmd);
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_PROJ_TYPE))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- if (!ReadProjectionType(cmd, projectionType, projStdDevSFDesc))
- {
- return false;
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_SF_PROJ_TYPE))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- if (!ReadProjectionType(cmd, sfProjectionType, sfProjStdDevSFDesc))
- {
- return false;
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_INTERP_MAX_EDGE_LENGTH))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- bool ok = false;
- dInterpParams.maxEdgeLength = cmd.arguments().takeFirst().toDouble(&ok);
- if (!ok || dInterpParams.maxEdgeLength < 0.0)
- {
- return cmd.error(QString("Invalid max edge length value! (after %1)").arg(COMMAND_RASTER_INTERP_MAX_EDGE_LENGTH));
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_FILL_KRIGING_KNN))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- bool ok = false;
- krigingParams.kNN = cmd.arguments().takeFirst().toInt(&ok);
- if (!ok || krigingParams.kNN <= 0)
- {
- return cmd.error(QString("Invalid Kriging knn value! (after %1)").arg(COMMAND_RASTER_FILL_KRIGING_KNN));
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_RESAMPLE))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- resample = true;
- }
- else
- {
- break;
- }
- }
- // there can be only one (std. dev. SF ;)
- QString stdDevSFDesc;
- if (!projStdDevSFDesc.isEmpty())
- {
- if (!sfProjStdDevSFDesc.isEmpty() && projStdDevSFDesc != sfProjStdDevSFDesc)
- {
- cmd.warning("[Rasterize] Can't set 2 different std. dev. SF for inverse variance projection modes. The point projection SF will be used by default.");
- }
- stdDevSFDesc = projStdDevSFDesc;
- }
- else
- {
- stdDevSFDesc = sfProjStdDevSFDesc;
- }
- if (gridStep == 0)
- {
- return cmd.error(QString("Grid step value not defined (use %1)").arg(COMMAND_GRID_STEP));
- }
- if (std::isnan(customHeight))
- {
- if (emptyCellFillStrategy == ccRasterGrid::FILL_CUSTOM_HEIGHT)
- {
- cmd.warning("[Rasterize] The filling stragety is set to 'fill with custom height' but no custom height was defined...");
- emptyCellFillStrategy = ccRasterGrid::LEAVE_EMPTY;
- }
- else if (emptyCellFillStrategy == ccRasterGrid::INTERPOLATE_DELAUNAY)
- {
- cmd.warning("[Rasterize] The filling stragety is set to 'Delaunay' but no custom height was defined. Some holes may remain (outside of the convex hull).");
- }
- }
- if (!outputCloud && !outputMesh && !outputRasterZ && !outputRasterRGB)
- {
- //if no export target is specified, we chose the cloud by default
- outputCloud = true;
- }
- if (resample && !outputCloud && !outputMesh)
- {
- cmd.warning("[Rasterize] The 'resample' option is set while the raster won't be exported as a cloud nor as a mesh");
- }
- //we'll get the first two clouds
- for (CLCloudDesc& cloudDesc : cmd.clouds())
- {
- if (!cloudDesc.pc)
- {
- assert(false);
- continue;
- }
- int invVarProjSFIndex = -1;
- if (projectionType == ccRasterGrid::PROJ_INVERSE_VAR_VALUE)
- {
- // let's check if the SF description is its name
- invVarProjSFIndex = cloudDesc.pc->getScalarFieldIndexByName(stdDevSFDesc.toStdString());
- if (invVarProjSFIndex < 0)
- {
- // let's check if it's a (valid) index then
- bool validValue = false;
- invVarProjSFIndex = stdDevSFDesc.toInt(&validValue);
- if (!validValue)
- {
- return cmd.error(QString("[Rasterize] Failed to recognize the std. dev. SF '%1' (neither an existing scalar field name nor a valid index)").arg(stdDevSFDesc));
- }
- else if (invVarProjSFIndex < 0 || static_cast<unsigned>(invVarProjSFIndex) >= cloudDesc.pc->getNumberOfScalarFields())
- {
- return cmd.error("[Rasterize] Invalid std. dev. SF index (negative or greater than the number of scalar fields in the cloud");
- }
- }
- }
- ccBBox gridBBox = cloudDesc.pc->getOwnBB();
- //compute the grid size
- unsigned gridWidth = 0;
- unsigned gridHeight = 0;
- if (!ccRasterGrid::ComputeGridSize(vertDir, gridBBox, gridStep, gridWidth, gridHeight))
- {
- return cmd.error("Failed to compute the grid dimensions (check input cloud(s) bounding-box)");
- }
- cmd.print(QString("Grid size: %1 x %2").arg(gridWidth).arg(gridHeight));
- if (gridWidth * gridHeight > (1 << 26)) //64 million of cells
- {
- if (cmd.silentMode())
- {
- ccLog::Warning("Huge grid detected!");
- }
- else
- {
- static bool s_firstTime = true;
- if (s_firstTime && QMessageBox::warning(cmd.widgetParent(), "Raster grid", "Grid size is huge. Are you sure you want to proceed?\n(you can avoid this message by running in SILENT mode)", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
- {
- return ccLog::Warning("Process cancelled");
- }
- s_firstTime = false;
- }
- }
- ccRasterGrid grid;
- {
- //memory allocation
- CCVector3d minCorner = gridBBox.minCorner();
- if (!grid.init(gridWidth, gridHeight, gridStep, minCorner))
- {
- //not enough memory
- return cmd.error("Not enough memory");
- }
- //progress dialog
- QScopedPointer<ccProgressDialog> pDlg(nullptr);
- if (!cmd.silentMode())
- {
- pDlg.reset(new ccProgressDialog(true, cmd.widgetParent()));
- }
- ccRasterGrid::InterpolationType interpolationType = ccRasterGrid::InterpolationTypeFromEmptyCellFillOption(emptyCellFillStrategy);
- void* interpolationParams = nullptr;
- switch (interpolationType)
- {
- case ccRasterGrid::InterpolationType::DELAUNAY:
- interpolationParams = (void*)&dInterpParams;
- break;
- case ccRasterGrid::InterpolationType::KRIGING:
- interpolationParams = (void*)&krigingParams;
- break;
- default:
- // do nothing
- break;
- }
- if (grid.fillWith( cloudDesc.pc,
- vertDir,
- projectionType,
- interpolationType,
- interpolationParams,
- sfProjectionType,
- pDlg.data(),
- invVarProjSFIndex )
- )
- {
- grid.fillEmptyCells(emptyCellFillStrategy, customHeight);
- cmd.print(QString("[Rasterize] Raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(grid.width).arg(grid.height).arg(grid.minHeight).arg(grid.maxHeight));
- }
- else
- {
- return cmd.error("Rasterize process failed");
- }
- }
- //generate the result entity (cloud by default)
- if (outputCloud || outputMesh)
- {
- ccPointCloud* rasterCloud = nullptr;
- try
- {
- //we always compute the default 'height' layer
- std::vector<ccRasterGrid::ExportableFields> exportedStatistics(1);
- exportedStatistics.back() = ccRasterGrid::PER_CELL_VALUE;
- rasterCloud = grid.convertToCloud( true,
- false,
- exportedStatistics,
- true,
- true,
- resample,
- resample,
- cloudDesc.pc,
- vertDir,
- gridBBox,
- 0.0,
- true,
- true,
- nullptr
- );
- }
- catch (const std::bad_alloc&)
- {
- return cmd.error("Not enough memory");
- }
- if (!rasterCloud)
- {
- return cmd.error("Failed to output the raster grid as a cloud");
- }
- rasterCloud->showColors(cloudDesc.pc->hasColors());
- if (rasterCloud->hasScalarFields())
- {
- rasterCloud->showSF(!cloudDesc.pc->hasColors());
- rasterCloud->setCurrentDisplayedScalarField(0);
- }
- //don't forget the original shift
- rasterCloud->copyGlobalShiftAndScale(*cloudDesc.pc);
- if (outputCloud)
- {
- assert(!outputMesh);
- //replace current cloud by the restarized version
- delete cloudDesc.pc;
- cloudDesc.pc = rasterCloud;
- cloudDesc.basename += QString("_RASTER");
- rasterCloud = nullptr;
- if (cmd.autoSaveMode())
- {
- QString errorStr = cmd.exportEntity(cloudDesc);
- if (!errorStr.isEmpty())
- {
- return cmd.error(errorStr);
- }
- }
- }
- else if (outputMesh)
- {
- std::string errorStr;
- CCCoreLib::GenericIndexedMesh* baseMesh = CCCoreLib::PointProjectionTools::computeTriangulation
- (
- rasterCloud,
- CCCoreLib::DELAUNAY_2D_AXIS_ALIGNED,
- CCCoreLib::PointProjectionTools::IGNORE_MAX_EDGE_LENGTH,
- vertDir,
- errorStr
- );
- if (baseMesh)
- {
- ccMesh* rasterMesh = new ccMesh(baseMesh, rasterCloud);
- delete baseMesh;
- baseMesh = nullptr;
- rasterCloud->setEnabled(false);
- rasterCloud->setVisible(true);
- rasterMesh->addChild(rasterCloud);
- rasterMesh->setName(rasterCloud->getName());
- //rasterCloud->setName("vertices");
- rasterMesh->showSF(rasterCloud->sfShown());
- rasterMesh->showColors(rasterCloud->colorsShown());
- rasterCloud = nullptr; //to avoid deleting it later
- cmd.print(QString("[Rasterize] Mesh '%1' successfully generated").arg(rasterMesh->getName()));
- CLMeshDesc meshDesc;
- meshDesc.mesh = rasterMesh;
- meshDesc.basename = cloudDesc.basename + QString("_RASTER_MESH");
- meshDesc.path = cloudDesc.path;
- QString errorStr = cmd.exportEntity(meshDesc);
- if (!errorStr.isEmpty())
- {
- delete rasterMesh;
- return cmd.error(errorStr);
- }
- //we keep the mesh loaded
- cmd.meshes().push_back(meshDesc);
- //delete rasterMesh;
- //rasterMesh = 0;
- }
- else
- {
- cmd.warning( QStringLiteral("[Rasterize] Failed to create output mesh ('%1')")
- .arg( QString::fromStdString( errorStr ) ) );
- }
- }
- if (rasterCloud)
- {
- delete rasterCloud;
- rasterCloud = nullptr;
- }
- }
- if (outputRasterZ)
- {
- ccRasterizeTool::ExportBands bands;
- {
- bands.height = true;
- bands.rgb = false; //not a good idea to mix RGB and height values!
- bands.allSFs = outputRasterSFs;
- }
- QString exportFilename = cmd.getExportFilename(cloudDesc, "tif", outputRasterSFs ? "RASTER_Z_AND_SF" : "RASTER_Z", nullptr, !cmd.addTimestamp());
- if (exportFilename.isEmpty())
- {
- exportFilename = "rasterZ.tif";
- }
- ccRasterizeTool::ExportGeoTiff(exportFilename, bands, emptyCellFillStrategy, grid, gridBBox, vertDir, customHeight, cloudDesc.pc);
- }
- if (outputRasterRGB)
- {
- ccRasterizeTool::ExportBands bands;
- {
- bands.rgb = true;
- bands.height = false; //not a good idea to mix RGB and height values!
- bands.allSFs = outputRasterSFs;
- }
- QString exportFilename = cmd.getExportFilename(cloudDesc, "tif", "RASTER_RGB", nullptr, !cmd.addTimestamp());
- if (exportFilename.isEmpty())
- {
- exportFilename = "rasterRGB.tif";
- }
- ccRasterizeTool::ExportGeoTiff(exportFilename, bands, emptyCellFillStrategy, grid, gridBBox, vertDir, customHeight, cloudDesc.pc);
- }
- }
- return true;
- }
- CommandVolume25D::CommandVolume25D()
- : ccCommandLineInterface::Command("2.5D Volume Calculation", COMMAND_VOLUME)
- {}
- bool CommandVolume25D::process(ccCommandLineInterface &cmd)
- {
- cmd.print("[2.5D VOLUME]");
- //look for local options
- bool groundIsFirst = false;
- double gridStep = 0;
- double constHeight = std::numeric_limits<double>::quiet_NaN();
- bool outputMesh = false;
- int vertDir = 2;
- while (!cmd.arguments().empty())
- {
- QString argument = cmd.arguments().front();
- if (ccCommandLineInterface::IsCommand(argument, COMMAND_VOLUME_GROUND_IS_FIRST))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- groundIsFirst = true;
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_MESH))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- outputMesh = true;
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_STEP))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- bool ok;
- gridStep = cmd.arguments().takeFirst().toDouble(&ok);
- if (!ok || gridStep <= 0)
- {
- return cmd.error(QString("Invalid grid step value! (after %1)").arg(COMMAND_GRID_STEP));
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_VOLUME_CONST_HEIGHT))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- bool ok;
- constHeight = cmd.arguments().takeFirst().toDouble(&ok);
- if (!ok)
- {
- return cmd.error(QString("Invalid const. height value! (after %1)").arg(COMMAND_VOLUME_CONST_HEIGHT));
- }
- }
- else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_VERT_DIR))
- {
- //local option confirmed, we can move on
- cmd.arguments().pop_front();
- bool ok;
- vertDir = cmd.arguments().takeFirst().toInt(&ok);
- if (!ok || vertDir < 0 || vertDir > 2)
- {
- return cmd.error(QString("Invalid vert. direction! (after %1)").arg(COMMAND_GRID_VERT_DIR));
- }
- }
- else
- {
- //unrecognized argument (probably another command?)
- break;
- }
- }
- if (gridStep == 0)
- {
- return cmd.error(QString("Grid step value not defined (use %1)").arg(COMMAND_GRID_STEP));
- }
- //we'll get the first two clouds
- CLCloudDesc *ground = nullptr;
- CLCloudDesc *ceil = nullptr;
- {
- CLCloudDesc* clouds[2] = { nullptr, nullptr };
- int index = 0;
- if (!cmd.clouds().empty())
- {
- clouds[index++] = &cmd.clouds()[0];
- if (std::isnan(constHeight) && cmd.clouds().size() > 1)
- {
- clouds[index++] = &cmd.clouds()[1];
- }
- }
- int expectedCount = std::isnan(constHeight) ? 2 : 1;
- if (index != expectedCount)
- {
- return cmd.error(QString("Not enough loaded entities (%1 found, %2 expected)").arg(index).arg(expectedCount));
- }
- if (index == 2 && groundIsFirst)
- {
- //put them in the right order (ground then ceil)
- std::swap(clouds[0], clouds[1]);
- }
- ceil = clouds[0];
- ground = clouds[1];
- }
- ccBBox gridBBox = ceil ? ceil->pc->getOwnBB() : ccBBox();
- if (ground)
- {
- gridBBox += ground->pc->getOwnBB();
- }
- //compute the grid size
- unsigned gridWidth = 0;
- unsigned gridHeight = 0;
- if (!ccRasterGrid::ComputeGridSize(vertDir, gridBBox, gridStep, gridWidth, gridHeight))
- {
- return cmd.error("Failed to compute the grid dimensions (check input cloud(s) bounding-box)");
- }
- cmd.print(QString("Grid size: %1 x %2").arg(gridWidth).arg(gridHeight));
- if (gridWidth * gridHeight > (1 << 26)) //64 million of cells
- {
- if (cmd.silentMode())
- {
- ccLog::Warning("Huge grid detected!");
- }
- else
- {
- static bool s_firstTime = true;
- if (s_firstTime && QMessageBox::warning(cmd.widgetParent(), "Volume grid", "Grid size is huge. Are you sure you want to proceed?\n(you can avoid this message by running in SILENT mode)", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
- {
- return ccLog::Warning("Process cancelled");
- }
- s_firstTime = false;
- }
- }
- ccRasterGrid grid;
- ccVolumeCalcTool::ReportInfo reportInfo;
- if (ccVolumeCalcTool::ComputeVolume(
- grid,
- ground ? ground->pc : nullptr,
- ceil ? ceil->pc : nullptr,
- gridBBox,
- vertDir,
- gridStep,
- gridWidth,
- gridHeight,
- ccRasterGrid::PROJ_AVERAGE_VALUE,
- ccRasterGrid::LEAVE_EMPTY,
- 0.0,
- ccRasterGrid::LEAVE_EMPTY,
- 0.0,
- reportInfo,
- constHeight,
- constHeight,
- cmd.silentMode() ? nullptr : cmd.widgetParent()))
- {
- CLCloudDesc* desc = ceil ? ceil : ground;
- assert(desc);
- //save repot in a separate text file
- {
- QString txtFilename = QString("%1/VolumeCalculationReport").arg(desc->path);
- if (cmd.addTimestamp())
- txtFilename += QString("_%1").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh'h'mm"));
- txtFilename += QString(".txt");
- QFile txtFile(txtFilename);
- txtFile.open(QIODevice::WriteOnly | QIODevice::Text);
- QTextStream txtStream(&txtFile);
- txtStream << reportInfo.toText() << endl;
- txtFile.close();
- }
- //generate the result entity (cloud by default)
- {
- ccPointCloud* rasterCloud = ccVolumeCalcTool::ConvertGridToCloud(grid, gridBBox, vertDir, true);
- if (!rasterCloud)
- {
- return cmd.error("Failed to output the volume grid");
- }
- if (rasterCloud->hasScalarFields())
- {
- //convert SF to RGB
- //rasterCloud->setCurrentDisplayedScalarField(0);
- rasterCloud->convertCurrentScalarFieldToColors(false);
- rasterCloud->showColors(true);
- }
- ccMesh* rasterMesh = nullptr;
- if (outputMesh)
- {
- std::string errorStr;
- CCCoreLib::GenericIndexedMesh* baseMesh = CCCoreLib::PointProjectionTools::computeTriangulation(rasterCloud,
- CCCoreLib::DELAUNAY_2D_AXIS_ALIGNED,
- CCCoreLib::PointProjectionTools::IGNORE_MAX_EDGE_LENGTH,
- vertDir,
- errorStr);
- if (baseMesh)
- {
- rasterMesh = new ccMesh(baseMesh, rasterCloud);
- delete baseMesh;
- baseMesh = nullptr;
- }
- if (rasterMesh)
- {
- rasterCloud->setEnabled(false);
- rasterCloud->setVisible(true);
- rasterMesh->addChild(rasterCloud);
- rasterMesh->setName(rasterCloud->getName());
- rasterCloud->setName("vertices");
- rasterMesh->showSF(rasterCloud->sfShown());
- rasterMesh->showColors(rasterCloud->colorsShown());
- cmd.print(QString("[Volume] Mesh '%1' successfully generated").arg(rasterMesh->getName()));
- }
- else
- {
- delete rasterCloud;
- return cmd.error( QStringLiteral("[Voume] Failed to create output mesh ('%1')")
- .arg( QString::fromStdString( errorStr ) ) );
- }
- }
- CLEntityDesc* outputDesc = nullptr;
- if (rasterMesh)
- {
- CLMeshDesc meshDesc;
- meshDesc.mesh = rasterMesh;
- meshDesc.basename = desc->basename;
- meshDesc.path = desc->path;
- cmd.meshes().push_back(meshDesc);
- outputDesc = &cmd.meshes().back();
- }
- else
- {
- CLCloudDesc cloudDesc;
- cloudDesc.pc = rasterCloud;
- cloudDesc.basename = desc->basename;
- cloudDesc.path = desc->path;
- cmd.clouds().push_back(cloudDesc);
- outputDesc = &cmd.clouds().back();
- }
- //save result
- if (outputDesc && cmd.autoSaveMode())
- {
- QString outputFilename;
- QString errorStr = cmd.exportEntity(*outputDesc, "HEIGHT_DIFFERENCE", &outputFilename);
- if (!errorStr.isEmpty())
- cmd.warning(errorStr);
- }
- }
- }
- else
- {
- return cmd.error("Failed to compte the volume");
- }
- return true;
- }
|