/**************************************************************************** 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_stepmergeneighboursections04.h" #include "tools/onf_citations.h" #include "ct_math/ct_mathpoint.h" ONF_StepMergeNeighbourSections04::ONF_StepMergeNeighbourSections04() : SuperClass() { _thickness = 0.10; _searchDistance = 10; _maxDistance = 0.50; _deltaZ = 0.20; _maxInd1 = 1.5; } QString ONF_StepMergeNeighbourSections04::description() const { return tr("Fusionner les billons parallèles"); } QString ONF_StepMergeNeighbourSections04::detailledDescription() const { return tr("Cette étape prend en entrée une liste de billons. Chaque billon est composée d'une séquence de clusters. " "
Un cluster est caractérisé par :" "" "
Ces billons sont issues d'une étape précédente telle que ONF_StepDetectSection. Cependant, en début d'étape " "elles sont remaniées de façon à ce que les clusters aient l' épaisseur choisie en paramètre de l'étape.
" "Au sein de chaque billon ce remaniement consiste à prendre tous les points de tous les clusters, afin de recréer des clusters de l' épaisseur choisie.
" "Ensuite, pour chaque cluster créé, on en détermine le barycentre et le buffer.
" "Le but de cette étape est de fusionner des billons appartenant dans la réalité au même arbre.
" "Elle traite spécifiquement le cas des billons se chevauchant verticalement. Elle est complétée par ONF_StepMergeEndToEndSections.
" "En plus de l' épaisseur de cluster, cette étape utilise les paramètres suivants :" "" "
Le fonctionnement de l'étape est le suivant. Les billons sont comparées deux à deux par ordre décroissant de longueur selon Z." "A chaque itération, on compare une billon A (la plus longue) constituée de n clusters ayant des barycentres Ai (i = 1 à n), " "avec une billon B constituée de m clusters ayant des barycentres Bj (j = 1 à m).
" "Pour ce faire on commence par calculer medBuffer : la médiane des distances buffers des barycentres Ai.
" "Pour que A et B soient fusionnées, il faut que pour tout i et j tels que la distance verticale |Ai - Bj|z < deltaZ" "" "En cas de fusion, les clusters et les barycentres sont recréés à partir de tous les points des deux billons sources pour former une nouvelle billon C.
" "La billon C devient la de facto la plus longue : elle est donc aussitôt utilisée dans l'itération suivant dans la comparaison avec la prochaine billon (plus petite) de la liste."); } QString ONF_StepMergeNeighbourSections04::inputDescription() const { return SuperClass::inputDescription() + tr("

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

"); } QString ONF_StepMergeNeighbourSections04::detailsDescription() const { return tr(""); } QStringList ONF_StepMergeNeighbourSections04::getStepRISCitations() const { return QStringList() << ONF_citations::citation()._citationOthmaniEtAl2001; } CT_VirtualAbstractStep* ONF_StepMergeNeighbourSections04::createNewInstance() const { // cree une copie de cette etape return new ONF_StepMergeNeighbourSections04(); } //////////////////// PROTECTED ////////////////// void ONF_StepMergeNeighbourSections04::declareInputModels(CT_StepInModelStructureManager& manager) { manager.addResult(_inResult, tr("Billons")); manager.setZeroOrMoreRootGroup(_inResult, _inZeroOrMoreRootGroup); manager.addGroup(_inZeroOrMoreRootGroup, _inGroupSection, tr("Billons")); manager.addGroup(_inGroupSection, _inGroupCluster, tr("Clusters")); manager.addItem(_inGroupCluster, _inCluster, tr("Points")); } void ONF_StepMergeNeighbourSections04::fillPostInputConfigurationDialog(CT_StepConfigurableDialog* postInputConfigDialog) { postInputConfigDialog->addDouble(tr("Epaisseur (en Z) des clusters :"), "cm", 0, 1000, 2, _thickness, 100); postInputConfigDialog->addDouble(tr("Distance de recherche de voisinage :"), "m", 0, 100, 2, _searchDistance); postInputConfigDialog->addDouble(tr("Distance XY maximale entre barycentres de clusters de billons à fusionner :"), "cm", 0, 1000, 2, _maxDistance, 100); postInputConfigDialog->addDouble(tr("Distance Z maximale entre barycentres de clusters de billons à fusionner :"), "cm", 0, 1000, 2, _deltaZ, 100); postInputConfigDialog->addDouble(tr("Facteur d'accroissement maximal des distances XY entre barycentres de clusters de billons à fusionner :"), tr("fois"), 0, 1000, 2, _maxInd1); } void ONF_StepMergeNeighbourSections04::declareOutputModels(CT_StepOutModelStructureManager& manager) { manager.addResult(_outResult, tr("Billons fusionnées")); manager.setRootGroup(_outResult, _outGroupSection, tr("Billons")); manager.addGroup(_outGroupSection, _outGroupCluster, tr("Clusters")); manager.addItem(_outGroupCluster, _outCluster, tr("Points")); manager.addItem(_outGroupCluster, _outRefPoint, tr("Barycentre")); } void ONF_StepMergeNeighbourSections04::compute() { //////////////////////////////////////////////////// // CREATION DES SECTION INITIALES (CHANGETHICKNESS + AJOUT DES POINTS DE REFERENCE BARYCENTRES) //////////////////////////////////////////////////// QList clusterSections; QList finalClusterSections; // Parcours des sections in for (const CT_StandardItemGroup* section : _inGroupSection.iterateInputs(_inResult)) { if (isStopped()) {return;} ClusterSection* clusterSection = new ClusterSection(); for (const CT_StandardItemGroup* group : section->groups(_inGroupCluster)) { const CT_PointCluster *cluster = group->singularItem(_inCluster); if (cluster!=nullptr) {clusterSection->addCluster(cluster);} } clusterSection->initRefPoints(_thickness); clusterSections.append(clusterSection); } std::sort(clusterSections.begin(), clusterSections.end(), ClusterSection::sortByLength); int size = clusterSections.size(); //////////////////////////////////////////////////// // ALGORITHME DE FUSION CONDITIONNELLE DES SECTIONS //////////////////////////////////////////////////// double distance2 = _searchDistance*_searchDistance; while (!clusterSections.isEmpty() && (!isStopped())) { // récupération de la section la plus longue : BASE ClusterSection* baseSection = clusterSections.takeLast(); bool hasToLoopAgain = true; while (hasToLoopAgain) { hasToLoopAgain = false; double x_fbase = baseSection->_refPoints.first()(0); double y_fbase = baseSection->_refPoints.first()(1); double baseMinZ = baseSection->_refPoints.first()(2); double baseMaxZ = baseSection->_refPoints.last()(2); // Parcours des autres segments : TESTED for (int sec = 0 ; sec < clusterSections.size() && !hasToLoopAgain ; sec++) { ClusterSection* testedSection = clusterSections.at(sec); // Est-ce que les segments BASE et TESTED se chevauchent en Z ? double testedMinZ = testedSection->_refPoints.first()(2); double testedMaxZ = testedSection->_refPoints.last()(2); // Est-ce que les section se chevauchent en Z if ((baseMinZ <= testedMaxZ) && (testedMinZ <= baseMaxZ)) { double dx = x_fbase - testedSection->_refPoints.first()(0); double dy = y_fbase - testedSection->_refPoints.first()(1); double distance = dx*dx + dy*dy; // Est-ce que les segments sont a proximite first a moins de _searchdistance en (x,y) ? if (distance < distance2) { // Si la fusion de TESTED et BASE n'a pas ete rejetee if (isFusionNeeded(baseSection, testedSection)) { // Creation du segment fusionne : MERGED ClusterSection* mergedSection = ClusterSection::mergeSections(baseSection, testedSection, _thickness); // Le segment MERGED est retenu, donc remplace BASE pour les tests suivants // BASE et TESTED sont donc supprimés delete baseSection; baseSection = mergedSection; clusterSections.removeOne(testedSection); delete testedSection; hasToLoopAgain = true; } } } } // FIN du while des TESTED } finalClusterSections.append(baseSection); waitForAckIfInDebugMode(); setProgress(100*int(1.0 - double(clusterSections.size())/double(size))); } // Create and add output data for (CT_ResultGroup* outRes : _outResult.iterateOutputs()) { for (int i = 0 ; i < finalClusterSections.size() ; i++) { ClusterSection* clusterSection = finalClusterSections.at(i); clusterSection->computeOutData(_thickness); CT_StandardItemGroup* section = new CT_StandardItemGroup(); outRes->addRootGroup(_outGroupSection, section); for (int j = 0 ; j < clusterSection->_refZvalues.size() ; j++) { CT_StandardItemGroup* clusterGroup = new CT_StandardItemGroup(); section->addGroup(_outGroupCluster, clusterGroup); clusterGroup->addSingularItem(_outCluster, clusterSection->_outClusters[j]); clusterGroup->addSingularItem(_outRefPoint, clusterSection->_outRefPoints[j]); } } } qDeleteAll(finalClusterSections); setProgress(100); } bool ONF_StepMergeNeighbourSections04::isFusionNeeded (ClusterSection *seg_base, ClusterSection *seg_tested) { int sizeTested = seg_tested->_refPoints.size(); int sizeBase = seg_base->_refPoints.size(); bool fusionNeeded = false; double zm_inTested = seg_tested->_refPoints.first()(2) - _deltaZ; double zmax_tested = seg_tested->_refPoints.last()(2) + _deltaZ; bool ok = true; QList buffers; // Parcours des points de BASE for (int i = 0 ; (i < sizeBase) && (ok) ; i++) { const Eigen::Vector4d& baseRefPoint = seg_base->_refPoints.at(i); // Parcours des points de BASE buffers.append(baseRefPoint(3)); } double distMax = 0; if (buffers.size() > 0) { std::sort(buffers.begin(), buffers.end()); // Modification AP, 06/03/2015 //distMax = buffers.at(buffers.size() / 2); distMax = _maxInd1*buffers.at(buffers.size() / 2); } // Parcours des points de BASE for (int i = 0 ; (i < sizeBase) && (ok) ; i++) { const Eigen::Vector4d& baseRefPoint = seg_base->_refPoints.at(i); // Parcours des points de BASE if (baseRefPoint(2) > zmax_tested) { ok = false; } else { if (baseRefPoint(2) > zm_inTested) { for (int j = 0 ; j < sizeTested ; j++) { const Eigen::Vector4d& testedRefPoint = seg_tested->_refPoints.at(j); double deltaZ = fabs(baseRefPoint(2) - testedRefPoint(2)); // On ne teste que des refpoint de meme Z à deltaZ près if (deltaZ <= _deltaZ) { // Proximite si la distance separant les barycentre < _distance double distance = sqrt(pow(baseRefPoint(0) - testedRefPoint(0), 2) + pow(baseRefPoint(1) - testedRefPoint(1), 2)); //double dist_Ratio = distance / std::max(baseRefPoint->xyBuffer(), testedRefPoint->xyBuffer()); if (distance > _maxDistance) {return false;} if (distance > distMax) {return false;} //if (dist_Ratio <= _maxInd1) {fusionNeeded = true;} fusionNeeded = true; } } } } } // FIN du test des distances 2 a 2 des points ( meme Z) de BASE et TESTED return fusionNeeded; }