|
| 1 | +#include "contactanalysisdialog.h" |
| 2 | +#include "ui_contactanalysisdialog.h" |
| 3 | + |
| 4 | +#include "domain/attribute.h" |
| 5 | +#include "domain/datafile.h" |
| 6 | +#include "domain/application.h" |
| 7 | +#include "domain/categorydefinition.h" |
| 8 | +#include "geostats/contactanalysis.h" |
| 9 | +#include "widgets/categoryselector.h" |
| 10 | +#include "widgets/linechartwidget.h" |
| 11 | +#include "dialogs/emptydialog.h" |
| 12 | + |
| 13 | +#include <QMessageBox> |
| 14 | +#include <algorithm> |
| 15 | + |
| 16 | +ContactAnalysisDialog::ContactAnalysisDialog(Attribute *attributeGrade, |
| 17 | + Attribute *attributeDomains, |
| 18 | + QWidget *parent) : |
| 19 | + QDialog(parent), |
| 20 | + ui(new Ui::ContactAnalysisDialog), |
| 21 | + m_attributeGrade(attributeGrade), |
| 22 | + m_attributeDomains(attributeDomains) |
| 23 | +{ |
| 24 | + ui->setupUi(this); |
| 25 | + |
| 26 | + //deletes dialog from memory upon user closing it |
| 27 | + this->setAttribute(Qt::WA_DeleteOnClose); |
| 28 | + |
| 29 | + //set dialog title |
| 30 | + setWindowTitle( "Contact Analysis" ); |
| 31 | + |
| 32 | + //update the suffix of the lag size spin box with the length unit symbol. |
| 33 | + onLengthUnitSymbolChanged( ui->txtLengthUnitSymbol->text() ); |
| 34 | + |
| 35 | + //get the pointer to the variables' parent data file and set caption of the dialog's label |
| 36 | + m_dataFile = dynamic_cast<DataFile*>( attributeGrade->getContainingFile() ); |
| 37 | + DataFile* dataFileOfDomain = dynamic_cast<DataFile*>( attributeDomains->getContainingFile() ); |
| 38 | + if( ! m_dataFile || ! dataFileOfDomain || (dataFileOfDomain != m_dataFile ) ) { //sanity check |
| 39 | + Application::instance()->logError( "ContactAnalysisDialog::ContactAnalysisDialog(): " |
| 40 | + "Container file object of the grade and/or domains attributes is not a data file or " |
| 41 | + "their parent files are different, which is not allowed for contact anaysis. ", true ); |
| 42 | + } else { |
| 43 | + //enable/disable certain dialog widgets depending on whether the data set is 3D or is gridded. |
| 44 | + ui->lblDataFile->setText( ui->lblDataFile->text() + "<b>" + m_dataFile->getName() + "</b>" ); |
| 45 | + if( m_dataFile->isGridded() || !m_dataFile->isTridimensional() ){ |
| 46 | + ui->lblZTolerance->setEnabled( false ); |
| 47 | + ui->dblSpinZTolerance->setEnabled( false ); |
| 48 | + } |
| 49 | + if( ! m_dataFile->isTridimensional() ) { |
| 50 | + ui->radioVertical->setEnabled( false ); |
| 51 | + ui->radioOmni3D->setEnabled( false ); |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + //display the variables' names |
| 56 | + ui->lblGradeAttribute->setText( ui->lblGradeAttribute->text() + "<b>" + attributeGrade->getName() + "</b>" ); |
| 57 | + ui->lblDomainsAttribute->setText( ui->lblDomainsAttribute->text() + "<b>" + attributeDomains->getName() + "</b>" ); |
| 58 | + |
| 59 | + //get the domains variable's category definition |
| 60 | + CategoryDefinition* cd = m_dataFile->getCategoryDefinition( attributeDomains ); |
| 61 | + if( ! cd ){ //sanity check |
| 62 | + Application::instance()->logError( "ContactAnalysisDialog::ContactAnalysisDialog(): " |
| 63 | + "Category definition of domains variable is null. ", true ); |
| 64 | + } |
| 65 | + |
| 66 | + //create the category selector widgets so the user can select both domains for the contact analysis |
| 67 | + m_selectorDomain1 = new CategorySelector( cd ); |
| 68 | + m_selectorDomain2 = new CategorySelector( cd ); |
| 69 | + ui->frmDomain1->layout()->addWidget( m_selectorDomain1 ); //Qt takes ownership of the widget object with this |
| 70 | + ui->frmDomain2->layout()->addWidget( m_selectorDomain2 ); //Qt takes ownership of the widget oject with this |
| 71 | +} |
| 72 | + |
| 73 | +ContactAnalysisDialog::~ContactAnalysisDialog() |
| 74 | +{ |
| 75 | + delete ui; |
| 76 | +} |
| 77 | + |
| 78 | +void ContactAnalysisDialog::onLengthUnitSymbolChanged(QString lengthUnitSymbol) |
| 79 | +{ |
| 80 | + ui->dblSpinLagSize->setSuffix( lengthUnitSymbol ); |
| 81 | +} |
| 82 | + |
| 83 | +void ContactAnalysisDialog::onProceed() |
| 84 | +{ |
| 85 | + ContactAnalysis contactAnalysis; |
| 86 | + if( ui->radioLateral->isChecked() ) |
| 87 | + contactAnalysis.setMode( ContactAnalysisMode::LATERAL ); |
| 88 | + else |
| 89 | + contactAnalysis.setMode( ContactAnalysisMode::VERTICAL ); |
| 90 | + contactAnalysis.setLagSize( ui->dblSpinLagSize->value() ); |
| 91 | + contactAnalysis.setZtolerance( ui->dblSpinZTolerance->value() ); |
| 92 | + contactAnalysis.setDomain1_code( static_cast<uint16_t>(m_selectorDomain1->getSelectedCategoryCode()) ); |
| 93 | + contactAnalysis.setDomain2_code( static_cast<uint16_t>(m_selectorDomain2->getSelectedCategoryCode()) ); |
| 94 | + contactAnalysis.setNumberOfLags( static_cast<uint16_t>(ui->spinNumberOfLags->value()) ); |
| 95 | + contactAnalysis.setInputDataFile( m_dataFile ); |
| 96 | + contactAnalysis.setAttributeGrade( m_attributeGrade ); |
| 97 | + contactAnalysis.setAttributeDomains( m_attributeDomains ); |
| 98 | + contactAnalysis.setMaxNumberOfSamples( static_cast<uint16_t>(ui->spinMaxNumberOfSamples->value()) ); |
| 99 | + contactAnalysis.setMinNumberOfSamples( static_cast<uint16_t>(ui->spinMinNumberOfSamples->value()) ); |
| 100 | + |
| 101 | + if( ! contactAnalysis.run() ) { |
| 102 | + |
| 103 | + Application::instance()->logError("ContactAnalysisDialog::onProceed(): failed execution:"); |
| 104 | + Application::instance()->logError(" " + contactAnalysis.getLastError()); |
| 105 | + QMessageBox::critical( this, "Error", "Contact analysis failed. Further details in the message panel." ); |
| 106 | + |
| 107 | + } else { //if the contact analysis completed successfully |
| 108 | + |
| 109 | + //shortcut for the STL's not-a-number value. |
| 110 | + const double NaN = std::numeric_limits<double>::quiet_NaN(); |
| 111 | + |
| 112 | + //get the contact analysis results |
| 113 | + std::vector< |
| 114 | + std::pair< |
| 115 | + ContactAnalysis::Lag, |
| 116 | + ContactAnalysis::MeanGradesBothDomains |
| 117 | + > |
| 118 | + > results = contactAnalysis.getResults(); |
| 119 | + |
| 120 | + //prepare data points for chart plotting |
| 121 | + //outer vector: the series of multivariate data |
| 122 | + //inner vectors: each multivariate datum (each element is a X, Y1, Y2,... value). |
| 123 | + std::vector< std::vector<double> > chartData; |
| 124 | + |
| 125 | + //traverse the results to fill the chart data |
| 126 | + //the desired effect is to have two different curves sharing a mirrored X-axis befitting a |
| 127 | + //contact analysis chart (NaN causes the line chart widget to not render the curve at the given point). |
| 128 | + for( const std::pair< ContactAnalysis::Lag, ContactAnalysis::MeanGradesBothDomains >& result : results ){ |
| 129 | + // lag values grades of domain 1 grades of domain 2 |
| 130 | + //indexes in the inner vectors: 0 1 2 |
| 131 | + chartData.push_back( { -result.first, result.second.first, NaN } ); |
| 132 | + } |
| 133 | + std::reverse( chartData.begin(), chartData.end() ); |
| 134 | + for( const std::pair< ContactAnalysis::Lag, ContactAnalysis::MeanGradesBothDomains >& result : results ){ |
| 135 | + // lag values grades of domain 1 grades of domain 2 |
| 136 | + //indexes in the inner vectors: 0 1 2 |
| 137 | + chartData.push_back( { result.first, NaN, result.second.second } ); |
| 138 | + } |
| 139 | + |
| 140 | + //get some properties of the domain categories relevant to make |
| 141 | + //the chart informative |
| 142 | + QColor colorDomain1, colorDomain2; |
| 143 | + QString nameDomain1, nameDomain2; |
| 144 | + { |
| 145 | + CategoryDefinition* cd = m_dataFile->getCategoryDefinition( m_attributeDomains ); |
| 146 | + colorDomain1 = cd->getCustomColor( cd->getCategoryIndex( contactAnalysis.getDomain1_code() )); |
| 147 | + colorDomain2 = cd->getCustomColor( cd->getCategoryIndex( contactAnalysis.getDomain2_code() )); |
| 148 | + nameDomain1 = cd->getCategoryNameByCode( contactAnalysis.getDomain1_code() ); |
| 149 | + nameDomain2 = cd->getCategoryNameByCode( contactAnalysis.getDomain2_code() ); |
| 150 | + } |
| 151 | + |
| 152 | + //display the results |
| 153 | + LineChartWidget* lcw = new LineChartWidget(this); |
| 154 | + lcw->setSharedYaxis( true ); |
| 155 | + lcw->setChartTitle( "Contact analysis of " + m_dataFile->getName() ); |
| 156 | + lcw->setData( chartData, 0, |
| 157 | + {{1, nameDomain1 + " (Domain 1)"}, {2, nameDomain2 + " (Domain 2)"}}, |
| 158 | + {{2, "mean " + m_attributeGrade->getName() }}, // shared y-axis is enabled, set only the last one |
| 159 | + {{1, colorDomain1} , {2, colorDomain2}} ); |
| 160 | + lcw->setXaxisCaption( "lag (" + ui->txtLengthUnitSymbol->text() + ")" ); |
| 161 | + EmptyDialog* ed = new EmptyDialog( this ); |
| 162 | + ed->addWidget( lcw ); |
| 163 | + ed->setWindowTitle( "Contact analysis results" ); |
| 164 | + ed->show(); |
| 165 | + |
| 166 | + } |
| 167 | +} |
0 commit comments