/**************************************************************************** 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_stepdetectsection07.h" #include "tools/onf_citations.h" #include "ct_math/ct_mathboundingshape.h" #include #include #include ONF_StepDetectSection07::ONF_StepDetectSection07() : SuperClass() { _deltaz = 0.1; } QString ONF_StepDetectSection07::description() const { return tr("Agréger verticalement les clusters en billons"); } QString ONF_StepDetectSection07::detailledDescription() const { return tr("Cette étape prend en entrée des couches horizontales (layers) contenant des clusters.
" "Ce type de structure peut par exemple être produite par l'étape ONF_StepHorizontalClustering.
" "Les clusters adjacents verticalement sont regroupés en billons (groupes). Pour ce faire :" "
    " "
  • Les clusters dont la distance verticale les séparant est inférieure au seuil choisi sont comparés deux à deux.
  • " "
  • Si leurs boites englobantes s'intersectent dans le plan XY, les clusters sont regroupés dans la même billon.
  • " "
" "N.B. : Les clusters ayant la plus grande boite englobante XY sont prioritaires."); } QString ONF_StepDetectSection07::inputDescription() const { return SuperClass::inputDescription() + tr("

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

"); } QString ONF_StepDetectSection07::detailsDescription() const { return tr(""); } QStringList ONF_StepDetectSection07::getStepRISCitations() const { return QStringList() << ONF_citations::citation()._citationOthmaniEtAl2001; } CT_VirtualAbstractStep* ONF_StepDetectSection07::createNewInstance() const { // cree une copie de cette etape return new ONF_StepDetectSection07(); } //////////////////// PROTECTED ////////////////// void ONF_StepDetectSection07::declareInputModels(CT_StepInModelStructureManager& manager) { manager.addResult(_inResult, tr("Clusters")); manager.setZeroOrMoreRootGroup(_inResult, _inZeroOrMoreRootGroup); manager.addGroup(_inZeroOrMoreRootGroup, _inLayer, tr("Tranches")); manager.addGroup(_inLayer, _inGroup, tr("Clusters)")); manager.addItem(_inGroup, _inCluster, tr("Points")); } void ONF_StepDetectSection07::fillPostInputConfigurationDialog(CT_StepConfigurableDialog* postInputConfigDialog) { // création effective de la configurationDialog postInputConfigDialog->addDouble(tr("Distance en z (en + et en -) maximum entre deux clusters de points à comparer"), "cm", 0, 1000, 2, _deltaz, 100); } void ONF_StepDetectSection07::declareOutputModels(CT_StepOutModelStructureManager& manager) { manager.addResult(_outResult); manager.setRootGroup(_outResult, _outGroupSection, tr("Billons")); manager.addGroup(_outGroupSection, _outGroup, tr("Clusters")); manager.addItem(_outGroup, _outCluster, tr("Clusters")); } void ONF_StepDetectSection07::compute() { // Layers triés par Z croissant, pour chacun : clusters triés par aire XY croissante QMap* > layersZMap; // Aires des clusters QMap clustersArea; // Boucle sur les layers for (const CT_StandardItemGroup* layer : _inLayer.iterateInputs(_inResult)) { bool firstCluster = true; double zLevel = 0; QMultiMap *clusterMap = nullptr; // Boucle sur les clusters du layer for (const CT_StandardItemGroup* groupWithCluster : layer->groups(_inGroup)) { if (isStopped()) {return;} for (const CT_PointCluster* cluster : groupWithCluster->singularItems(_inCluster)) { if (firstCluster) { firstCluster = false; zLevel = cluster->centerZ(); clusterMap = new QMultiMap(); layersZMap.insert(zLevel, clusterMap); } Eigen::Vector3d min, max; cluster->boundingBox(min, max); double area = std::abs((max.x() - min.x()) * (max.y() - min.y())); clusterMap->insert(area, cluster); clustersArea.insert(cluster, area); } } } setProgress( 25 ); // Sections de sortie, triées par aire XY croissante QMultiMap* > sectionMap; // Section "terminées" QList* > finishedSections; // Boucle sur les layers QMapIterator* > itZMap(layersZMap); while (itZMap.hasNext() && !isStopped()) { itZMap.next(); // Récupération des clusters du layer en cours QMultiMap* clusterMap = itZMap.value(); double zLevel = itZMap.key(); // Parcours des sections existantes QList*> sections = sectionMap.values(); sectionMap.clear(); while (!sections.isEmpty()&& !isStopped()) { QList* section = sections.takeLast(); // La section testée doit être en dessous du layer ET, ne pas être à moins de la hauteur du layer - _deltaZ if ((section->last()->centerZ() <= zLevel) && (section->last()->centerZ() >= (zLevel - _deltaz))) { const CT_PointCluster* sectionCluster = section->last(); Eigen::Vector3d minSectionCluster, maxSectionCluster; sectionCluster->boundingBox(minSectionCluster, maxSectionCluster); // Boucle sur les clusters du layer QMutableMapIterator itClusters(*clusterMap); itClusters.toBack(); bool found = false; while (!found && itClusters.hasPrevious() && !isStopped()) { itClusters.previous(); const CT_PointCluster* layerCluster = itClusters.value(); Eigen::Vector3d minLayerCluster, maxLayerCluster; layerCluster->boundingBox(minLayerCluster, maxLayerCluster); if (CT_MathBoundingShape::aabbIntersects2D(minSectionCluster, maxSectionCluster, minLayerCluster, maxLayerCluster)) { found = true; section->append(layerCluster); itClusters.remove(); } } // Re-création de la map triée des sections sectionMap.insert(clustersArea.value(section->last()), section); } else { finishedSections.append(section); } } // Création d'une nouvelle section pour chaque cluster non rattaché à une section existante QMutableMapIterator itClusters(*clusterMap); while (itClusters.hasNext()) { itClusters.next(); QList* newSection = new QList(); newSection->append(itClusters.value()); sectionMap.insert(itClusters.key(), newSection); itClusters.remove(); } } setProgress( 50 ); finishedSections.append(sectionMap.values()); sectionMap.clear(); for (CT_ResultGroup* outRes : _outResult.iterateOutputs()) { // Enregistrement des sections dans le résultat QListIterator* > itSections(finishedSections); while (itSections.hasNext()) { QList *list = itSections.next(); int sizelist = list->size(); if (sizelist > 0) { CT_StandardItemGroup* section = new CT_StandardItemGroup(); outRes->addRootGroup(_outGroupSection, section); for (int j = 0 ; j < sizelist ; j++) { const CT_PointCluster* cl = list->at(j); CT_StandardItemGroup *outClusterGroup = new CT_StandardItemGroup(); section->addGroup(_outGroup, outClusterGroup); CT_PointCluster* newCluster = new CT_PointCluster(*cl); outClusterGroup->addSingularItem(_outCluster, newCluster); } } } } qDeleteAll(finishedSections); qDeleteAll(layersZMap.values()); setProgress( 100 ); }