//local #include "ccRasterizeTool.h" //Qt #include #include //qCC_db #include "ccCommandRaster.h" #include #include #include #include //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::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(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 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 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::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; }