/**************************************************************************** Copyright (C) 2010-2019 the Office National des Forêts (ONF), France All rights reserved. Contact : alexandre.piboule@onf.fr Developers : Alexandre PIBOULE (ONF) This file is part of PluginONF library. PluginONF is free library: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. PluginONF is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PluginONF. If not, see . *****************************************************************************/ #include "onf_stepcomputedsm.h" #include "ct_log/ct_logmanager.h" #define EPSILON 0.000001 ONF_StepComputeDSM::ONF_StepComputeDSM() : SuperClass() { _gridsize = 0.5; _convertNA = true; _gridMode = 1; _NAMode = 0; _naReplaceValue = 0; _xBase = 0; _yBase = 0; _name = tr("MNS"); } QString ONF_StepComputeDSM::description() const { return tr("Créer MNS (Zmax)"); } QString ONF_StepComputeDSM::detailledDescription() const { return tr("Cette étape permet de générer un Modèle Numérique de Surface (MNS).
" "Le MNS est calculé comme un raster Zmax à la résolution spécifiée."); } QString ONF_StepComputeDSM::inputDescription() const { return SuperClass::inputDescription() + tr("

Il faut sélectionner un nuage de points (Points végétation) qui sera utilisé pour calculer le MNS.
" "Optionnellement, une emprise peut être choisie, afin de caler l'extension du raster MNS (il faudra sélectionner l'option correspondante dans les paramètres)."); } QString ONF_StepComputeDSM::outputDescription() const { return tr("Un raster MNS est ajouté à la copie du résultat d'entrée, au même niveau que la scène choisie."); } QString ONF_StepComputeDSM::detailsDescription() const { return tr("Un raster MNS est créé pour chaque nuage de point en entrée.
" "Dans chaque cellule du MNS la valeur est l'altitude du point le plus haut.
" "Si il n'y a aucun point, la cellule est initialisée à valeur manquante."); } CT_VirtualAbstractStep* ONF_StepComputeDSM::createNewInstance() const { // cree une copie de cette etape return new ONF_StepComputeDSM(); } /////////////////////// PROTECTED /////////////////////// void ONF_StepComputeDSM::declareInputModels(CT_StepInModelStructureManager& manager) { manager.addResult(_inResult, tr("Points végétation")); manager.setZeroOrMoreRootGroup(_inResult, _inZeroOrMoreRootGroup); manager.addGroup(_inZeroOrMoreRootGroup, _inGroup); manager.addItem(_inGroup, _inScene, tr("Points végétation")); manager.addItem(_inGroup, _inArea, tr("Emprise")); } void ONF_StepComputeDSM::fillPostInputConfigurationDialog(CT_StepConfigurableDialog* postInputConfigDialog) { postInputConfigDialog->addDouble(tr("Résolution du raster :"), "cm", 1, 1000, 0, _gridsize, 100); postInputConfigDialog->addString(tr("Nom à donner au raster"), "", _name, tr("Attention : ce paramètre ne peut être pris en compte qu'à l'ajout de l'étape ou au chargement d'un script.
Une modification de ce paramètre lors d'une reconfiguration des paramètres se répercutera dans les scripts exportés ultérieurement, mais pas dans la session en cours. ")); postInputConfigDialog->addSeparationLine(); postInputConfigDialog->addBool(tr("Remplacer les valeurs NA par :"), "", "", _convertNA, tr("La section suivante définit s'il faut remplacer les valeurs manquantes du raster par une valeur indiquée.
Si cette case est coché, toutes les valeurs manquantes seront remplacée par la valeur indiquée.")); CT_ButtonGroup &bg_NAMode = postInputConfigDialog->addButtonGroup(_NAMode); postInputConfigDialog->addExcludeValue("", "", tr("Min(MNS)"), bg_NAMode, 0, tr("Les valeurs manquantes seront remplacées par la plus petite valeur observée dans le raster. ")); postInputConfigDialog->addExcludeValue("", "", tr("La valeur ci-dessous"), bg_NAMode, 1, tr("Les valeurs manquantes seront remplacées par la valeur choisie. ")); postInputConfigDialog->addDouble(tr("Valeur de remplacement des NA :"), "m", -std::numeric_limits::max(), std::numeric_limits::max(), 2, _naReplaceValue); postInputConfigDialog->addSeparationLine(); postInputConfigDialog->addText(tr("Quelle emprise utiliser ?"),"", "", tr("La section suivante définit comment l'extension selon les axes X et Y du raster est déterminée.")); CT_ButtonGroup &bg_gridMode = postInputConfigDialog->addButtonGroup(_gridMode); postInputConfigDialog->addExcludeValue("", "", tr("La boite englobante de la scène"), bg_gridMode, 0, tr("Dans ce cas l'extension est directement calculée à partir des X et Y maximum et minimum des points de la scène d'entrée. ")); postInputConfigDialog->addExcludeValue("", "", tr("L'emprise précédement sélectionnée "), bg_gridMode, 2, tr("Cette option ne fonctionne que si une emprise a été séléctionnée dans les résultats d'entrée (optionnel). Dans ce cas c'est cette emprise qui détermine l'extension du raster. Cette option est utilisée dans le cas de dallages pré-définis. ")); postInputConfigDialog->addExcludeValue("", "", tr("Recaler par rapport aux coordonnées suivantes :"), bg_gridMode, 1, tr("Cette option calcule d'abord l'entension des points de la scène (comme la première option), mais l'agrandit de façon à ce que les coordonnées du coin en bas à gauche du raster tombe \"juste\" par rapport aux coordonnées indiquées (cette à dire qu'elles soient égales à ces coordonnées de référence plus un multiple de la résolution). C'est l'option par défaut, permettant des rasters cohérents entre eux. ")); postInputConfigDialog->addDouble(tr("Coordonnée X :"), "", -std::numeric_limits::max(), std::numeric_limits::max(), 4, _xBase, 1, tr("Coordonnée X de référence pour la troisième option. ")); postInputConfigDialog->addDouble(tr("Coordonnée Y :"), "", -std::numeric_limits::max(), std::numeric_limits::max(), 4, _yBase, 1, tr("Coordonnée Y de référence pour la troisième option. ")); } void ONF_StepComputeDSM::declareOutputModels(CT_StepOutModelStructureManager& manager) { manager.addResultCopy(_inResult); manager.addItem(_inGroup, _outDSM, _name); } void ONF_StepComputeDSM::compute() { for (CT_StandardItemGroup* group : _inGroup.iterateOutputs(_inResult)) { for (const CT_AbstractItemDrawableWithPointCloud* inScene : group->singularItems(_inScene)) { if (isStopped()) {return;} const CT_AbstractPointCloudIndex *pointCloudIndex = inScene->pointCloudIndex(); if (pointCloudIndex->size() > 0) { const CT_AbstractGeometricalItem *emprise = group->singularItem(_inArea); // Creation du raster double minX = std::floor(inScene->minX()*100.0) / 100.0; double minY = std::floor(inScene->minY()*100.0) / 100.0; double maxX = std::ceil(inScene->maxX()*100.0) / 100.0; double maxY = std::ceil(inScene->maxY()*100.0) / 100.0; if (_gridMode == 2 && emprise != nullptr && emprise->hasBoundingBox()) { Eigen::Vector3d min, max; emprise->boundingBox(min, max); minX = min(0); minY = min(1); maxX = max(0) - EPSILON; maxY = max(1) - EPSILON; } else if (_gridMode == 1) { minX = std::floor((inScene->minX() - _xBase - 1) / _gridsize) * _gridsize + _xBase; minY = std::floor((inScene->minY() - _yBase - 1) / _gridsize) * _gridsize + _yBase; while (minX < inScene->minX()) {minX += _gridsize;}; while (minY < inScene->minY()) {minY += _gridsize;}; while (minX > inScene->minX()) {minX -= _gridsize;}; while (minY > inScene->minY()) {minY -= _gridsize;}; } CT_Image2D* mns = CT_Image2D::createImage2DFromXYCoords(minX, minY, maxX, maxY, _gridsize, inScene->minZ(), -9999, -9999); // Création MNS (version Zmax) CT_PointIterator itP(pointCloudIndex); while(itP.hasNext() && !isStopped()) { const CT_Point &point =itP.next().currentPoint(); mns->setMaxValueAtCoords(point(0), point(1), static_cast(point(2))); } setProgress(50.0f); // ajout du raster MNS mns->computeMinMax(); group->addSingularItem(_outDSM, mns); if (_convertNA) { float minNaVal = mns->dataMin(); if (_NAMode == 1) {minNaVal = static_cast(_naReplaceValue);} for (size_t index = 0 ; index < mns->nCells() ; index++) { if (qFuzzyCompare(mns->valueAtIndex(index),mns->NA())) {mns->setValueAtIndex(index, minNaVal);} } } } } setProgress(100.0f); } }