/**************************************************************************** Copyright (C) 2010-2012 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_stepadjustplotposition02.h" #include "documentinterface.h" #include "interfacesforplugin.h" #include #include #include #include #include ONF_StepAdjustPlotPosition02::ONF_StepAdjustPlotPosition02() : SuperClass() { _dataContainer = nullptr; setManual(true); _m_status = 0; _m_doc = nullptr; } ONF_StepAdjustPlotPosition02::~ONF_StepAdjustPlotPosition02() { } QString ONF_StepAdjustPlotPosition02::description() const { return tr("Recaler une placette terrain"); } QString ONF_StepAdjustPlotPosition02::detailledDescription() const { return tr("Cette étape permet de recaler précisément une placette terrain (positions et dbh des arbres) sur un nuage LIDAR.
" "En entrée il faut :
" "- Des données terrains (Cercles 2D avec bdh, hauteur, IDarbre, IDplacette, espèce optionnelle, commentaire optionnel)
" "- Un résultats contenant :
" " * Un nuage de points
" " * Des attributs LAS (optionnel)
" " * Un raster \"maxima\" (apex)
" " * Un MNS
" " * Une emprise circulaire pour la placette
" " * Des attributs hauteurs de points (optionnel)
" "- Optionnellement un MNT."); } QString ONF_StepAdjustPlotPosition02::inputDescription() const { return SuperClass::inputDescription() + tr("

"); } QString ONF_StepAdjustPlotPosition02::outputDescription() const { return SuperClass::outputDescription() + tr("

"); } QString ONF_StepAdjustPlotPosition02::detailsDescription() const { return tr(""); } CT_VirtualAbstractStep* ONF_StepAdjustPlotPosition02::createNewInstance() const { return new ONF_StepAdjustPlotPosition02(); } //////////////////// PROTECTED METHODS ////////////////// void ONF_StepAdjustPlotPosition02::declareInputModels(CT_StepInModelStructureManager& manager) { manager.addResult(_inResPlot, tr("Placette"), "", true); manager.setZeroOrMoreRootGroup(_inResPlot, _inZeroOrMoreRootGroupRef); manager.addGroup(_inZeroOrMoreRootGroupRef, _inGrp); manager.addItem(_inGrp, _inRef, tr("Arbre")); manager.addItemAttribute(_inRef, _inRefDbh, CT_AbstractCategory::DATA_VALUE, tr("DBH")); manager.addItemAttribute(_inRef, _inRefHeight, CT_AbstractCategory::DATA_VALUE, tr("Height")); manager.addItemAttribute(_inRef, _inRefID, CT_AbstractCategory::DATA_ID, tr("IDtree")); manager.addItemAttribute(_inRef, _inRefIDplot, CT_AbstractCategory::DATA_ID, tr("IDplot")); manager.addItemAttribute(_inRef, _inSpecies, CT_AbstractCategory::DATA_VALUE, tr("Species")); manager.addItemAttribute(_inRef, _inComment, CT_AbstractCategory::DATA_VALUE, tr("Comment")); manager.addResult(_inResScene, tr("Scène"), "", true); manager.setZeroOrMoreRootGroup(_inResScene, _inZeroOrMoreRootGroupSc); manager.addGroup(_inZeroOrMoreRootGroupSc, _inGrpSc); manager.addItem(_inGrpSc, _inScene, tr("Scène")); manager.addItem(_inGrpSc, _inLasAtt, tr("Attributs LAS")); manager.addItem(_inGrpSc, _inMaxima, tr("Maxima")); manager.addItem(_inGrpSc, _inMns, tr("MNS")); manager.addItem(_inGrpSc, _inArea, tr("Emprise")); manager.addPointAttribute(_inGrpSc, _inHeightAtt, tr("Attribut Hauteur")); manager.addResult(_inResultDTM, tr("MNT"), "", true); manager.setZeroOrMoreRootGroup(_inResultDTM, _inZeroOrMoreRootGroupDTM); manager.addGroup(_inZeroOrMoreRootGroupDTM, _inGroupDTM); manager.addItem(_inGroupDTM, _inDTM, tr("MNT")); } void ONF_StepAdjustPlotPosition02::declareOutputModels(CT_StepOutModelStructureManager& manager) { manager.addResultCopy(_inResPlot); manager.addItem(_inGrp, _outCircle, tr("Arbre déplacé")); manager.addItemAttribute(_outCircle, _outDBHAtt, CT_AbstractCategory::DATA_NUMBER, tr("DBH")); manager.addItemAttribute(_outCircle, _outHeightAtt, CT_AbstractCategory::DATA_NUMBER, tr("Height")); manager.addItemAttribute(_outCircle, _outPlotIDAtt, CT_AbstractCategory::DATA_ID, tr("IDplot")); manager.addItemAttribute(_outCircle, _outTreeIDAtt, CT_AbstractCategory::DATA_ID, tr("IDtree")); manager.addItemAttribute(_outCircle, _outSpeciesAtt, CT_AbstractCategory::DATA_VALUE, tr("Species")); manager.addItemAttribute(_outCircle, _outTransXAtt, CT_AbstractCategory::DATA_NUMBER, tr("TransX")); manager.addItemAttribute(_outCircle, _outTransYAtt, CT_AbstractCategory::DATA_NUMBER, tr("TransY")); manager.addItemAttribute(_outCircle, _outMovedAtt, CT_AbstractCategory::DATA_VALUE, tr("Moved")); manager.addItemAttribute(_outCircle, _outZPointAtt, CT_AbstractCategory::DATA_NUMBER, tr("zPointClicked")); } void ONF_StepAdjustPlotPosition02::fillPostInputConfigurationDialog(CT_StepConfigurableDialog* postInputConfigDialog) { postInputConfigDialog->addFileChoice(tr("Fichier d'export des placettes recalées"), CT_FileChoiceButton::OneNewFile, tr("Fichier Texte (*.txt)"), _exportFile); postInputConfigDialog->addFileChoice(tr("Fichier de couleurs pour les espèces"), CT_FileChoiceButton::OneExistingFile, tr("Fichier CSV (*.csv)"), _paramFile, tr("Fichier contenant sur chaque ligne : CodeEspèce,composanteRouge,compostanteBleue,composanteVerte")); } void ONF_StepAdjustPlotPosition02::compute() { _dataContainer = new ONF_ActionAdjustPlotPosition02_dataContainer(); for (const CT_Image2D* imageIn : _inDTM.iterateInputs(_inResultDTM)) { _dataContainer->_dtm = const_cast*>(imageIn); } if (!_paramFile.isEmpty() && QFile::exists(_paramFile.first())) { QFile fSpecies(_paramFile.first()); if (fSpecies.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream stream(&fSpecies); while (!stream.atEnd()) { QString line = stream.readLine(); if (!line.isNull()) { QStringList values = line.split(QRegExp("[;,]")); if (values.size() >= 4) { QString spCode = values.at(0).toUpper(); bool okR, okG, okB; int colR = values.at(1).toInt(&okR); int colG = values.at(2).toInt(&okG); int colB = values.at(3).toInt(&okB); if (!spCode.isEmpty() && okR && okG && okB && colR >= 0 && colG >= 0 && colB >= 0 && colR <= 255 && colG <= 255 && colB <= 255) { _dataContainer->_spColors.insert(spCode, QColor(colR, colG, colB)); } } } } } } _m_status = 0; double maxPointHeight = 0; for (const CT_StandardItemGroup* grp : _inGrpSc.iterateInputs(_inResScene)) { for (const CT_AbstractItemDrawableWithPointCloud* sc : grp->singularItems(_inScene)) { CT_StdLASPointsAttributesContainer* lasAtt = const_cast(grp->singularItem(_inLasAtt)); CT_PointsAttributesScalarTemplated* heightAtt = const_cast*>(grp->singularItem(_inHeightAtt)); CT_Image2D* maximaRaster = const_cast*>(grp->singularItem(_inMaxima)); CT_Image2D* mns = const_cast*>(grp->singularItem(_inMns)); CT_Circle2D* area = const_cast(grp->singularItem(_inArea)); _dataContainer->_scenes.append(const_cast(sc)); _dataContainer->_LASattributes.append(lasAtt); _dataContainer->_heightAttributes.append(heightAtt); _dataContainer->_dsm = mns; if (area != nullptr) { _dataContainer->_idPlot = area->displayableName(); _dataContainer->_xPlot = area->centerX(); _dataContainer->_yPlot = area->centerY(); _dataContainer->_radiusPlot = area->getRadius(); } _dataContainer->_quality = 0; const CT_AbstractPointCloudIndex *pointCloudIndex = sc->pointCloudIndex(); CT_PointIterator itP(pointCloudIndex); while(itP.hasNext() && (!isStopped())) { const CT_Point &point = itP.next().currentPoint(); float hPoint = mns->valueAtCoords(point(0), point(1)); if (_dataContainer->_dtm != nullptr) { float dtmVal = _dataContainer->_dtm->valueAtCoords(point(0), point(1)); if (!qFuzzyCompare(dtmVal, _dataContainer->_dtm->NA())) { hPoint -= dtmVal; } } if (hPoint > maxPointHeight) {maxPointHeight = hPoint;} int maxRasterIndex = maximaRaster->valueAtCoords(point(0), point(1)); if (maxRasterIndex > 0) { Eigen::Vector3d previous = _dataContainer->_maximaPoints.value(maxRasterIndex, Eigen::Vector3d(0,0,-std::numeric_limits::max())); if (point(2) > previous(2)) { _dataContainer->_maximaPoints.insert(maxRasterIndex, point); } } } } } if (maxPointHeight <= 0.0 || maxPointHeight > 60.0) {maxPointHeight = 50;} double maxTreeHeight = 0; double maxTreeDBH = 0; for (CT_StandardItemGroup* grp : _inGrp.iterateOutputs(_inResPlot)) { for (const CT_Circle2D* circle : grp->singularItems(_inRef)) { ONF_CoRegistration_TreePosition* treePos = new ONF_CoRegistration_TreePosition(); treePos->_x = circle->centerX(); treePos->_y = circle->centerY(); treePos->_originalX = circle->centerX(); treePos->_originalY = circle->centerY(); const CT_AbstractItemAttribute* att = circle->itemAttribute(_inRefDbh); if (att != nullptr) {treePos->_dbh = att->toFloat(circle, nullptr);} if (treePos->_dbh <= 0) {treePos->_dbh = 7.5;} if (treePos->_dbh > maxTreeDBH) {maxTreeDBH = treePos->_dbh;} att = circle->itemAttribute(_inRefHeight); if (att != nullptr) {treePos->_height = att->toFloat(circle, nullptr);} treePos->_trueheight = treePos->_height; if (treePos->_height <= 0) {treePos->_height = 0;} if (treePos->_height > maxTreeHeight) {maxTreeHeight = treePos->_height;} att = circle->itemAttribute(_inRefID); if (att != nullptr) {treePos->_idTree = att->toString(circle, nullptr);} att = circle->itemAttribute(_inRefIDplot); if (att != nullptr) {treePos->_idPlot = att->toString(circle, nullptr);} att = circle->itemAttribute(_inSpecies); if (att != nullptr) {treePos->_species = att->toString(circle, nullptr);} att = circle->itemAttribute(_inComment); if (att != nullptr) {treePos->_comment = att->toString(circle, nullptr);} treePos->_color = _dataContainer->_spColors.value(treePos->_species.toUpper(), QColor(255,255,255)); _dataContainer->_positions.append(treePos); treePos->_grp = grp; } } if (qFuzzyCompare(maxTreeHeight, 0)) {maxTreeHeight = maxPointHeight;} // Quick and dirty height interpolation from dbh for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_CoRegistration_TreePosition* treePos = _dataContainer->_positions.at(i); if (treePos->_height <= 0) { treePos->_height = maxTreeHeight * treePos->_dbh / maxTreeDBH; } } requestManualMode(); _m_status = 1; double transX = _dataContainer->_transX; double transY = _dataContainer->_transY; if (_exportFile.size() > 0) { QFile file(_exportFile.first()); if (!file.exists()) { if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream stream(&file); stream << tr("Time\tPlot\tXbefore\tYbefore\tTransX\tTransY\tXafter\tYafter\tQuality\tComment\n"); file.close(); } } if (file.open(QIODevice::Append | QIODevice::Text)) { double xAfter = _dataContainer->_xPlot + transX; double yAfter = _dataContainer->_yPlot + transY; QTextStream stream(&file); stream << QDateTime::currentDateTime().toString() << "\t"; stream << _dataContainer->_idPlot << "\t"; stream << QString::number(_dataContainer->_xPlot, 'f',4) << "\t"; stream << QString::number(_dataContainer->_yPlot, 'f',4) << "\t"; stream << QString::number(transX, 'f',4) << "\t"; stream << QString::number(transY, 'f',4) << "\t"; stream << QString::number(xAfter, 'f',4) << "\t"; stream << QString::number(yAfter, 'f',4) << "\t"; stream << _dataContainer->_quality << "\t"; stream << _dataContainer->_comment << "\n"; file.close(); } } for (int i = 0 ; i < _dataContainer->_positions.size() ; i++) { ONF_CoRegistration_TreePosition* treePos = _dataContainer->_positions.at(i); if (treePos->_grp != nullptr) { CT_Circle2D* circle = new CT_Circle2D(new CT_Circle2DData(Eigen::Vector2d(treePos->_x + transX, treePos->_y + transY), double(treePos->_dbh) / 200.0)); treePos->_grp->addSingularItem(_outCircle, circle); circle->addItemAttribute(_outDBHAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_NUMBER, treePos->_dbh)); circle->addItemAttribute(_outHeightAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_NUMBER, treePos->_trueheight)); circle->addItemAttribute(_outPlotIDAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_ID, treePos->_idPlot)); circle->addItemAttribute(_outTreeIDAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_ID, treePos->_idTree)); circle->addItemAttribute(_outSpeciesAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_VALUE, treePos->_species)); circle->addItemAttribute(_outTransXAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_NUMBER, float(transX))); circle->addItemAttribute(_outTransYAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_NUMBER, float(transY))); circle->addItemAttribute(_outMovedAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_VALUE, treePos->_moved)); circle->addItemAttribute(_outZPointAtt, new CT_StdItemAttributeT(CT_AbstractCategory::DATA_NUMBER, float(treePos->_zPoint))); } delete treePos; } _dataContainer->_dsm->setAlternativeDrawManager(nullptr); delete _dataContainer; requestManualMode(); } void ONF_StepAdjustPlotPosition02::initManualMode() { if(_m_doc == nullptr) { // create a new 3D document _m_doc = guiContext()->documentManager()->new3DDocument(); ONF_ActionAdjustPlotPosition02* action = new ONF_ActionAdjustPlotPosition02(_dataContainer); // set the action (a copy of the action is added at all graphics view, and the action passed in parameter is deleted) _m_doc->setCurrentAction(action, false); } // QMessageBox::information(nullptr, // tr("Mode manuel"), // tr("Bienvenue dans le mode manuel de cette étape.\n"), // QMessageBox::Ok); } void ONF_StepAdjustPlotPosition02::useManualMode(bool quit) { if(_m_status == 0) { if(quit) { } } else if(_m_status == 1) { if(!quit) { _m_doc = nullptr; quitManualMode(); } } }