ccCommandRaster.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. //local
  2. #include "ccRasterizeTool.h"
  3. //Qt
  4. #include <QString>
  5. #include <QMessageBox>
  6. //qCC_db
  7. #include "ccCommandRaster.h"
  8. #include <ccMesh.h>
  9. #include <ccProgressDialog.h>
  10. #include <ccVolumeCalcTool.h>
  11. #include <QDateTime>
  12. //shared commands
  13. constexpr char COMMAND_GRID_VERT_DIR[] = "VERT_DIR";
  14. constexpr char COMMAND_GRID_STEP[] = "GRID_STEP";
  15. constexpr char COMMAND_GRID_OUTPUT_CLOUD[] = "OUTPUT_CLOUD";
  16. constexpr char COMMAND_GRID_OUTPUT_MESH[] = "OUTPUT_MESH";
  17. constexpr char COMMAND_GRID_OUTPUT_RASTER_Z[] = "OUTPUT_RASTER_Z";
  18. constexpr char COMMAND_GRID_OUTPUT_RASTER_Z_AND_SF[] = "OUTPUT_RASTER_Z_AND_SF";
  19. constexpr char COMMAND_GRID_OUTPUT_RASTER_RGB[] = "OUTPUT_RASTER_RGB";
  20. //Rasterize specific commands
  21. constexpr char COMMAND_RASTERIZE[] = "RASTERIZE";
  22. constexpr char COMMAND_RASTER_CUSTOM_HEIGHT[] = "CUSTOM_HEIGHT";
  23. constexpr char COMMAND_RASTER_FILL_EMPTY_CELLS[] = "EMPTY_FILL";
  24. constexpr char COMMAND_RASTER_FILL_MIN_HEIGHT[] = "MIN_H";
  25. constexpr char COMMAND_RASTER_FILL_MAX_HEIGHT[] = "MAX_H";
  26. constexpr char COMMAND_RASTER_FILL_CUSTOM_HEIGHT[] = "CUSTOM_H";
  27. constexpr char COMMAND_RASTER_FILL_INTERPOLATE[] = "INTERP";
  28. constexpr char COMMAND_RASTER_FILL_KRIGING[] = "KRIGING";
  29. constexpr char COMMAND_RASTER_FILL_KRIGING_KNN[] = "KRIGING_KNN";
  30. constexpr char COMMAND_RASTER_PROJ_TYPE[] = "PROJ";
  31. constexpr char COMMAND_RASTER_SF_PROJ_TYPE[] = "SF_PROJ";
  32. constexpr char COMMAND_RASTER_INTERP_MAX_EDGE_LENGTH[] = "MAX_EDGE_LENGTH";
  33. constexpr char COMMAND_RASTER_PROJ_MIN[] = "MIN";
  34. constexpr char COMMAND_RASTER_PROJ_MAX[] = "MAX";
  35. constexpr char COMMAND_RASTER_PROJ_AVG[] = "AVG";
  36. constexpr char COMMAND_RASTER_PROJ_MED[] = "MED";
  37. constexpr char COMMAND_RASTER_PROJ_INVERSE_VAR[] = "INV_VAR";
  38. constexpr char COMMAND_RASTER_RESAMPLE[] = "RESAMPLE";
  39. //2.5D Volume calculation specific commands
  40. constexpr char COMMAND_VOLUME[] = "VOLUME";
  41. constexpr char COMMAND_VOLUME_GROUND_IS_FIRST[] = "GROUND_IS_FIRST";
  42. constexpr char COMMAND_VOLUME_CONST_HEIGHT[] = "CONST_HEIGHT";
  43. static bool ReadProjectionType(ccCommandLineInterface& cmd, ccRasterGrid::ProjectionType& projType, QString& stdDevSFDesc)
  44. {
  45. QString option = cmd.arguments().takeFirst().toUpper();
  46. stdDevSFDesc.clear();
  47. if (option == COMMAND_RASTER_PROJ_MIN)
  48. {
  49. projType = ccRasterGrid::PROJ_MINIMUM_VALUE;
  50. }
  51. else if (option == COMMAND_RASTER_PROJ_MAX)
  52. {
  53. projType = ccRasterGrid::PROJ_MAXIMUM_VALUE;
  54. }
  55. else if (option == COMMAND_RASTER_PROJ_AVG)
  56. {
  57. projType = ccRasterGrid::PROJ_AVERAGE_VALUE;
  58. }
  59. else if (option == COMMAND_RASTER_PROJ_MED)
  60. {
  61. projType = ccRasterGrid::PROJ_MEDIAN_VALUE;
  62. }
  63. else if (option == COMMAND_RASTER_PROJ_INVERSE_VAR)
  64. {
  65. projType = ccRasterGrid::PROJ_INVERSE_VAR_VALUE;
  66. // we expect the std. dev. SF index as well
  67. if (cmd.arguments().size() != 0)
  68. {
  69. stdDevSFDesc = cmd.arguments().takeFirst();
  70. }
  71. else
  72. {
  73. cmd.error(QString("Expecting the std. dev. SF index after %1").arg(option));
  74. return false;
  75. }
  76. }
  77. else
  78. {
  79. assert(false);
  80. cmd.error(QString("Unknown projection type: %1").arg(option));
  81. return false;
  82. }
  83. return true;
  84. }
  85. static ccRasterGrid::EmptyCellFillOption GetEmptyCellFillingStrategy(QString option, ccCommandLineInterface& cmd)
  86. {
  87. if (option == COMMAND_RASTER_FILL_MIN_HEIGHT)
  88. {
  89. return ccRasterGrid::FILL_MINIMUM_HEIGHT;
  90. }
  91. else if (option == COMMAND_RASTER_FILL_MAX_HEIGHT)
  92. {
  93. return ccRasterGrid::FILL_MAXIMUM_HEIGHT;
  94. }
  95. else if (option == COMMAND_RASTER_FILL_CUSTOM_HEIGHT)
  96. {
  97. return ccRasterGrid::FILL_CUSTOM_HEIGHT;
  98. }
  99. else if (option == COMMAND_RASTER_FILL_INTERPOLATE)
  100. {
  101. return ccRasterGrid::INTERPOLATE_DELAUNAY;
  102. }
  103. else if (option == COMMAND_RASTER_FILL_KRIGING)
  104. {
  105. return ccRasterGrid::KRIGING;
  106. }
  107. else
  108. {
  109. assert(false);
  110. cmd.warning(QString("Unknown empty cell filling strategy: %1 (defaulting to 'leave empty')").arg(option));
  111. return ccRasterGrid::LEAVE_EMPTY;
  112. }
  113. }
  114. CommandRasterize::CommandRasterize()
  115. : ccCommandLineInterface::Command("Rasterize", COMMAND_RASTERIZE)
  116. {}
  117. bool CommandRasterize::process(ccCommandLineInterface &cmd)
  118. {
  119. cmd.print("[RASTERIZE]");
  120. //look for local options
  121. double gridStep = 0;
  122. bool outputCloud = false;
  123. bool outputRasterZ = false;
  124. bool outputRasterSFs = false;
  125. bool outputRasterRGB = false;
  126. bool outputMesh = false;
  127. bool resample = false;
  128. double customHeight = std::numeric_limits<double>::quiet_NaN();
  129. int vertDir = 2;
  130. ccRasterGrid::ProjectionType projectionType = ccRasterGrid::PROJ_AVERAGE_VALUE;
  131. ccRasterGrid::ProjectionType sfProjectionType = ccRasterGrid::PROJ_AVERAGE_VALUE;
  132. ccRasterGrid::EmptyCellFillOption emptyCellFillStrategy = ccRasterGrid::LEAVE_EMPTY;
  133. ccRasterGrid::DelaunayInterpolationParams dInterpParams;
  134. ccRasterGrid::KrigingParams krigingParams;
  135. {
  136. // force auto-guess
  137. krigingParams.autoGuess = true;
  138. }
  139. QString projStdDevSFDesc, sfProjStdDevSFDesc;
  140. while (!cmd.arguments().empty())
  141. {
  142. QString argument = cmd.arguments().front();
  143. if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_CLOUD))
  144. {
  145. //local option confirmed, we can move on
  146. cmd.arguments().pop_front();
  147. if (outputMesh)
  148. {
  149. cmd.warning("Can't output the grid as a mesh AND a cloud at the same time");
  150. }
  151. else
  152. {
  153. outputCloud = true;
  154. }
  155. }
  156. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_MESH))
  157. {
  158. //local option confirmed, we can move on
  159. cmd.arguments().pop_front();
  160. if (outputCloud)
  161. {
  162. cmd.warning("Can't output the grid as a mesh AND a cloud at the same time");
  163. }
  164. else
  165. {
  166. outputMesh = true;
  167. }
  168. }
  169. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_RASTER_Z))
  170. {
  171. //local option confirmed, we can move on
  172. cmd.arguments().pop_front();
  173. outputRasterZ = true;
  174. outputRasterSFs = false;
  175. }
  176. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_RASTER_Z_AND_SF))
  177. {
  178. //local option confirmed, we can move on
  179. cmd.arguments().pop_front();
  180. outputRasterZ = true;
  181. outputRasterSFs = true;
  182. }
  183. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_RASTER_RGB))
  184. {
  185. //local option confirmed, we can move on
  186. cmd.arguments().pop_front();
  187. outputRasterRGB = true;
  188. }
  189. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_STEP))
  190. {
  191. //local option confirmed, we can move on
  192. cmd.arguments().pop_front();
  193. bool ok;
  194. gridStep = cmd.arguments().takeFirst().toDouble(&ok);
  195. if (!ok || gridStep <= 0)
  196. {
  197. return cmd.error(QString("Invalid grid step value! (after %1)").arg(COMMAND_GRID_STEP));
  198. }
  199. }
  200. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_CUSTOM_HEIGHT))
  201. {
  202. //local option confirmed, we can move on
  203. cmd.arguments().pop_front();
  204. bool ok;
  205. customHeight = cmd.arguments().takeFirst().toDouble(&ok);
  206. if (!ok)
  207. {
  208. return cmd.error(QString("Invalid custom height value! (after %1)").arg(COMMAND_RASTER_CUSTOM_HEIGHT));
  209. }
  210. }
  211. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_VERT_DIR))
  212. {
  213. //local option confirmed, we can move on
  214. cmd.arguments().pop_front();
  215. bool ok;
  216. vertDir = cmd.arguments().takeFirst().toInt(&ok);
  217. if (!ok || vertDir < 0 || vertDir > 2)
  218. {
  219. return cmd.error(QString("Invalid vert. direction! (after %1)").arg(COMMAND_GRID_VERT_DIR));
  220. }
  221. }
  222. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_FILL_EMPTY_CELLS))
  223. {
  224. //local option confirmed, we can move on
  225. cmd.arguments().pop_front();
  226. emptyCellFillStrategy = GetEmptyCellFillingStrategy(cmd.arguments().takeFirst().toUpper(), cmd);
  227. }
  228. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_PROJ_TYPE))
  229. {
  230. //local option confirmed, we can move on
  231. cmd.arguments().pop_front();
  232. if (!ReadProjectionType(cmd, projectionType, projStdDevSFDesc))
  233. {
  234. return false;
  235. }
  236. }
  237. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_SF_PROJ_TYPE))
  238. {
  239. //local option confirmed, we can move on
  240. cmd.arguments().pop_front();
  241. if (!ReadProjectionType(cmd, sfProjectionType, sfProjStdDevSFDesc))
  242. {
  243. return false;
  244. }
  245. }
  246. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_INTERP_MAX_EDGE_LENGTH))
  247. {
  248. //local option confirmed, we can move on
  249. cmd.arguments().pop_front();
  250. bool ok = false;
  251. dInterpParams.maxEdgeLength = cmd.arguments().takeFirst().toDouble(&ok);
  252. if (!ok || dInterpParams.maxEdgeLength < 0.0)
  253. {
  254. return cmd.error(QString("Invalid max edge length value! (after %1)").arg(COMMAND_RASTER_INTERP_MAX_EDGE_LENGTH));
  255. }
  256. }
  257. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_FILL_KRIGING_KNN))
  258. {
  259. //local option confirmed, we can move on
  260. cmd.arguments().pop_front();
  261. bool ok = false;
  262. krigingParams.kNN = cmd.arguments().takeFirst().toInt(&ok);
  263. if (!ok || krigingParams.kNN <= 0)
  264. {
  265. return cmd.error(QString("Invalid Kriging knn value! (after %1)").arg(COMMAND_RASTER_FILL_KRIGING_KNN));
  266. }
  267. }
  268. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_RASTER_RESAMPLE))
  269. {
  270. //local option confirmed, we can move on
  271. cmd.arguments().pop_front();
  272. resample = true;
  273. }
  274. else
  275. {
  276. break;
  277. }
  278. }
  279. // there can be only one (std. dev. SF ;)
  280. QString stdDevSFDesc;
  281. if (!projStdDevSFDesc.isEmpty())
  282. {
  283. if (!sfProjStdDevSFDesc.isEmpty() && projStdDevSFDesc != sfProjStdDevSFDesc)
  284. {
  285. 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.");
  286. }
  287. stdDevSFDesc = projStdDevSFDesc;
  288. }
  289. else
  290. {
  291. stdDevSFDesc = sfProjStdDevSFDesc;
  292. }
  293. if (gridStep == 0)
  294. {
  295. return cmd.error(QString("Grid step value not defined (use %1)").arg(COMMAND_GRID_STEP));
  296. }
  297. if (std::isnan(customHeight))
  298. {
  299. if (emptyCellFillStrategy == ccRasterGrid::FILL_CUSTOM_HEIGHT)
  300. {
  301. cmd.warning("[Rasterize] The filling stragety is set to 'fill with custom height' but no custom height was defined...");
  302. emptyCellFillStrategy = ccRasterGrid::LEAVE_EMPTY;
  303. }
  304. else if (emptyCellFillStrategy == ccRasterGrid::INTERPOLATE_DELAUNAY)
  305. {
  306. 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).");
  307. }
  308. }
  309. if (!outputCloud && !outputMesh && !outputRasterZ && !outputRasterRGB)
  310. {
  311. //if no export target is specified, we chose the cloud by default
  312. outputCloud = true;
  313. }
  314. if (resample && !outputCloud && !outputMesh)
  315. {
  316. cmd.warning("[Rasterize] The 'resample' option is set while the raster won't be exported as a cloud nor as a mesh");
  317. }
  318. //we'll get the first two clouds
  319. for (CLCloudDesc& cloudDesc : cmd.clouds())
  320. {
  321. if (!cloudDesc.pc)
  322. {
  323. assert(false);
  324. continue;
  325. }
  326. int invVarProjSFIndex = -1;
  327. if (projectionType == ccRasterGrid::PROJ_INVERSE_VAR_VALUE)
  328. {
  329. // let's check if the SF description is its name
  330. invVarProjSFIndex = cloudDesc.pc->getScalarFieldIndexByName(stdDevSFDesc.toStdString());
  331. if (invVarProjSFIndex < 0)
  332. {
  333. // let's check if it's a (valid) index then
  334. bool validValue = false;
  335. invVarProjSFIndex = stdDevSFDesc.toInt(&validValue);
  336. if (!validValue)
  337. {
  338. 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));
  339. }
  340. else if (invVarProjSFIndex < 0 || static_cast<unsigned>(invVarProjSFIndex) >= cloudDesc.pc->getNumberOfScalarFields())
  341. {
  342. return cmd.error("[Rasterize] Invalid std. dev. SF index (negative or greater than the number of scalar fields in the cloud");
  343. }
  344. }
  345. }
  346. ccBBox gridBBox = cloudDesc.pc->getOwnBB();
  347. //compute the grid size
  348. unsigned gridWidth = 0;
  349. unsigned gridHeight = 0;
  350. if (!ccRasterGrid::ComputeGridSize(vertDir, gridBBox, gridStep, gridWidth, gridHeight))
  351. {
  352. return cmd.error("Failed to compute the grid dimensions (check input cloud(s) bounding-box)");
  353. }
  354. cmd.print(QString("Grid size: %1 x %2").arg(gridWidth).arg(gridHeight));
  355. if (gridWidth * gridHeight > (1 << 26)) //64 million of cells
  356. {
  357. if (cmd.silentMode())
  358. {
  359. ccLog::Warning("Huge grid detected!");
  360. }
  361. else
  362. {
  363. static bool s_firstTime = true;
  364. 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)
  365. {
  366. return ccLog::Warning("Process cancelled");
  367. }
  368. s_firstTime = false;
  369. }
  370. }
  371. ccRasterGrid grid;
  372. {
  373. //memory allocation
  374. CCVector3d minCorner = gridBBox.minCorner();
  375. if (!grid.init(gridWidth, gridHeight, gridStep, minCorner))
  376. {
  377. //not enough memory
  378. return cmd.error("Not enough memory");
  379. }
  380. //progress dialog
  381. QScopedPointer<ccProgressDialog> pDlg(nullptr);
  382. if (!cmd.silentMode())
  383. {
  384. pDlg.reset(new ccProgressDialog(true, cmd.widgetParent()));
  385. }
  386. ccRasterGrid::InterpolationType interpolationType = ccRasterGrid::InterpolationTypeFromEmptyCellFillOption(emptyCellFillStrategy);
  387. void* interpolationParams = nullptr;
  388. switch (interpolationType)
  389. {
  390. case ccRasterGrid::InterpolationType::DELAUNAY:
  391. interpolationParams = (void*)&dInterpParams;
  392. break;
  393. case ccRasterGrid::InterpolationType::KRIGING:
  394. interpolationParams = (void*)&krigingParams;
  395. break;
  396. default:
  397. // do nothing
  398. break;
  399. }
  400. if (grid.fillWith( cloudDesc.pc,
  401. vertDir,
  402. projectionType,
  403. interpolationType,
  404. interpolationParams,
  405. sfProjectionType,
  406. pDlg.data(),
  407. invVarProjSFIndex )
  408. )
  409. {
  410. grid.fillEmptyCells(emptyCellFillStrategy, customHeight);
  411. 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));
  412. }
  413. else
  414. {
  415. return cmd.error("Rasterize process failed");
  416. }
  417. }
  418. //generate the result entity (cloud by default)
  419. if (outputCloud || outputMesh)
  420. {
  421. ccPointCloud* rasterCloud = nullptr;
  422. try
  423. {
  424. //we always compute the default 'height' layer
  425. std::vector<ccRasterGrid::ExportableFields> exportedStatistics(1);
  426. exportedStatistics.back() = ccRasterGrid::PER_CELL_VALUE;
  427. rasterCloud = grid.convertToCloud( true,
  428. false,
  429. exportedStatistics,
  430. true,
  431. true,
  432. resample,
  433. resample,
  434. cloudDesc.pc,
  435. vertDir,
  436. gridBBox,
  437. 0.0,
  438. true,
  439. true,
  440. nullptr
  441. );
  442. }
  443. catch (const std::bad_alloc&)
  444. {
  445. return cmd.error("Not enough memory");
  446. }
  447. if (!rasterCloud)
  448. {
  449. return cmd.error("Failed to output the raster grid as a cloud");
  450. }
  451. rasterCloud->showColors(cloudDesc.pc->hasColors());
  452. if (rasterCloud->hasScalarFields())
  453. {
  454. rasterCloud->showSF(!cloudDesc.pc->hasColors());
  455. rasterCloud->setCurrentDisplayedScalarField(0);
  456. }
  457. //don't forget the original shift
  458. rasterCloud->copyGlobalShiftAndScale(*cloudDesc.pc);
  459. if (outputCloud)
  460. {
  461. assert(!outputMesh);
  462. //replace current cloud by the restarized version
  463. delete cloudDesc.pc;
  464. cloudDesc.pc = rasterCloud;
  465. cloudDesc.basename += QString("_RASTER");
  466. rasterCloud = nullptr;
  467. if (cmd.autoSaveMode())
  468. {
  469. QString errorStr = cmd.exportEntity(cloudDesc);
  470. if (!errorStr.isEmpty())
  471. {
  472. return cmd.error(errorStr);
  473. }
  474. }
  475. }
  476. else if (outputMesh)
  477. {
  478. std::string errorStr;
  479. CCCoreLib::GenericIndexedMesh* baseMesh = CCCoreLib::PointProjectionTools::computeTriangulation
  480. (
  481. rasterCloud,
  482. CCCoreLib::DELAUNAY_2D_AXIS_ALIGNED,
  483. CCCoreLib::PointProjectionTools::IGNORE_MAX_EDGE_LENGTH,
  484. vertDir,
  485. errorStr
  486. );
  487. if (baseMesh)
  488. {
  489. ccMesh* rasterMesh = new ccMesh(baseMesh, rasterCloud);
  490. delete baseMesh;
  491. baseMesh = nullptr;
  492. rasterCloud->setEnabled(false);
  493. rasterCloud->setVisible(true);
  494. rasterMesh->addChild(rasterCloud);
  495. rasterMesh->setName(rasterCloud->getName());
  496. //rasterCloud->setName("vertices");
  497. rasterMesh->showSF(rasterCloud->sfShown());
  498. rasterMesh->showColors(rasterCloud->colorsShown());
  499. rasterCloud = nullptr; //to avoid deleting it later
  500. cmd.print(QString("[Rasterize] Mesh '%1' successfully generated").arg(rasterMesh->getName()));
  501. CLMeshDesc meshDesc;
  502. meshDesc.mesh = rasterMesh;
  503. meshDesc.basename = cloudDesc.basename + QString("_RASTER_MESH");
  504. meshDesc.path = cloudDesc.path;
  505. QString errorStr = cmd.exportEntity(meshDesc);
  506. if (!errorStr.isEmpty())
  507. {
  508. delete rasterMesh;
  509. return cmd.error(errorStr);
  510. }
  511. //we keep the mesh loaded
  512. cmd.meshes().push_back(meshDesc);
  513. //delete rasterMesh;
  514. //rasterMesh = 0;
  515. }
  516. else
  517. {
  518. cmd.warning( QStringLiteral("[Rasterize] Failed to create output mesh ('%1')")
  519. .arg( QString::fromStdString( errorStr ) ) );
  520. }
  521. }
  522. if (rasterCloud)
  523. {
  524. delete rasterCloud;
  525. rasterCloud = nullptr;
  526. }
  527. }
  528. if (outputRasterZ)
  529. {
  530. ccRasterizeTool::ExportBands bands;
  531. {
  532. bands.height = true;
  533. bands.rgb = false; //not a good idea to mix RGB and height values!
  534. bands.allSFs = outputRasterSFs;
  535. }
  536. QString exportFilename = cmd.getExportFilename(cloudDesc, "tif", outputRasterSFs ? "RASTER_Z_AND_SF" : "RASTER_Z", nullptr, !cmd.addTimestamp());
  537. if (exportFilename.isEmpty())
  538. {
  539. exportFilename = "rasterZ.tif";
  540. }
  541. ccRasterizeTool::ExportGeoTiff(exportFilename, bands, emptyCellFillStrategy, grid, gridBBox, vertDir, customHeight, cloudDesc.pc);
  542. }
  543. if (outputRasterRGB)
  544. {
  545. ccRasterizeTool::ExportBands bands;
  546. {
  547. bands.rgb = true;
  548. bands.height = false; //not a good idea to mix RGB and height values!
  549. bands.allSFs = outputRasterSFs;
  550. }
  551. QString exportFilename = cmd.getExportFilename(cloudDesc, "tif", "RASTER_RGB", nullptr, !cmd.addTimestamp());
  552. if (exportFilename.isEmpty())
  553. {
  554. exportFilename = "rasterRGB.tif";
  555. }
  556. ccRasterizeTool::ExportGeoTiff(exportFilename, bands, emptyCellFillStrategy, grid, gridBBox, vertDir, customHeight, cloudDesc.pc);
  557. }
  558. }
  559. return true;
  560. }
  561. CommandVolume25D::CommandVolume25D()
  562. : ccCommandLineInterface::Command("2.5D Volume Calculation", COMMAND_VOLUME)
  563. {}
  564. bool CommandVolume25D::process(ccCommandLineInterface &cmd)
  565. {
  566. cmd.print("[2.5D VOLUME]");
  567. //look for local options
  568. bool groundIsFirst = false;
  569. double gridStep = 0;
  570. double constHeight = std::numeric_limits<double>::quiet_NaN();
  571. bool outputMesh = false;
  572. int vertDir = 2;
  573. while (!cmd.arguments().empty())
  574. {
  575. QString argument = cmd.arguments().front();
  576. if (ccCommandLineInterface::IsCommand(argument, COMMAND_VOLUME_GROUND_IS_FIRST))
  577. {
  578. //local option confirmed, we can move on
  579. cmd.arguments().pop_front();
  580. groundIsFirst = true;
  581. }
  582. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_OUTPUT_MESH))
  583. {
  584. //local option confirmed, we can move on
  585. cmd.arguments().pop_front();
  586. outputMesh = true;
  587. }
  588. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_STEP))
  589. {
  590. //local option confirmed, we can move on
  591. cmd.arguments().pop_front();
  592. bool ok;
  593. gridStep = cmd.arguments().takeFirst().toDouble(&ok);
  594. if (!ok || gridStep <= 0)
  595. {
  596. return cmd.error(QString("Invalid grid step value! (after %1)").arg(COMMAND_GRID_STEP));
  597. }
  598. }
  599. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_VOLUME_CONST_HEIGHT))
  600. {
  601. //local option confirmed, we can move on
  602. cmd.arguments().pop_front();
  603. bool ok;
  604. constHeight = cmd.arguments().takeFirst().toDouble(&ok);
  605. if (!ok)
  606. {
  607. return cmd.error(QString("Invalid const. height value! (after %1)").arg(COMMAND_VOLUME_CONST_HEIGHT));
  608. }
  609. }
  610. else if (ccCommandLineInterface::IsCommand(argument, COMMAND_GRID_VERT_DIR))
  611. {
  612. //local option confirmed, we can move on
  613. cmd.arguments().pop_front();
  614. bool ok;
  615. vertDir = cmd.arguments().takeFirst().toInt(&ok);
  616. if (!ok || vertDir < 0 || vertDir > 2)
  617. {
  618. return cmd.error(QString("Invalid vert. direction! (after %1)").arg(COMMAND_GRID_VERT_DIR));
  619. }
  620. }
  621. else
  622. {
  623. //unrecognized argument (probably another command?)
  624. break;
  625. }
  626. }
  627. if (gridStep == 0)
  628. {
  629. return cmd.error(QString("Grid step value not defined (use %1)").arg(COMMAND_GRID_STEP));
  630. }
  631. //we'll get the first two clouds
  632. CLCloudDesc *ground = nullptr;
  633. CLCloudDesc *ceil = nullptr;
  634. {
  635. CLCloudDesc* clouds[2] = { nullptr, nullptr };
  636. int index = 0;
  637. if (!cmd.clouds().empty())
  638. {
  639. clouds[index++] = &cmd.clouds()[0];
  640. if (std::isnan(constHeight) && cmd.clouds().size() > 1)
  641. {
  642. clouds[index++] = &cmd.clouds()[1];
  643. }
  644. }
  645. int expectedCount = std::isnan(constHeight) ? 2 : 1;
  646. if (index != expectedCount)
  647. {
  648. return cmd.error(QString("Not enough loaded entities (%1 found, %2 expected)").arg(index).arg(expectedCount));
  649. }
  650. if (index == 2 && groundIsFirst)
  651. {
  652. //put them in the right order (ground then ceil)
  653. std::swap(clouds[0], clouds[1]);
  654. }
  655. ceil = clouds[0];
  656. ground = clouds[1];
  657. }
  658. ccBBox gridBBox = ceil ? ceil->pc->getOwnBB() : ccBBox();
  659. if (ground)
  660. {
  661. gridBBox += ground->pc->getOwnBB();
  662. }
  663. //compute the grid size
  664. unsigned gridWidth = 0;
  665. unsigned gridHeight = 0;
  666. if (!ccRasterGrid::ComputeGridSize(vertDir, gridBBox, gridStep, gridWidth, gridHeight))
  667. {
  668. return cmd.error("Failed to compute the grid dimensions (check input cloud(s) bounding-box)");
  669. }
  670. cmd.print(QString("Grid size: %1 x %2").arg(gridWidth).arg(gridHeight));
  671. if (gridWidth * gridHeight > (1 << 26)) //64 million of cells
  672. {
  673. if (cmd.silentMode())
  674. {
  675. ccLog::Warning("Huge grid detected!");
  676. }
  677. else
  678. {
  679. static bool s_firstTime = true;
  680. 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)
  681. {
  682. return ccLog::Warning("Process cancelled");
  683. }
  684. s_firstTime = false;
  685. }
  686. }
  687. ccRasterGrid grid;
  688. ccVolumeCalcTool::ReportInfo reportInfo;
  689. if (ccVolumeCalcTool::ComputeVolume(
  690. grid,
  691. ground ? ground->pc : nullptr,
  692. ceil ? ceil->pc : nullptr,
  693. gridBBox,
  694. vertDir,
  695. gridStep,
  696. gridWidth,
  697. gridHeight,
  698. ccRasterGrid::PROJ_AVERAGE_VALUE,
  699. ccRasterGrid::LEAVE_EMPTY,
  700. 0.0,
  701. ccRasterGrid::LEAVE_EMPTY,
  702. 0.0,
  703. reportInfo,
  704. constHeight,
  705. constHeight,
  706. cmd.silentMode() ? nullptr : cmd.widgetParent()))
  707. {
  708. CLCloudDesc* desc = ceil ? ceil : ground;
  709. assert(desc);
  710. //save repot in a separate text file
  711. {
  712. QString txtFilename = QString("%1/VolumeCalculationReport").arg(desc->path);
  713. if (cmd.addTimestamp())
  714. txtFilename += QString("_%1").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh'h'mm"));
  715. txtFilename += QString(".txt");
  716. QFile txtFile(txtFilename);
  717. txtFile.open(QIODevice::WriteOnly | QIODevice::Text);
  718. QTextStream txtStream(&txtFile);
  719. txtStream << reportInfo.toText() << endl;
  720. txtFile.close();
  721. }
  722. //generate the result entity (cloud by default)
  723. {
  724. ccPointCloud* rasterCloud = ccVolumeCalcTool::ConvertGridToCloud(grid, gridBBox, vertDir, true);
  725. if (!rasterCloud)
  726. {
  727. return cmd.error("Failed to output the volume grid");
  728. }
  729. if (rasterCloud->hasScalarFields())
  730. {
  731. //convert SF to RGB
  732. //rasterCloud->setCurrentDisplayedScalarField(0);
  733. rasterCloud->convertCurrentScalarFieldToColors(false);
  734. rasterCloud->showColors(true);
  735. }
  736. ccMesh* rasterMesh = nullptr;
  737. if (outputMesh)
  738. {
  739. std::string errorStr;
  740. CCCoreLib::GenericIndexedMesh* baseMesh = CCCoreLib::PointProjectionTools::computeTriangulation(rasterCloud,
  741. CCCoreLib::DELAUNAY_2D_AXIS_ALIGNED,
  742. CCCoreLib::PointProjectionTools::IGNORE_MAX_EDGE_LENGTH,
  743. vertDir,
  744. errorStr);
  745. if (baseMesh)
  746. {
  747. rasterMesh = new ccMesh(baseMesh, rasterCloud);
  748. delete baseMesh;
  749. baseMesh = nullptr;
  750. }
  751. if (rasterMesh)
  752. {
  753. rasterCloud->setEnabled(false);
  754. rasterCloud->setVisible(true);
  755. rasterMesh->addChild(rasterCloud);
  756. rasterMesh->setName(rasterCloud->getName());
  757. rasterCloud->setName("vertices");
  758. rasterMesh->showSF(rasterCloud->sfShown());
  759. rasterMesh->showColors(rasterCloud->colorsShown());
  760. cmd.print(QString("[Volume] Mesh '%1' successfully generated").arg(rasterMesh->getName()));
  761. }
  762. else
  763. {
  764. delete rasterCloud;
  765. return cmd.error( QStringLiteral("[Voume] Failed to create output mesh ('%1')")
  766. .arg( QString::fromStdString( errorStr ) ) );
  767. }
  768. }
  769. CLEntityDesc* outputDesc = nullptr;
  770. if (rasterMesh)
  771. {
  772. CLMeshDesc meshDesc;
  773. meshDesc.mesh = rasterMesh;
  774. meshDesc.basename = desc->basename;
  775. meshDesc.path = desc->path;
  776. cmd.meshes().push_back(meshDesc);
  777. outputDesc = &cmd.meshes().back();
  778. }
  779. else
  780. {
  781. CLCloudDesc cloudDesc;
  782. cloudDesc.pc = rasterCloud;
  783. cloudDesc.basename = desc->basename;
  784. cloudDesc.path = desc->path;
  785. cmd.clouds().push_back(cloudDesc);
  786. outputDesc = &cmd.clouds().back();
  787. }
  788. //save result
  789. if (outputDesc && cmd.autoSaveMode())
  790. {
  791. QString outputFilename;
  792. QString errorStr = cmd.exportEntity(*outputDesc, "HEIGHT_DIFFERENCE", &outputFilename);
  793. if (!errorStr.isEmpty())
  794. cmd.warning(errorStr);
  795. }
  796. }
  797. }
  798. else
  799. {
  800. return cmd.error("Failed to compte the volume");
  801. }
  802. return true;
  803. }