diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index 25143bf6..4da41402 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -536,6 +536,8 @@ void MainWindow::solveButtonClicked() if(ui->pointInitializationTreeView->property("timeSeriesFlag").toBool()) { + CPLDebug("STATION_FETCH", "Time Series option selected..."); + QDateTime start = ui->weatherStationDataStartDateTimeEdit->dateTime(); QDateTime end = ui->weatherStationDataEndDateTimeEdit->dateTime(); @@ -607,6 +609,8 @@ void MainWindow::solveButtonClicked() if(nTimeSteps == 1) { + CPLDebug("STATION_FETCH", "USER WANTS 1 STEP, USING START TIME..."); + int startYear = year[0]; int startMonth = month[0]; int startDay = day[0]; @@ -640,6 +644,8 @@ void MainWindow::solveButtonClicked() } else { + CPLDebug("STATION_FETCH", "USING TIME LIST..."); + ninjaErr = NinjaGetTimeList( ninjaTools, year.data(), month.data(), day.data(), @@ -672,6 +678,8 @@ void MainWindow::solveButtonClicked() if(ninjaErr == NINJA_SUCCESS) { + CPLDebug("STATION_FETCH", "TIME LIST GENERATED..."); + numNinjas = ui->weatherStationDataTimestepsSpinBox->value(); ninjaErr = NinjaMakePointArmy( ninjaArmy, @@ -749,6 +757,8 @@ void MainWindow::solveButtonClicked() } else { + CPLDebug("STATION_FETCH", "USING CURRENT/LATEST TIME DATA..."); + int year, month, day, hour, minute; QDateTime date = ui->weatherStationDataLabel->property("simulationTime").toDateTime(); year = date.date().year(); @@ -806,6 +816,8 @@ void MainWindow::solveButtonClicked() if(ninjaErr == NINJA_SUCCESS) { + CPLDebug("STATION_FETCH", "TIME LIST GENERATED..."); + ninjaErr = NinjaMakePointArmy( ninjaArmy, yearVec.data(), monthVec.data(), dayVec.data(), hourVec.data(), minuteVec.data(), nTimeSteps, @@ -1102,6 +1114,10 @@ bool MainWindow::prepareArmy(NinjaArmyH *ninjaArmy, int numNinjas, const char* i { if(ui->pointInitializationWriteStationKMLCheckBox->isChecked()) { + if(i == 0) + { + writeToConsole("Writing Weather Station .kml"); + } // function needs MAJOR rework to get the testing to work, direct call to non-ninjaArmy function makes this process tougher ninjaErr = NinjaSetStationKML(ninjaArmy, i, ui->elevationInputFileLineEdit->property("fullpath").toString().toUtf8().constData(), ui->outputDirectoryLineEdit->text().toUtf8().constData(), ui->outputSpeedUnitsComboBox->currentText().toUtf8().constData(), papszOptions); //ninjaErr = NinjaSetStationKML(ninjaArmy, i+10, ui->elevationInputFileLineEdit->property("fullpath").toString().toUtf8().constData(), ui->outputDirectoryLineEdit->text().toUtf8().constData(), ui->outputSpeedUnitsComboBox->currentText().toUtf8().constData(), papszOptions); // test error handling // function needs reorganized to handle this test @@ -1169,6 +1185,14 @@ bool MainWindow::prepareArmy(NinjaArmyH *ninjaArmy, int numNinjas, const char* i return false; } + ninjaErr = NinjaSetStabilityFlag(ninjaArmy, i, ui->stabilityCheckBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetStabilityFlag(ninjaArmy, i+10, ui->stabilityCheckBox->isChecked(), papszOptions); // test error handling + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetStabilityFlag: ninjaErr =" << ninjaErr; + return false; + } + if(ui->vegetationStackedWidget->currentIndex() == 0) { ninjaErr = NinjaSetUniVegetation(ninjaArmy, i, ui->vegetationComboBox->currentText().toLower().toUtf8().constData(), papszOptions); @@ -1663,7 +1687,7 @@ void MainWindow::writeSettings() settings.setValue("customRes", ui->meshResolutionSpinBox->value()); settings.setValue("nProcessors", ui->numberOfProcessorsSpinBox->value()); - writeToConsole("Settings saved."); + writeToConsole("Settings saved successfully.", Qt::darkGreen); } void MainWindow::readSettings() @@ -1706,7 +1730,7 @@ void MainWindow::readSettings() ui->numberOfProcessorsSpinBox->setValue(settings.value("nProcessors").toInt()); } - writeToConsole("Settings read."); + writeToConsole("Settings read successfully.", Qt::darkGreen); } void MainWindow::showEvent(QShowEvent *event) diff --git a/src/gui/pointInitializationInput.cpp b/src/gui/pointInitializationInput.cpp index c2e37011..7efeffad 100644 --- a/src/gui/pointInitializationInput.cpp +++ b/src/gui/pointInitializationInput.cpp @@ -93,6 +93,8 @@ void PointInitializationInput::weatherStationDownloadBetweenDatesStartTimeDateTi { if(ui->downloadBetweenDatesEndTimeDateTimeEdit->dateTime() < ui->downloadBetweenDatesStartTimeDateTimeEdit->dateTime()) { + emit writeToConsoleSignal("Start Time is greater than End Time!, fixing End Time..."); + CPLDebug("STATION_FETCH", "START TIME > END TIME, FIXING END TIME"); ui->downloadBetweenDatesEndTimeDateTimeEdit->setDateTime(ui->downloadBetweenDatesStartTimeDateTimeEdit->dateTime().addSecs(3600)); } updateTimeSteps(); @@ -102,6 +104,8 @@ void PointInitializationInput::weatherStationDownloadBetweenDatesEndTimeDateTime { if(ui->downloadBetweenDatesEndTimeDateTimeEdit->dateTime() < ui->downloadBetweenDatesStartTimeDateTimeEdit->dateTime()) { + emit writeToConsoleSignal("Start Time is greater than End Time!, fixing Start Time..."); + CPLDebug("STATION_FETCH", "START TIME > END TIME, FIXING START TIME"); ui->downloadBetweenDatesStartTimeDateTimeEdit->setDateTime(ui->downloadBetweenDatesEndTimeDateTimeEdit->dateTime().addSecs(-3600)); } updateTimeSteps(); @@ -183,7 +187,7 @@ static void comMessageHandler(const char *pszMessage, void *pUser) //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); emit self->updateProgressMessageSignal(QString::fromStdString("StationFetch ended in warning:\n"+clipStr)); - emit self->writeToConsoleSignal(QString::fromStdString("StationFetch warning: "+clipStr), Qt::yellow); + emit self->writeToConsoleSignal(QString::fromStdString("StationFetch warning: "+clipStr), QColor(255, 140, 0)); } else { @@ -194,9 +198,9 @@ static void comMessageHandler(const char *pszMessage, void *pUser) void PointInitializationInput::weatherStationDataDownloadButtonClicked() { - emit writeToConsoleSignal("Fetching station data..."); + emit writeToConsoleSignal("Downloading station data..."); - progress = new QProgressDialog("Fetching Station Data...", QString(), 0, 0, ui->centralwidget); + progress = new QProgressDialog("Downloading Station Data...", QString(), 0, 0, ui->centralwidget); progress->setWindowModality(Qt::WindowModal); progress->setCancelButton(nullptr); progress->setMinimumDuration(0); @@ -212,11 +216,21 @@ void PointInitializationInput::weatherStationDataDownloadButtonClicked() qDebug() << "NinjaSetToolsComMessageHandler(): ninjaErr =" << ninjaErr; } + CPLDebug("STATION_FETCH", "Fetch Station GUI Function"); + CPLDebug("STATION_FETCH", "---------------------------------------"); + QString DEMTimeZone = ui->timeZoneComboBox->currentText(); QByteArray DEMTimeZoneBytes = ui->timeZoneComboBox->currentText().toUtf8(); QDateTime start = ui->downloadBetweenDatesStartTimeDateTimeEdit->dateTime(); QDateTime end = ui->downloadBetweenDatesEndTimeDateTimeEdit->dateTime(); + CPLDebug("STATION_FETCH", "DEM FILE NAME: %s", QFileInfo(ui->elevationInputFileLineEdit->property("fullpath").toString()).absoluteFilePath().toStdString().c_str()); + CPLDebug("STATION_FETCH", "TIME ZONE: %s", DEMTimeZone.toStdString().c_str()); + CPLDebug("STATION_FETCH", "geoLoc: %i, (0=\"Download From DEM\",1=\"Download From Station ID\")", ui->weatherStationDataSourceComboBox->currentIndex()); + CPLDebug("STATION_FETCH", "timeLoc: %i, (0=\"Download Most Recent Data\",1=\"Download Between Two Dates\")", ui->weatherStationDataTimeComboBox->currentIndex()); + CPLDebug("STATION_FETCH", "---------------------------------------"); + CPLDebug("STATION_FETCH", "USING DEM: %s", ui->elevationInputFileLineEdit->text().toStdString().c_str()); + QVector year = {start.date().year(), end.date().year()}; QVector month = {start.date().month(), end.date().month()}; QVector day = {start.date().day(), end.date().day()}; @@ -387,6 +401,14 @@ void PointInitializationInput::weatherStationDataDownloadButtonClicked() QFuture future; if(ui->weatherStationDataSourceComboBox->currentIndex() == 0) { + if(fetchLatestFlag == true) + { + CPLDebug("STATION_FETCH", "Fetch Params: DEM and Current Data"); + } + else + { + CPLDebug("STATION_FETCH", "Fetch Params: DEM and Time series"); + } QString units = ui->downloadFromDEMComboBox->currentText(); double buffer = ui->downloadFromDEMSpinBox->value(); future = QtConcurrent::run(&PointInitializationInput::fetchStationFromBbox, this, @@ -397,6 +419,14 @@ void PointInitializationInput::weatherStationDataDownloadButtonClicked() } else { + if(fetchLatestFlag == true) + { + CPLDebug("STATION_FETCH", "STID and Current Data"); + } + else + { + CPLDebug("STATION_FETCH", "STID and Timeseries"); + } QString stationList = ui->downloadFromStationIDLineEdit->text(); future = QtConcurrent::run(&PointInitializationInput::fetchStationByName, this, ninjaTools, @@ -561,10 +591,11 @@ void PointInitializationInput::fetchStationDataFinished() { // get the return value of the QtConcurrent::run() function int result = futureWatcher->future().result(); + CPLDebug("STATION_FETCH", "station fetch return value: %i", result); if(result == NINJA_SUCCESS) { - emit writeToConsoleSignal("Finished fetching station data.", Qt::darkGreen); + emit writeToConsoleSignal("Finished downloading station data.", Qt::darkGreen); if (progress) { @@ -577,7 +608,7 @@ void PointInitializationInput::fetchStationDataFinished() } else { - emit writeToConsoleSignal("Failed to fetch station data."); + emit writeToConsoleSignal("Failed to download station data."); } } // delete the futureWatcher every time, whether success or failure @@ -633,6 +664,8 @@ void PointInitializationInput::pointInitializationTreeViewItemSelectionChanged(c { AppState& state = AppState::instance(); QModelIndexList selectedRows = ui->pointInitializationTreeView->selectionModel()->selectedRows(); + CPLDebug("STATION_FETCH", "========================================"); + CPLDebug("STATION_FETCH", "NUMBER OF SELECTED STATIONS: %lli", selectedRows.count()); stationFiles.clear(); stationFileTypes.clear(); @@ -641,32 +674,39 @@ void PointInitializationInput::pointInitializationTreeViewItemSelectionChanged(c minStationLocalDateTime = QDateTime(); state.isStationFileSelected = false; - if (selectedRows.count() > 0) + if(selectedRows.count() > 0) { state.isStationFileSelected = true; } for(int i = 0; i < selectedRows.count(); i++) { + // If its a directory, make it so that it can't be selected if(stationFileSystemModel->isDir(selectedRows[i])) { + CPLDebug("STATION_FETCH", "IGNORING SELECTED DIRECTORY!"); ui->pointInitializationTreeView->selectionModel()->select(selectedRows[i], QItemSelectionModel::Deselect | QItemSelectionModel::Rows); return; } + CPLDebug("STATION_FETCH", "----------------------------------------"); + CPLDebug("STATION_FETCH", "STATION NAME: %s", stationFileSystemModel->filePath(selectedRows[i]).toStdString().c_str()); + QString recentFileSelected = stationFileSystemModel->filePath(selectedRows[i]); stationFiles.push_back(recentFileSelected); // note, selected vs valid are two separate things //qDebug() << "[GUI-Point] Selected file path:" << recentFileSelected; + CPLDebug("STATION_FETCH", "Selected file path: %s", recentFileSelected.toStdString().c_str()); QByteArray filePathBytes = recentFileSelected.toUtf8(); const char* filePath = filePathBytes.constData(); char** options = nullptr; int stationHeader = NinjaGetWxStationHeaderVersion(filePath, options); //qDebug() << "[GUI-Point] Station Header: " << stationHeader; + CPLDebug("STATION_FETCH", "STATION HEADER TYPE: %i", stationHeader); if(stationHeader == 1) { - writeToConsoleSignal("Station has old station format, which is no longer allowed!"); + emit writeToConsoleSignal("Station has old station format, which is no longer allowed!"); state.isStationFileSelectionValid = false; return; } @@ -684,45 +724,54 @@ void PointInitializationInput::pointInitializationTreeViewItemSelectionChanged(c if(hDS == NULL) { - writeToConsoleSignal("Cannot open station file!"); + emit writeToConsoleSignal("Cannot open station file!"); state.isStationFileSelectionValid = false; return; } OGRLayer* poLayer = hDS->GetLayer(0); poLayer->ResetReading(); - qint64 lastIndex = poLayer->GetFeatureCount(); + qint64 lastIndex = poLayer->GetFeatureCount(); // How many lines are on disk //qDebug() << "[GUI-Point] Number of Time Entries:" << lastIndex; + CPLDebug("STATION_FETCH", "Number of Time Entries: %llu", lastIndex); OGRFeature* poFeature = poLayer->GetFeature(1); // Skip header, row 1 is first time in series if(poFeature == NULL) { - writeToConsoleSignal("No Stations Found in file!"); + emit writeToConsoleSignal("No Stations Found in file!"); state.isStationFileSelectionValid = false; return; } QString startDateTimeStr(poFeature->GetFieldAsString(15)); // Time should be in 15th (last) column (0-14) - //qDebug() << "[GUI-Point] Station start time:" << startDateTimeStr; poFeature = poLayer->GetFeature(lastIndex); // last time in series QString stopDateTimeStr(poFeature->GetFieldAsString(15)); - //qDebug() << "[GUI-Point] Station end Time:" << stopDateTimeStr; + + //qDebug() << "[GUI-Point] Station start time:" << startDateTimeStr; + //qDebug() << "[GUI-Point] Station end time:" << stopDateTimeStr; + CPLDebug("STATION_FETCH", "STATION START TIME: %s", startDateTimeStr.toStdString().c_str()); + CPLDebug("STATION_FETCH", "STATION END TIME: %s", stopDateTimeStr.toStdString().c_str()); if(startDateTimeStr.isEmpty() && stopDateTimeStr.isEmpty()) // No time series { //qDebug() << "[GUI-Point] File cannot be used for Time Series"; + CPLDebug("STATION_FETCH", "File cannot be used for Time Series"); timeSeriesFlag = false; stationFileTypes.push_back(0); } else if(!startDateTimeStr.isEmpty() && !stopDateTimeStr.isEmpty()) // Some type of time series { //qDebug() << "[GUI-Point] File can be used for Time Series, suggesting time series parameters..."; + CPLDebug("STATION_FETCH", "File can be used for Times Series, suggesting time series parameters..."); + CPLDebug("STATION_FETCH", "Suggesting Potentially Reasonable Time Series Parameters..."); readStationTime(startDateTimeStr, stopDateTimeStr); stationFileTypes.push_back(1); } } + CPLDebug("STATION_FETCH", "Type of Station File: %i", stationFileTypes[i]); + ui->pointInitializationDataTimeStackedWidget->setCurrentIndex(timeSeriesFlag ? 0 : 1); if (!timeSeriesFlag) @@ -735,9 +784,13 @@ void PointInitializationInput::pointInitializationTreeViewItemSelectionChanged(c } state.isStationFileSelectionValid = true; - for (int i = 0; i < stationFileTypes.size(); i++) + for(int i = 0; i < stationFileTypes.size(); i++) { - if (stationFileTypes[i] != stationFileTypes[0]) { + CPLDebug("STATION_FETCH", "stationFileTypes[%i] = %i", i, stationFileTypes[i]); + if(stationFileTypes[i] != stationFileTypes[0]) + { + CPLDebug("STATION_FETCH", "found unique stationFileType at: %i", i); + CPLDebug("STATION_FETCH", "WARNING NOT ALL CSVS ARE OF THE SAME TYPE, CANNOT CONTINUE"); state.isStationFileSelectionValid = false; break; } @@ -819,6 +872,11 @@ void PointInitializationInput::readStationTime(QString startDateTimeStr, QString ui->weatherStationDataStartDateTimeEdit->setEnabled(true); ui->weatherStationDataEndDateTimeEdit->setEnabled(true); + CPLDebug("STATION_FETCH", "minStationLocalDateTime = %s", minStationLocalDateTime.toString("MM/dd/yyyy HH:mm").toStdString().c_str()); + CPLDebug("STATION_FETCH", "maxStationLocalDateTime = %s", maxStationLocalDateTime.toString("MM/dd/yyyy HH:mm").toStdString().c_str()); + emit writeToConsoleSignal("Start Time (local): "+minStationLocalDateTime.toString()); + emit writeToConsoleSignal("End Time (local): "+maxStationLocalDateTime.toString()); + updateTimeSteps(); } @@ -852,6 +910,8 @@ void PointInitializationInput::weatherStationDataStartDateTimeEditChanged() { if(ui->weatherStationDataEndDateTimeEdit->dateTime() < ui->weatherStationDataStartDateTimeEdit->dateTime()) { + emit writeToConsoleSignal("Start Time is greater than End Time!, fixing End Time..."); + CPLDebug("STATION_FETCH", "START TIME > END TIME, FIXING END TIME!"); ui->weatherStationDataEndDateTimeEdit->setDateTime(ui->weatherStationDataStartDateTimeEdit->dateTime().addSecs(3600)); } updateTimeSteps(); @@ -861,6 +921,8 @@ void PointInitializationInput::weatherStationDataEndDateTimeEditChanged() { if(ui->weatherStationDataEndDateTimeEdit->dateTime() < ui->weatherStationDataStartDateTimeEdit->dateTime()) { + emit writeToConsoleSignal("Start Time is greater than End Time!, fixing Start Time..."); + CPLDebug("STATION_FETCH", "START TIME > END TIME, FIXING START TIME!"); ui->weatherStationDataStartDateTimeEdit->setDateTime(ui->weatherStationDataEndDateTimeEdit->dateTime().addSecs(-3600)); } updateTimeSteps(); @@ -868,6 +930,8 @@ void PointInitializationInput::weatherStationDataEndDateTimeEditChanged() void PointInitializationInput::updateTimeSteps() { + CPLDebug("STATION_FETCH", "Updating suggested time steps..."); + int timesteps; if(ui->weatherStationDataStartDateTimeEdit->dateTime() == ui->weatherStationDataEndDateTimeEdit->dateTime()) @@ -884,6 +948,7 @@ void PointInitializationInput::updateTimeSteps() if(timesteps == 1) { + CPLDebug("STATION_FETCH", "One Step Set for Timeseries, greying out stop time!"); ui->weatherStationDataEndDateTimeEdit->setEnabled(false); ui->weatherStationDataEndDateTimeEdit->setToolTip("Stop time is disabled for 1 time step simulations"); } diff --git a/src/gui/surfaceInput.cpp b/src/gui/surfaceInput.cpp index f685c7ac..6f8fc2e8 100644 --- a/src/gui/surfaceInput.cpp +++ b/src/gui/surfaceInput.cpp @@ -99,6 +99,8 @@ void SurfaceInput::elevationInputTypePushButtonClicked() void SurfaceInput::boundingBoxReceived(double north, double south, double east, double west) { + qDebug() << "north south east west =" << QString::number(north) << QString::number(south) << QString::number(east) << QString::number(west); + ui->boundingBoxNorthLineEdit->blockSignals(true); ui->boundingBoxEastLineEdit->blockSignals(true); ui->boundingBoxSouthLineEdit->blockSignals(true); @@ -319,6 +321,8 @@ void SurfaceInput::elevationInputFileOpenButtonClicked() QString directoryPath; QFileInfo inputFileDirInfo(inputFileDir); + CPLDebug("WINDNINJA", "inputFileDir=%s", inputFileDirInfo.absolutePath().toStdString().c_str()); + if(!ui->elevationInputFileLineEdit->property("fullpath").toString().isEmpty()) { directoryPath = ui->elevationInputFileLineEdit->property("fullpath").toString(); @@ -345,7 +349,22 @@ void SurfaceInput::elevationInputFileOpenButtonClicked() return; } - bool retVal = loadDemMetadata(demFilePath); + // check and attempt to fill the to be opened dem file for NO_DATA values + // update the dem file if NO_DATA values are filled + QString nanFilledDemFilePath; + bool retVal = checkAndFillToBeOpenedDemNoDataValues(demFilePath, nanFilledDemFilePath); + if(retVal == false) + { + // failed with some kind of error, messaging is handled by the underlying function + return; + } + if(!nanFilledDemFilePath.isEmpty()) + { + // required and succeeded in filling + demFilePath = nanFilledDemFilePath; + } + + retVal = loadDemMetadata(demFilePath); if(retVal == false) { return; @@ -358,9 +377,9 @@ void SurfaceInput::elevationInputFileOpenButtonClicked() void SurfaceInput::startFetchDEM(QVector boundingBox, std::string demFile, double resolution, std::string fetchType) { - emit writeToConsoleSignal("Fetching DEM file..."); + emit writeToConsoleSignal("Downloading DEM file..."); - progress = new QProgressDialog("Fetching DEM file...", QString(), 0, 0, ui->centralwidget); + progress = new QProgressDialog("Downloading DEM file...", QString(), 0, 0, ui->centralwidget); progress->setWindowModality(Qt::WindowModal); progress->setCancelButton(nullptr); progress->setMinimumDuration(0); @@ -393,11 +412,20 @@ void SurfaceInput::fetchDEMFinished() // get the return value of the QtConcurrent::run() function int result = futureWatcher->future().result(); - if(result == NINJA_SUCCESS) + if(result >= 0) // returned NINJA_SUCCESS, or a nNoDataCount value { - emit writeToConsoleSignal("Finished fetching DEM file.", Qt::darkGreen); + emit writeToConsoleSignal("Finished downloading DEM file.", Qt::darkGreen); + + bool retVal = true; + + // check and attempt to fill the downloaded dem file for NO_DATA values + retVal = checkAndFillDownloadedDemNoDataValues(pendingDownloadDemFilePath); + + if(retVal == true) + { + retVal = loadDemMetadata(pendingDownloadDemFilePath); + } - bool retVal = loadDemMetadata(pendingDownloadDemFilePath); if(retVal == true) { ui->elevationInputFileLineEdit->setProperty("fullpath", pendingDownloadDemFilePath); @@ -405,10 +433,11 @@ void SurfaceInput::fetchDEMFinished() ui->elevationInputFileLineEdit->setToolTip(pendingDownloadDemFilePath); ui->inputsStackedWidget->setCurrentIndex(3); } + //else // if(retVal == false) //{ - // // message is handled in loadDemMetadata() - // // don't want to return here, need to wrap up all the other todos of this function or things won't close properly + // // message is handled in the various underlying functions + // // don't want to return here, need to wrap up all the other todos of this finished() function or things won't close properly // //return; //} @@ -421,7 +450,7 @@ void SurfaceInput::fetchDEMFinished() } else { - emit writeToConsoleSignal("Failed to fetch DEM file."); + emit writeToConsoleSignal("Failed to download DEM file."); } // delete the futureWatcher every time, whether success or failure @@ -707,7 +736,7 @@ static void comMessageHandler(const char *pszMessage, void *pUser) //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); emit self->updateProgressMessageSignal(QString::fromStdString("SurfaceFetch ended in warning:\n"+clipStr+"\n")); - emit self->writeToConsoleSignal(QString::fromStdString("SurfaceFetch warning: "+clipStr), Qt::yellow); + emit self->writeToConsoleSignal(QString::fromStdString("SurfaceFetch warning: "+clipStr), QColor(255, 140, 0)); } else { @@ -743,8 +772,162 @@ int SurfaceInput::fetchDEMFile(QVector boundingBox, std::string demFile, return NINJA_SUCCESS; } +bool SurfaceInput::checkAndFillDownloadedDemNoDataValues(const QString demFilePath) +{ + emit writeToConsoleSignal("Checking downloaded dem file for NO_DATA values..."); + + GDALDataset *poDS; + poDS = (GDALDataset*)GDALOpen(demFilePath.toStdString().c_str(), GA_Update); + if(poDS == nullptr) + { + qCritical() << "ERROR: Cannot open dem file."; + comMessageHandler("ERROR: Cannot open dem file.", this); + return false; + } + + if(GDALHasNoData(poDS, 1)) + { + qWarning() << "WARNING: The downloaded file contains NO_DATA values. WindNinja will attempt to fill with valid data..."; + emit writeToConsoleSignal("WARNING: The downloaded file contains NO_DATA values. WindNinja will attempt to fill with valid data...", QColor(255, 140, 0)); + QMessageBox::information(nullptr, tr("WindNinja"), + tr("The downloaded file contains NO_DATA values. WindNinja will attempt to fill with valid data. Please click OK to proceed."), + QMessageBox::Ok); + // need this OR the QMessageBox and writeToConsoleSignal() + //comMessageHandler("WARNING: The downloaded file contains NO_DATA values. WindNinja will attempt to fill with valid data...", this); + + int nNoDataValues = GDALFillBandNoData(poDS, 1, 100); + if(nNoDataValues) + { + qWarning() << "WARNING: Could not fill NO_DATA pixels, too many pixels were invalid."; + emit writeToConsoleSignal("WARNING: Could not fill NO_DATA pixels, too many pixels were invalid.", QColor(255, 140, 0)); + QMessageBox::warning(nullptr, tr("WindNinja"), + tr("Could not fill NO_DATA pixels, too many pixels were invalid."), + QMessageBox::Ok); + // again, need this OR the QMessageBox and writeToConsoleSignal() + //comMessageHandler("WARNING: Could not fill no data pixels, too many pixels were invalid.", this); + GDALClose((GDALDatasetH)poDS); + return false; + } + qInfo() << "succeeded in filling NO_DATA pixels."; + emit writeToConsoleSignal("succeeded in filling NO_DATA pixel.", Qt::darkGreen); + QMessageBox::information(nullptr, tr("WindNinja"), + tr("Succeeded in filling NO_DATA pixels."), + QMessageBox::Ok); + } + + GDALClose((GDALDatasetH)poDS); + + emit writeToConsoleSignal("finished checking for NO_DATA values.", Qt::darkGreen); + return true; +} + +bool SurfaceInput::checkAndFillToBeOpenedDemNoDataValues(const QString demFilePath, QString& nanFilledDemFilePath) +{ + emit writeToConsoleSignal("Checking dem file for NO_DATA values..."); + + nanFilledDemFilePath = ""; + + GDALDataset *poDS = (GDALDataset*)GDALOpen(demFilePath.toStdString().c_str(), GA_ReadOnly); + if(poDS == nullptr) + { + qCritical() << "ERROR: Cannot open dem file."; + comMessageHandler("ERROR: Cannot open dem file.", this); + return false; + } + + if(GDALHasNoData(poDS, 1)) + { + qWarning() << "WARNING: The dem contains NO_DATA values. WindNinja will attempt to fill with valid data..."; + emit writeToConsoleSignal("WARNING: The dem contains NO_DATA values. WindNinja will attempt to fill with valid data...", QColor(255, 140, 0)); + int response = QMessageBox::warning(nullptr, tr("WindNinja"), + tr("The input dataset contains pixels with NO_DATA. " + "These datasets cannot be used by WindNinja, " + "would you like to attempt to fill those pixels?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + + if(response == QMessageBox::Yes) + { + nanFilledDemFilePath = QFileDialog::getSaveFileName(ui->centralwidget, tr("Set NO_DATA Filled DEM File"), + QFileInfo(demFilePath).absolutePath(), + tr("GeoTiff (*.tif)")); + if(nanFilledDemFilePath.isEmpty()) + { + qWarning() << "Cancelled by user with no selection. Not filling NO_DATA values. Not loading dem file."; + emit writeToConsoleSignal("Cancelled by user with no selection. Not filling NO_DATA values. Not loading dem file."); + return false; + } + + if(!nanFilledDemFilePath.endsWith(".tif", Qt::CaseInsensitive)) + { + nanFilledDemFilePath += ".tif"; + } + + GDALDriverH hDriver = GDALGetDriverByName("GTiff"); + GDALDatasetH hNewDS; + hNewDS = GDALCreateCopy(hDriver, nanFilledDemFilePath.toStdString().c_str(), (GDALDriverH)poDS, FALSE, NULL, NULL, NULL); + + int nSuccess; + GDALRasterBandH hSrcBand, hDstBand; + hSrcBand = GDALGetRasterBand((GDALDatasetH)poDS, 1); + hDstBand = GDALGetRasterBand(hNewDS, 1); + GDALSetRasterNoDataValue(hDstBand, GDALGetRasterNoDataValue(hSrcBand, &nSuccess)); + if(nSuccess == false) + { + qCritical() << "ERROR: Could not get NO_DATA value definition from the dem file."; + comMessageHandler("ERROR: Could not get NO_DATA value definition from the dem file.", this); + return false; + } + + int nNoData = GDALFillBandNoData((GDALDataset*)hNewDS, 1, 100); + if(nNoData) + { + qWarning() << "WARNING: Could not fill NO_DATA pixels, too many pixels were invalid."; + emit writeToConsoleSignal("WARNING: Could not fill NO_DATA pixels, too many pixels were invalid.", QColor(255, 140, 0)); + QMessageBox::warning(nullptr, tr("WindNinja"), + tr("Could not fill NO_DATA pixels, too many pixels were invalid."), + QMessageBox::Ok); + // again, need this OR the QMessageBox and writeToConsoleSignal() + //comMessageHandler("WARNING: Could not fill NO_DATA pixels, too many pixels were invalid.", this); + GDALClose(hNewDS); + GDALClose((GDALDatasetH)poDS); + VSIUnlink(nanFilledDemFilePath.toStdString().c_str()); + nanFilledDemFilePath = ""; + return false; + } + else + { + GDALFlushCache(hNewDS); + GDALClose(hNewDS); + GDALClose((GDALDatasetH)poDS); + qInfo() << "succeeded in filling NO_DATA pixels."; + emit writeToConsoleSignal("succeeded in filling NO_DATA pixels.", Qt::darkGreen); + QMessageBox::information(nullptr, tr("WindNinja"), + tr("Succeeded in filling NO_DATA pixels."), + QMessageBox::Ok); + return true; + } + } + else + { + qWarning() << "Cancelled by user. Not filling NO_DATA values. Not loading dem file."; + emit writeToConsoleSignal("Cancelled by user. Not filling NO_DATA values. Not loading dem file."); + return false; + } + } + else + { + GDALClose((GDALDatasetH)poDS); + } + + emit writeToConsoleSignal("finished checking for NO_DATA values.", Qt::darkGreen); + return true; +} + bool SurfaceInput::loadDemMetadata(const QString demFilePath) { + emit writeToConsoleSignal("Opening dem file to load in metadata..."); + emit writeToConsoleSignal("demFilePath="+demFilePath); + CPLSetConfigOption( "GDAL_PAM_ENABLED", "OFF" ); double adfGeoTransform[6]; @@ -753,11 +936,15 @@ bool SurfaceInput::loadDemMetadata(const QString demFilePath) poInputDS = (GDALDataset*)GDALOpen(demFilePath.toStdString().c_str(), GA_ReadOnly); if(poInputDS == nullptr) { - qCritical() << "ERROR: Cannot open dem file for reading in SurfaceInput::loadDemMetadata()."; - comMessageHandler("ERROR: Cannot open dem file for reading in SurfaceInput::loadDemMetadata().", this); + qCritical() << "ERROR: Cannot open dem file."; + comMessageHandler("ERROR: Cannot open dem file.", this); return false; } + QString GDALDriverLongName = poInputDS->GetDriver()->GetMetadataItem(GDAL_DMD_LONGNAME); + emit writeToConsoleSignal("using "+GDALDriverLongName+" driver to read dem file..."); + + // set the file type here QString GDALDriverName = poInputDS->GetDriver()->GetDescription(); if(GDALDriverName == "AAIGrid") { @@ -770,7 +957,8 @@ bool SurfaceInput::loadDemMetadata(const QString demFilePath) else if (GDALDriverName == "GTiff") { int bandCount = GDALGetRasterCount(poInputDS); - if(bandCount >1) + // if it's a multi-band GeoTIFF, it's an lcp + if(bandCount > 1) { demFileType = "LCP"; } @@ -784,36 +972,107 @@ bool SurfaceInput::loadDemMetadata(const QString demFilePath) demFileType = "IMG"; } + // get x and y dimension GDALXSize = poInputDS->GetRasterXSize(); GDALYSize = poInputDS->GetRasterYSize(); - GDALGetCorners(poInputDS, DEMCorners); + if(!GDALTestSRS(poInputDS)) + { + qCritical() << "ERROR: Invalid spatial reference (prj), cannot do a simulation with the supplied dem file."; + comMessageHandler("ERROR: Invalid spatial reference (prj), cannot do a simulation with the supplied dem file.", this); + GDALClose((GDALDatasetH)poInputDS); + return false; + } + else + { + std::string GDALProjRef = poInputDS->GetProjectionRef(); + const char *pszProjRef; + OGRSpatialReference oSRS; + pszProjRef = GDALProjRef.c_str(); + oSRS.importFromWkt((char**)&pszProjRef); + if(GDALProjRef == "") + { + qCritical() << "ERROR: Invalid spatial reference (prj), cannot do a simulation with the supplied dem file."; + comMessageHandler("ERROR: Invalid spatial reference (prj), cannot do a simulation with the supplied dem file.", this); + GDALClose((GDALDatasetH)poInputDS); + return false; + } + + // Check for geographic. Separate case as we may allow support later on. + if(oSRS.IsGeographic()) + { + qCritical() << "ERROR: The dem coordinate system is in a geographic projection (latitude/longitude). WindNinja only supports projected coordinate systems (e.g., UTM)"; + comMessageHandler("ERROR: The dem coordinate system is in a geographic projection (latitude/longitude). WindNinja only supports projected coordinate systems (e.g., UTM)", this); + GDALClose((GDALDatasetH)poInputDS); + return false; + } + + // if it gets here, it has a valid projected coordinate system + } + + // check for no data values + if(GDALHasNoData(poInputDS, 1)) + { + qCritical() << "ERROR: The dem file contains NO_DATA values, cannot use."; + comMessageHandler("ERROR: The dem file contains NO_DATA values, cannot use.", this); + GDALClose((GDALDatasetH)poInputDS); + return false; + } + + // get dem corners + //if(!GDALGetCorners(poInputDS, DEMCorners)) // this actually returns 0 when success, rather than 1, so strange + if(GDALGetCorners(poInputDS, DEMCorners)) + { + qCritical() << "ERROR: Cannot get the corners of the dem file, cannot use."; + comMessageHandler("ERROR: Cannot get the corners of the dem file, cannot use.", this); + GDALClose((GDALDatasetH)poInputDS); + return false; + } + + // get center of the dem double latitude, longitude; - GDALGetCenter(poInputDS, &longitude, &latitude); + if(!GDALGetCenter(poInputDS, &longitude, &latitude)) + { + qCritical() << "ERROR: Cannot get the center of the dem file, cannot use."; + comMessageHandler("ERROR: Cannot get the center of the dem file, cannot use.", this); + GDALClose((GDALDatasetH)poInputDS); + return false; + } + + // get dem timezone std::string timeZone = FetchTimeZone(longitude, latitude, NULL); int index = ui->timeZoneComboBox->findText(QString::fromStdString(timeZone)); - if (index >= 0) + if(index >= 0) { ui->timeZoneComboBox->setCurrentIndex(index); } + // get the geo-transform, get the cell size, check the dem has square cell size if (poInputDS->GetGeoTransform(adfGeoTransform) == CE_None) { double c1, c2; c1 = adfGeoTransform[1]; c2 = adfGeoTransform[5]; - if (abs(c1) == abs(c2)) { + if(abs(c1) == abs(c2)) + { GDALCellSize = abs(c1); - } else { + } + else + { + qCritical() << "ERROR: The dem file has non-square cell size, cannot use."; + comMessageHandler("ERROR: The dem file has non-square cell size, cannot use.", this); GDALClose((GDALDatasetH)poInputDS); + return false; } } + // get min/max values GDALRasterBand* band = poInputDS->GetRasterBand(1); int gotMin = 0, gotMax = 0; double minVal = band->GetMinimum(&gotMin); double maxVal = band->GetMaximum(&gotMax); - if (!gotMin || !gotMax) { + if(!gotMin || !gotMax) + { band->ComputeStatistics(false, &minVal, &maxVal, nullptr, nullptr, nullptr, nullptr); } @@ -824,6 +1083,8 @@ bool SurfaceInput::loadDemMetadata(const QString demFilePath) CPLSetConfigOption( "GDAL_PAM_ENABLED", "ON" ); + emit writeToConsoleSignal("Metadata loaded from dem file successfully.", Qt::darkGreen); + return true; } @@ -928,6 +1189,8 @@ void SurfaceInput::updateMeshResolutionByUnits() ui->meshResolutionSpinBox->setValue(ui->meshResolutionSpinBox->value() * 3.28084); } } + + emit writeToConsoleSignal("Mesh Resolution set to "+QString::number(ui->meshResolutionSpinBox->value())+" "+ui->meshResolutionUnitsComboBox->itemData(ui->meshResolutionUnitsComboBox->currentIndex()).toString().toUtf8().constData()); } void SurfaceInput::computeBoundingBox(double centerLat, double centerLon, double radius, double boundingBox[4]) diff --git a/src/gui/surfaceInput.h b/src/gui/surfaceInput.h index 13d33624..cd47ed83 100644 --- a/src/gui/surfaceInput.h +++ b/src/gui/surfaceInput.h @@ -103,6 +103,8 @@ private slots: QString fetchTimeZoneDetails(QString currentTimeZone); QVector> fetchAllTimeZones(bool isShowAllTimeZonesSelected); int fetchDEMFile(QVector boundingBox, std::string demFile, double resolution, std::string fetchType); + bool checkAndFillDownloadedDemNoDataValues(const QString demFilePath); + bool checkAndFillToBeOpenedDemNoDataValues(const QString demFilePath, QString& nanFilledDemFilePath); bool loadDemMetadata(const QString demFilePath); void computeBoundingBox(double centerLat, double centerLon, double radius, double boundingBox[4]); void computePointRadius(double north, double east, double south, double west, double pointRadius[3]); diff --git a/src/gui/weatherModelInput.cpp b/src/gui/weatherModelInput.cpp index 2d6c386d..5be8a5fe 100644 --- a/src/gui/weatherModelInput.cpp +++ b/src/gui/weatherModelInput.cpp @@ -63,9 +63,9 @@ WeatherModelInput::WeatherModelInput(Ui::MainWindow* ui, QObject* parent) void WeatherModelInput::weatherModelDownloadButtonClicked() { - emit writeToConsoleSignal("Fetching weather model data..."); + emit writeToConsoleSignal("Downloading weather model data..."); - progress = new QProgressDialog("Fetching Forecast Data...", QString(), 0, 0, ui->centralwidget); + progress = new QProgressDialog("Downloading Forecast Data...", QString(), 0, 0, ui->centralwidget); progress->setWindowModality(Qt::WindowModal); progress->setCancelButton(nullptr); progress->setMinimumDuration(0); @@ -79,7 +79,7 @@ void WeatherModelInput::weatherModelDownloadButtonClicked() if (ui->weatherModelComboBox->currentText().contains("PASTCAST")) { - progress->setLabelText("Fetching Pastcast Data..."); + progress->setLabelText("Downloading Pastcast Data..."); QDateTime start = ui->pastcastStartDateTimeEdit->dateTime(); QDateTime end = ui->pastcastEndDateTimeEdit->dateTime(); @@ -188,7 +188,7 @@ static void comMessageHandler(const char *pszMessage, void *pUser) //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); emit self->updateProgressMessageSignal(QString::fromStdString("WeatherModelFetch ended in warning:\n"+clipStr)); - emit self->writeToConsoleSignal(QString::fromStdString("WeatherModelFetch warning: "+clipStr), Qt::yellow); + emit self->writeToConsoleSignal(QString::fromStdString("WeatherModelFetch warning: "+clipStr), QColor(255, 140, 0)); } else { @@ -295,7 +295,7 @@ void WeatherModelInput::weatherModelDownloadFinished() if(result == NINJA_SUCCESS) { - emit writeToConsoleSignal("Finished fetching weather model data.", Qt::darkGreen); + emit writeToConsoleSignal("Finished downloading weather model data.", Qt::darkGreen); if (progress) { @@ -305,7 +305,7 @@ void WeatherModelInput::weatherModelDownloadFinished() } } else { - emit writeToConsoleSignal("Failed to fetch weather model data."); + emit writeToConsoleSignal("Failed to download weather model data."); } // delete the futureWatcher every time, whether success or failure diff --git a/src/ninja/landfireclient.cpp b/src/ninja/landfireclient.cpp index 42d18f8d..6d6ff0a8 100644 --- a/src/ninja/landfireclient.cpp +++ b/src/ninja/landfireclient.cpp @@ -345,8 +345,6 @@ SURF_FETCH_E LandfireClient::FetchBoundingBox( double *bbox, double resolution, VSIUnlink( pszTmpZip ); } - - return nNoDataCount; } diff --git a/src/ninja/ninjaArmy.cpp b/src/ninja/ninjaArmy.cpp index accd5567..b83ca31d 100644 --- a/src/ninja/ninjaArmy.cpp +++ b/src/ninja/ninjaArmy.cpp @@ -1456,6 +1456,7 @@ int ninjaArmy::NinjaMakePointArmy( int * yearList, int * monthList, int * dayLis sFiles.emplace_back(stationFileNames[i]); } pointInitialization::storeFileNames(sFiles); + CPLDebug("STATION_FETCH", "FILES STORED..."); makePointArmy( timeList, std::string(timeZone), sFiles[0], std::string(elevationFile), matchPointsFlag, momentumFlag ); } diff --git a/src/ninja/ninjaTools.cpp b/src/ninja/ninjaTools.cpp index e1f8cc32..6e2f1341 100644 --- a/src/ninja/ninjaTools.cpp +++ b/src/ninja/ninjaTools.cpp @@ -120,7 +120,8 @@ int ninjaTools::fetchDEMBBox(double *boundsBox, const char *fileName, double res double southBound = boundsBox[2]; double westBound = boundsBox[3]; int result = fetcher->FetchBoundingBox(boundsBox, resolution, fileName, NULL); - if (result != 0) + //if(result != 0) + if(result < 0) { //Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); Com->ninjaCom(ninjaComClass::ninjaFailure, "in ninjaTools::fetchDEMBBox(), fetching failed!"); @@ -173,7 +174,8 @@ int ninjaTools::fetchDEMPoint(double * adfPoint,double *adfBuff, const char* uni } lengthUnits::eLengthUnits ninjaUnits = lengthUnits::getUnit(std::string(units)); int result = fetcher->FetchPoint(adfPoint, adfBuff, ninjaUnits, dfCellSize, pszDstFile, papszOptions); - if (result != 0) + //if(result != 0) + if(result < 0) { //Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); Com->ninjaCom(ninjaComClass::ninjaFailure, "in ninjaTools::fetchDEMPoint(), fetching failed!"); @@ -367,6 +369,16 @@ int ninjaTools::fetchStationFromBBox( const int* yearList, const int * monthList timeList.push_back(boost::posix_time::ptime(boost::gregorian::date(yearList[i], monthList[i], dayList[i]), boost::posix_time::time_duration(hourList[i], minuteList[i], 0, 0))); } + // Custom API_KEY STUFF + const char *api_key_conf_opt = CPLGetConfigOption("CUSTOM_API_KEY", "FALSE"); + if(api_key_conf_opt != "FALSE") + { + std::ostringstream api_stream; + api_stream << api_key_conf_opt; + pointInitialization::setCustomAPIKey(api_stream.str()); + } + // End Custom API_KEY STUFF + wxStation::SetStationFormat(wxStation::newFormat); if(!fetchLatestFlag) @@ -389,7 +401,8 @@ int ninjaTools::fetchStationFromBBox( const int* yearList, const int * monthList bool success = pointInitialization::fetchStationFromBbox(std::string(elevationFile), timeList, timeZone, fetchLatestFlag); if(!success) { - Com->ninjaCom(ninjaComClass::ninjaFailure, "pointInitialization::fetchStationFromBbox() failed."); + Com->ninjaCom(ninjaComClass::ninjaFailure, "pointInitialization::fetchStationFromBbox() failed.\nCould not read station File: Possibly no stations exist for request."); + pointInitialization::removeBadDirectory(stationPathName); return NINJA_E_INVALID; } if(locationFileFlag) @@ -426,6 +439,16 @@ int ninjaTools::fetchStationByName( const int* yearList, const int * monthList, timeList.push_back(boost::posix_time::ptime(boost::gregorian::date(yearList[i], monthList[i], dayList[i]), boost::posix_time::time_duration(hourList[i], minuteList[i], 0, 0))); } + // Custom API_KEY STUFF + const char *api_key_conf_opt = CPLGetConfigOption("CUSTOM_API_KEY", "FALSE"); + if(api_key_conf_opt != "FALSE") + { + std::ostringstream api_stream; + api_stream << api_key_conf_opt; + pointInitialization::setCustomAPIKey(api_stream.str()); + } + // End Custom API_KEY STUFF + wxStation::SetStationFormat(wxStation::newFormat); if(!fetchLatestFlag) @@ -447,7 +470,8 @@ int ninjaTools::fetchStationByName( const int* yearList, const int * monthList, bool success = pointInitialization::fetchStationByName(std::string(stationList), timeList, timeZone, fetchLatestFlag); if(!success) { - Com->ninjaCom(ninjaComClass::ninjaFailure, "pointInitialization::fetchStationByName() failed."); + Com->ninjaCom(ninjaComClass::ninjaFailure, "pointInitialization::fetchStationByName() failed.\nCould not read station File: Possibly no stations exist for request."); + pointInitialization::removeBadDirectory(stationPathName); return NINJA_E_INVALID; } if(locationFileFlag)