diff --git a/NeighborhoodFeature.cpp b/NeighborhoodFeature.cpp index 1f9c944..c1f086e 100644 --- a/NeighborhoodFeature.cpp +++ b/NeighborhoodFeature.cpp @@ -49,6 +49,12 @@ bool NeighborhoodFeature::checkValidity(QString corePointRole, QString &error) c return false; } + if (std::isnan(scale)) + { + error = "No scale defined"; + return false; + } + return true; } @@ -315,7 +321,7 @@ bool NeighborhoodFeature::computeValue(CCLib::DgmOctree::NeighboursSet& pointsIn { outputValue = maxZ - queryPoint.z; } - else if (type == Zmax) + else if (type == Zmin) { outputValue = queryPoint.z - minZ; } diff --git a/PointFeature.cpp b/PointFeature.cpp index f4dfa5d..0e04e99 100644 --- a/PointFeature.cpp +++ b/PointFeature.cpp @@ -86,7 +86,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const { if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_INTENSITY]) < 0) { - error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_INTENSITY]); + error = QString("Cloud %0 has no '%1' scalar field").arg(cloud1->getName()).arg(LAS_FIELD_NAMES[LAS_INTENSITY]); return false; } return true; @@ -99,7 +99,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const { if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]) < 0) { - error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]); + error = QString("Cloud %0 has no '%1' scalar field").arg(cloud1->getName()).arg(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]); return false; } return true; @@ -108,7 +108,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const { if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]) < 0) { - error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]); + error = QString("Cloud %0 has no '%1' scalar field").arg(cloud1->getName()).arg(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]); return false; } return true; @@ -117,12 +117,12 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const { if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]) < 0) { - error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]); + error = QString("Cloud %0 has no '%1' scalar field").arg(cloud1->getName()).arg(LAS_FIELD_NAMES[LAS_NUMBER_OF_RETURNS]); return false; } if (cloud1->getScalarFieldIndexByName(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]) < 0) { - error = QString("Cloud has no '%1' scalar field").arg(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]); + error = QString("Cloud %0 has no '%1' scalar field").arg(cloud1->getName()).arg(LAS_FIELD_NAMES[LAS_RETURN_NUMBER]); return false; } return true; @@ -132,7 +132,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const case PointFeature::B: if (!cloud1->hasColors()) { - error = "Cloud has no RGB color"; + error = QString("Cloud %0 has no RGB color").arg(cloud1->getName()); return false; } return true; @@ -140,7 +140,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const { if (cloud1->getScalarFieldIndexByName(s_NIRSFName) < 0) { - error = QString("Cloud has no '%1' scalar field").arg(s_NIRSFName); + error = QString("Cloud %0 has no '%1' scalar field").arg(cloud1->getName()).arg(s_NIRSFName); return false; } return true; @@ -150,7 +150,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const { if (!cloud1->hasNormals()) { - error = "Cloud has no normals"; + error = QString("Cloud %0 has no normals").arg(cloud1->getName()); return false; } return true; @@ -159,7 +159,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const { if (cloud1->getScalarFieldIndexByName(s_M3C2SFName) < 0) { - error = QString("Cloud has no '%1' scalar field").arg(s_M3C2SFName); + error = QString("Cloud %0 has no '%1' scalar field").arg(cloud1->getName()).arg(s_M3C2SFName); return false; } return true; @@ -168,7 +168,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const { if (cloud1->getScalarFieldIndexByName(s_PCVSFName) < 0) { - error = QString("Cloud has no '%1' scalar field").arg(s_PCVSFName); + error = QString("Cloud %0 has no '%1' scalar field").arg(cloud1->getName()).arg(s_PCVSFName); return false; } return true; @@ -176,7 +176,7 @@ bool PointFeature::checkValidity(QString corePointRole, QString &error) const case PointFeature::SF: if (sourceSFIndex >= static_cast(cloud1->getNumberOfScalarFields())) { - error = QString("Cloud has no scalar field #%1").arg(sourceSFIndex); + error = QString("Cloud %0 has no scalar field #%1").arg(cloud1->getName()).arg(sourceSFIndex); return false; } return true; diff --git a/q3DMASC.cpp b/q3DMASC.cpp index 87dff44..731e972 100644 --- a/q3DMASC.cpp +++ b/q3DMASC.cpp @@ -129,7 +129,7 @@ void q3DMASCPlugin::doClassifyAction() m_app->dispToConsole("Invalid classifier file (no cloud label defined)", ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; } - else if (cloudLabels.size() > 3) + else if (cloudLabels.size() > (cloudLabels.contains("TEST") ? 4 : 3)) { m_app->dispToConsole("This classifier uses more than 3 clouds (the GUI version cannot handle it)", ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; @@ -263,10 +263,10 @@ void q3DMASCPlugin::doTrainAction() //if no filename is specified in the training file, we are bound to ask the user to specify them bool useCloudsFromDB = (!filenamesSpecified || QMessageBox::question(m_app->getMainWindow(), "Use clouds in DB", "Use clouds in db (yes) or clouds specified in the file(no)?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes); - QString mainCloudLabel; + QString mainCloudLabel = corePointsLabel; if (useCloudsFromDB) { - if (cloudLabels.size() > 3) + if (cloudLabels.size() > (cloudLabels.contains("TEST") ? 4 : 3)) { m_app->dispToConsole("This classifier uses more than 3 different clouds (the GUI version cannot handle it)", ccMainAppInterface::WRN_CONSOLE_MESSAGE); return; @@ -293,7 +293,7 @@ void q3DMASCPlugin::doTrainAction() static masc::TrainParameters s_params; masc::Feature::Set features; - if (!masc::Tools::LoadTrainingFile(inputFilename, features, loadedClouds, s_params, &corePoints)) + if (!masc::Tools::LoadTrainingFile(inputFilename, features, loadedClouds, s_params, &corePoints, m_app->getMainWindow())) { m_app->dispToConsole("Failed to load the training file", ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; @@ -305,6 +305,11 @@ void q3DMASCPlugin::doTrainAction() return; } + if (mainCloudLabel.isEmpty()) + { + mainCloudLabel = corePoints.role; + } + if (!masc::Classifier::GetClassificationSF(corePoints.origin)) { m_app->dispToConsole("Missing 'Classification' field on core points cloud", ccMainAppInterface::ERR_CONSOLE_MESSAGE); diff --git a/q3DMASCTools.cpp b/q3DMASCTools.cpp index fc6019c..da6d110 100644 --- a/q3DMASCTools.cpp +++ b/q3DMASCTools.cpp @@ -603,7 +603,7 @@ static bool ReadCorePoints(const QString& command, const Tools::NamedClouds& clo return true; } -static bool ReadCloud(const QString& command, Tools::NamedClouds& clouds, QDir& defaultDir, int lineNumber) +static bool ReadCloud(const QString& command, Tools::NamedClouds& clouds, QDir& defaultDir, int lineNumber, FileIOFilter::LoadParameters& loadParameters) { QStringList tokens = command.split('='); if (tokens.size() != 2) @@ -616,10 +616,8 @@ static bool ReadCloud(const QString& command, Tools::NamedClouds& clouds, QDir& QString pcFilename = defaultDir.absoluteFilePath(tokens[1].trimmed()); //try to open the cloud { - FileIOFilter::LoadParameters parameters; - parameters.alwaysDisplayLoadDialog = false; CC_FILE_ERROR error = CC_FERR_NO_ERROR; - ccHObject* object = FileIOFilter::LoadFromFile(pcFilename, parameters, error); + ccHObject* object = FileIOFilter::LoadFromFile(pcFilename, loadParameters, error); if (error != CC_FERR_NO_ERROR || !object) { //error message already issued @@ -676,6 +674,20 @@ bool Tools::LoadFile( const QString& filename, return false; } + //to use the same 'global shift' for multiple files + CCVector3d loadCoordinatesShift(0, 0, 0); + bool loadCoordinatesTransEnabled = false; + FileIOFilter::LoadParameters loadParameters; + if (!cloudsAreProvided) + { + loadParameters.alwaysDisplayLoadDialog = true; + loadParameters.shiftHandlingMode = ccGlobalShiftManager::DIALOG_IF_NECESSARY; + loadParameters.coordinatesShift = &loadCoordinatesShift; + loadParameters.coordinatesShiftEnabled = &loadCoordinatesTransEnabled; + loadParameters.parentWidget = parent; + FileIOFilter::ResetSesionCounter(); + } + try { assert(!rawFeatures || rawFeatures->empty()); @@ -733,7 +745,7 @@ bool Tools::LoadFile( const QString& filename, continue; } QString command = line.mid(6); - if (!ReadCloud(command, *clouds, fi.absoluteDir(), lineNumber)) + if (!ReadCloud(command, *clouds, fi.absoluteDir(), lineNumber, loadParameters)) { return false; } @@ -746,7 +758,7 @@ bool Tools::LoadFile( const QString& filename, continue; } QString command = line.mid(5); - if (!ReadCloud("TEST=" + command, *clouds, fi.absoluteDir(), lineNumber)) //add the TEST keyword so that the cloud will be loaded as the TEST cloud + if (!ReadCloud("TEST=" + command, *clouds, fi.absoluteDir(), lineNumber, loadParameters)) //add the TEST keyword so that the cloud will be loaded as the TEST cloud { return false; } @@ -765,7 +777,6 @@ bool Tools::LoadFile( const QString& filename, else { QString command = line.mid(12); - if (clouds && !ReadCorePoints(command, *clouds, *corePoints, lineNumber)) { return false; @@ -874,10 +885,11 @@ bool Tools::LoadTrainingFile( QString filename, Feature::Set& rawFeatures, NamedClouds& loadedClouds, TrainParameters& parameters, - CorePoints* corePoints/*=nullptr*/) + CorePoints* corePoints/*=nullptr*/, + QWidget* parentWidget/*=nullptr*/) { bool cloudsWereProvided = !loadedClouds.empty(); - if (LoadFile(filename, &loadedClouds, cloudsWereProvided, &rawFeatures, corePoints, nullptr, ¶meters, nullptr)) + if (LoadFile(filename, &loadedClouds, cloudsWereProvided, &rawFeatures, corePoints, nullptr, ¶meters, parentWidget)) { return true; } diff --git a/q3DMASCTools.h b/q3DMASCTools.h index 7657998..8ac0d88 100644 --- a/q3DMASCTools.h +++ b/q3DMASCTools.h @@ -38,7 +38,7 @@ namespace masc typedef QMap NamedClouds; - static bool LoadTrainingFile(QString filename, Feature::Set& rawFeatures, NamedClouds& loadedClouds, TrainParameters& parameters, CorePoints* corePoints = nullptr); + static bool LoadTrainingFile(QString filename, Feature::Set& rawFeatures, NamedClouds& loadedClouds, TrainParameters& parameters, CorePoints* corePoints = nullptr, QWidget* parent = nullptr); static bool LoadClassifierCloudLabels(QString filename, QList& labels, QString& corePointsLabel, bool& filenamesSpecified); diff --git a/qTrain3DMASCDialog.cpp b/qTrain3DMASCDialog.cpp index cd5017a..e118739 100644 --- a/qTrain3DMASCDialog.cpp +++ b/qTrain3DMASCDialog.cpp @@ -87,7 +87,7 @@ bool Train3DMASCDialog::isFeatureSelected(QString featureName) const void Train3DMASCDialog::sortByFeatureImportance() { - tableWidget->sortByColumn(FeatureImportanceColumn, Qt::AscendingOrder); + tableWidget->sortByColumn(FeatureImportanceColumn, Qt::DescendingOrder); } void Train3DMASCDialog::setFeatureImportance(QString featureName, float importance)