diff --git a/src/gui/WidgetDownloadDEM.cpp b/src/gui/WidgetDownloadDEM.cpp index 9130b716..7328c20c 100644 --- a/src/gui/WidgetDownloadDEM.cpp +++ b/src/gui/WidgetDownloadDEM.cpp @@ -48,13 +48,17 @@ WidgetDownloadDEM::WidgetDownloadDEM(QWidget *parent) QString pathToCerts = QString::fromStdString(pathToSslCerts); QSslSocket::addDefaultCaCertificates( pathToCerts, QSsl::Pem, QRegExp::FixedString); + + wasDemFetched = false; + elevSource = ""; + demSelected = false; setupUi(this); setupGM(); initializeGoogleMapsInterface(); connectInputs(); - this->readSettings(); + this->readSettings(); // sets northBounds, southBound, eastBound, westBound initial values progressBar = new QProgressDialog(this); progressBar->setWindowModality(Qt::ApplicationModal); @@ -211,6 +215,8 @@ void WidgetDownloadDEM::setupGM() */ void WidgetDownloadDEM::saveDEM() { + wasDemFetched = true; + QVariant mbr = wvGoogleMaps->page()->mainFrame()->evaluateJavaScript("mbr()"); if(mbr.isNull()) { qDebug()<<"no mbr"; @@ -372,6 +378,7 @@ void WidgetDownloadDEM::updateDEMSource(int index) { switch(index){ case 0: //SRTM + elevSource = "srtm"; fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::SRTM, FindDataPath("/data")); northDEMBound = srtm_northBound; southDEMBound = srtm_southBound; @@ -381,6 +388,7 @@ void WidgetDownloadDEM::updateDEMSource(int index) break; #ifdef HAVE_GMTED case 1: //GMTED + elevSource = "gmted"; fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::WORLD_GMTED, FindDataPath("/data")); northDEMBound = world_gmted_northBound; southDEMBound = world_gmted_southBound; @@ -391,6 +399,7 @@ void WidgetDownloadDEM::updateDEMSource(int index) #endif #ifdef WITH_LCP_CLIENT case 2: //LCP + elevSource = "lcp"; fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::LCP, FindDataPath("/data")); northDEMBound = lcp_northBound; southDEMBound = lcp_southBound; @@ -567,6 +576,36 @@ void WidgetDownloadDEM::readSettings() longitude = 43.911944; } +bool WidgetDownloadDEM::get_wasDemFetched() +{ + return wasDemFetched; +} + +std::string WidgetDownloadDEM::get_elevSource() +{ + return elevSource; +} + +double WidgetDownloadDEM::get_northBound() +{ + return northBound; +} + +double WidgetDownloadDEM::get_southBound() +{ + return southBound; +} + +double WidgetDownloadDEM::get_eastBound() +{ + return eastBound; +} + +double WidgetDownloadDEM::get_westBound() +{ + return westBound; +} + /** * @brief Handles close events of GUI such as clicking X close icon * diff --git a/src/gui/WidgetDownloadDEM.h b/src/gui/WidgetDownloadDEM.h index c3471d0d..09227824 100644 --- a/src/gui/WidgetDownloadDEM.h +++ b/src/gui/WidgetDownloadDEM.h @@ -67,7 +67,14 @@ class WidgetDownloadDEM : public QWidget, private Ui::WidgetDownloadDEM void writeSettings(); void readSettings(); int fetchBoundBox(double *boundsBox, const char *fileName, double resolution); - + + bool get_wasDemFetched(); + std::string get_elevSource(); + double get_northBound(); + double get_southBound(); + double get_eastBound(); + double get_westBound(); + protected: void closeEvent(QCloseEvent *event); @@ -89,6 +96,9 @@ class WidgetDownloadDEM : public QWidget, private Ui::WidgetDownloadDEM private: QDialog dlg; + bool wasDemFetched; + std::string elevSource; + double latitude; double longitude; double northBound; diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index 620c035d..f05b6387 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -67,6 +67,8 @@ mainWindow::mainWindow(QWidget *parent) GDALCenterLat = GDALCenterLon = 0; hasGDALCenter = false; + demWidget = NULL; + tree = new WindNinjaTree; createConsole(); @@ -118,26 +120,28 @@ mainWindow::mainWindow(QWidget *parent) ** Check for version updates, or messages from the server. */ #ifdef PHONE_HOME_QUERIES_ENABLED -void mainWindow::checkMessages(void) { +void mainWindow::checkMessages(void) +{ QMessageBox mbox; char *papszMsg = NinjaQueryServerMessages(true); - if (papszMsg != NULL) { - if (strcmp(papszMsg, "TRUE\n") == 0) { - mbox.setText("There is a fatal flaw in Windninja, it must close."); - mbox.exec(); - delete[] papszMsg; - abort(); - } - - else { - char *papszMsg = NinjaQueryServerMessages(false); - if (papszMsg != NULL) { - mbox.setText(papszMsg); - - mbox.exec(); - delete[] papszMsg; - } - } + if (papszMsg != NULL) + { + if (strcmp(papszMsg, "TRUE\n") == 0) + { + mbox.setText("There is a fatal flaw in Windninja, it must close."); + mbox.exec(); + delete[] papszMsg; + abort(); + } else + { + char *papszMsg = NinjaQueryServerMessages(false); + if (papszMsg != NULL) + { + mbox.setText(papszMsg); + mbox.exec(); + delete[] papszMsg; + } + } } } @@ -1607,7 +1611,7 @@ int mainWindow::checkInputFile(QString fileName) //get the geo-transform if(poInputDS->GetGeoTransform(adfGeoTransform) == CE_None) { - int c1, c2; + double c1, c2; c1 = adfGeoTransform[1]; c2 = adfGeoTransform[5]; if(abs(c1) == abs(c2)) @@ -1666,8 +1670,68 @@ void mainWindow::openOutputPath() int mainWindow::solve() { + + std::string outputDir = tree->solve->outputDirectory().toStdString(); + if( outputDir == "" ) + { + // This should never happen, so if it does, fix it. + QMessageBox::critical(this,tr("Failure."), + "no output directory specified in solve page", + QMessageBox::Ok | QMessageBox::Default); + disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + progressDialog->setValue( 0 ); + progressDialog->cancel(); + progressDialog->hide(); + disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + setCursor(Qt::ArrowCursor); //Restart everything + return false; + } + #ifdef WIN32 + std::replace(outputDir.begin(),outputDir.end(), '/', '\\'); + #endif + + + bool writeCF = tree->solve->CaseFileBox->isChecked(); + + CaseFile casefile; + + std::string zipFile = CPLFormFilename(outputDir.c_str(), "tmp_ninja", "zip"); + + std::string mainCaseCfgFilename = "config.cfg"; + std::string mainCaseCfgFile = CPLFormFilename(outputDir.c_str(), mainCaseCfgFilename.c_str(), ""); + + std::ofstream mainCaseCfgFILE; + + if (writeCF) { + casefile.setCaseZipFile(zipFile); + casefile.openCaseZipFile(); + mainCaseCfgFILE.open(mainCaseCfgFile); + //if (!mainCaseCfgFILE.is_open()) // useful for debugging + if (!mainCaseCfgFILE) + { + QMessageBox::critical(this,tr("Failure."), + "Could not open the casefile " + QString(mainCaseCfgFile.c_str()) + " file for writing!", + QMessageBox::Ok | QMessageBox::Default); + disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + progressDialog->setValue( 0 ); + progressDialog->cancel(); + progressDialog->hide(); + disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + setCursor(Qt::ArrowCursor); //Restart everything + return false; + } + } + + + if (writeCF) { + mainCaseCfgFILE << "--output_path " << outputDir << "\n"; + } + #ifdef NINJAFOAM bool useNinjaFoam = tree->ninjafoam->ninjafoamGroupBox->isChecked(); + if (writeCF && useNinjaFoam) { + mainCaseCfgFILE << "--momentum_flag true\n"; + } #endif //disable the open output path button tree->solve->openOutputPathButton->setDisabled( true ); @@ -1675,21 +1739,54 @@ int mainWindow::solve() //dem file std::string demFile = inputFileName.toStdString(); + if (writeCF) { + if (demWidget != NULL && demWidget->get_wasDemFetched() == true) + { + mainCaseCfgFILE << "--fetch_elevation true" << "\n"; + mainCaseCfgFILE << "--north " << demWidget->get_northBound() << "\n"; + mainCaseCfgFILE << "--south " << demWidget->get_southBound() << "\n"; + mainCaseCfgFILE << "--west " << demWidget->get_westBound() << "\n"; + mainCaseCfgFILE << "--east " << demWidget->get_eastBound() << "\n"; + mainCaseCfgFILE << "--elevation_source " << demWidget->get_elevSource() << "\n"; + } else + { + mainCaseCfgFILE << "--fetch_elevation false" << "\n"; + } + mainCaseCfgFILE << "--elevation_file " << demFile << "\n"; + } + #ifdef NINJAFOAM std::string caseFile = existingCaseDir.toStdString(); + if (writeCF && useNinjaFoam) { + mainCaseCfgFILE << "--existing_case_directory" << caseFile << "\n"; + } #endif //vegetation/roughness int vegIndex = tree->surface->roughnessComboBox->currentIndex(); WindNinjaInputs::eVegetation vegetation; - if( inputFileType != LCP ) { - //get choice from combo - if(vegIndex == 0) - vegetation = WindNinjaInputs::grass; - else if( vegIndex == 1 ) - vegetation = WindNinjaInputs::brush; - else if( vegIndex == 2 ) - vegetation = WindNinjaInputs::trees; + if( inputFileType != LCP ) + { + //get choice from combo + if(vegIndex == 0) + { + vegetation = WindNinjaInputs::grass; + if (writeCF) { + mainCaseCfgFILE << "--vegetation grass\n"; + } + } else if( vegIndex == 1 ) + { + vegetation = WindNinjaInputs::brush; + if (writeCF) { + mainCaseCfgFILE << "--vegetation brush\n"; + } + } else if( vegIndex == 2 ) + { + vegetation = WindNinjaInputs::trees; + if (writeCF) { + mainCaseCfgFILE << "--vegetation trees\n"; + } + } } //mesh @@ -1699,19 +1796,47 @@ int mainWindow::solve() lengthUnits::eLengthUnits meshUnits; bool customMesh = false; if( meshIndex == 0 ) - meshChoice = Mesh::coarse; - else if( meshIndex == 1 ) - meshChoice = Mesh::medium; - else if( meshIndex == 2 ) - meshChoice = Mesh::fine; - else { - meshRes = tree->surface->meshResDoubleSpinBox->value(); - customMesh = true; + { + meshChoice = Mesh::coarse; + if (writeCF) { + mainCaseCfgFILE << "--mesh_choice coarse\n"; + } + } else if( meshIndex == 1 ) + { + meshChoice = Mesh::medium; + if (writeCF) { + mainCaseCfgFILE << "--mesh_choice medium\n"; + } + } else if( meshIndex == 2 ) + { + meshChoice = Mesh::fine; + if (writeCF) { + mainCaseCfgFILE << "--mesh_choice fine\n"; + } + } else + { + meshRes = tree->surface->meshResDoubleSpinBox->value(); + customMesh = true; + } + if( tree->surface->meshFeetRadioButton->isChecked() ) + { meshUnits = lengthUnits::feet; - else + if (writeCF) { + mainCaseCfgFILE << "--units_mesh_resolution ft\n"; + } + } else + { meshUnits = lengthUnits::meters; + if (writeCF) { + mainCaseCfgFILE << "--units_mesh_resolution m\n"; + } + } + + if (writeCF && customMesh) { + mainCaseCfgFILE << "--mesh_resolution " << std::to_string(static_cast(meshRes)) << "\n"; } + #ifdef NINJAFOAM WindNinjaInputs::eNinjafoamMeshChoice ninjafoamMeshChoice; if(useNinjaFoam){ @@ -1747,11 +1872,15 @@ int mainWindow::solve() progressDialog->cancel(); disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); setCursor(Qt::ArrowCursor); + casefile.closeCaseZipFile(); return false; } QVariant temp = tree->surface->timeZone->tzComboBox->itemData( tzIndex ); std::string timeZone = temp.toString().toStdString(); + if (writeCF) { + mainCaseCfgFILE << "--time_zone " << timeZone << "\n"; + } //diurnal bool useDiurnal = tree->diurnal->diurnalGroupBox->isChecked(); @@ -1759,70 +1888,179 @@ int mainWindow::solve() //stability bool useStability = tree->stability->stabilityGroupBox->isChecked(); + if (writeCF) { + if (useDiurnal == 0) + { + mainCaseCfgFILE << "--diurnal_winds false\n"; + } else + { + mainCaseCfgFILE << "--diurnal_winds true\n"; + } + if (useStability == 0) + { + mainCaseCfgFILE << "--non_neutral_stability false\n"; + } else + { + mainCaseCfgFILE << "--non_neutral_stability true\n"; + } + } + //initialization method WindNinjaInputs::eInitializationMethod initMethod; if( tree->wind->windGroupBox->isChecked() ) + { initMethod = WindNinjaInputs::domainAverageInitializationFlag; - else if( tree->point->pointGroupBox->isChecked() ) + if (writeCF) { + mainCaseCfgFILE << "--initialization_method domainAverageInitialization\n"; + } + } else if( tree->point->pointGroupBox->isChecked() ) + { initMethod = WindNinjaInputs::pointInitializationFlag; - else if( tree->weather->weatherGroupBox->isChecked() ) + if (writeCF) { + mainCaseCfgFILE << "--initialization_method pointInitialization\n"; + } + } else if( tree->weather->weatherGroupBox->isChecked() ) + { initMethod = WindNinjaInputs::wxModelInitializationFlag; + if (writeCF) { + mainCaseCfgFILE << "--initialization_method wxModelInitialization\n"; + } + } //input wind height double inHeight = tree->wind->metaWind->inputHeightDoubleSpinBox->value(); + if (writeCF) { + mainCaseCfgFILE << "--input_wind_height " << std::to_string(static_cast(inHeight)) << "\n"; + } lengthUnits::eLengthUnits inHeightUnits; if(tree->wind->metaWind->feetRadioButton->isChecked()) - inHeightUnits = lengthUnits::feet; - else - inHeightUnits = lengthUnits::meters; + { + inHeightUnits = lengthUnits::feet; + if (writeCF) { + mainCaseCfgFILE << "--units_input_wind_height ft\n"; + } + } else + { + inHeightUnits = lengthUnits::meters; + if (writeCF) { + mainCaseCfgFILE << "--units_input_wind_height m\n"; + } + } //speed units and air temp units velocityUnits::eVelocityUnits inputSpeedUnits; if(tree->wind->windTable->inputSpeedUnits->currentIndex() == 0) - inputSpeedUnits = velocityUnits::milesPerHour; - else if(tree->wind->windTable->inputSpeedUnits->currentIndex() == 1) - inputSpeedUnits = velocityUnits::metersPerSecond; - else if(tree->wind->windTable->inputSpeedUnits->currentIndex() == 3) - inputSpeedUnits = velocityUnits::knots; - else - inputSpeedUnits = velocityUnits::kilometersPerHour; + { + inputSpeedUnits = velocityUnits::milesPerHour; + if (writeCF) { + mainCaseCfgFILE << "--input_speed_units mph\n"; + } + } else if(tree->wind->windTable->inputSpeedUnits->currentIndex() == 1) + { + inputSpeedUnits = velocityUnits::metersPerSecond; + if (writeCF) { + mainCaseCfgFILE << "--input_speed_units mps\n"; + } + } else if(tree->wind->windTable->inputSpeedUnits->currentIndex() == 3) + { + inputSpeedUnits = velocityUnits::knots; + if (writeCF) { + mainCaseCfgFILE << "--input_speed_units kts\n"; + } + } else + { + inputSpeedUnits = velocityUnits::kilometersPerHour; + if (writeCF) { + mainCaseCfgFILE << "--input_speed_units kph\n"; + } + } temperatureUnits::eTempUnits tempUnits; if(tree->wind->windTable->airTempUnits->currentIndex() == 0) - tempUnits = temperatureUnits::F; - else if(tree->wind->windTable->airTempUnits->currentIndex() == 1) - tempUnits = temperatureUnits::C; + { + tempUnits = temperatureUnits::F; + if (writeCF) { + mainCaseCfgFILE << "--air_temp_units F\n"; + } + } else if(tree->wind->windTable->airTempUnits->currentIndex() == 1) + { + tempUnits = temperatureUnits::C; + if (writeCF) { + mainCaseCfgFILE << "--air_temp_units C\n"; + } + } //model init std::string weatherFile; QModelIndex mi = tree->weather->treeView->selectionModel()->currentIndex(); - if( mi.isValid() ) { - QFileInfo fi( tree->weather->model->fileInfo( mi ) ); - weatherFile = fi.absoluteFilePath().toStdString(); + if( mi.isValid() ) + { + QFileInfo fi( tree->weather->model->fileInfo( mi ) ); + weatherFile = fi.absoluteFilePath().toStdString(); + if (writeCF) { + mainCaseCfgFILE << "--wx_model_type "; + mainCaseCfgFILE << "--forecast_duration " << tree->weather->hourSpinBox->value() << "\n"; + } + } else + { + weatherFile = ""; } - else - weatherFile = ""; //output height double outHeight = tree->output->outputHeight->outputHeightDoubleSpinBox->value(); + if (writeCF) { + mainCaseCfgFILE << "--output_wind_height " << std::to_string(static_cast(outHeight)) << "\n"; + } lengthUnits::eLengthUnits outHeightUnits; if(tree->output->outputHeight->feetRadioButton->isChecked()) - outHeightUnits = lengthUnits::feet; - else - outHeightUnits = lengthUnits::meters; + { + outHeightUnits = lengthUnits::feet; + if (writeCF) { + mainCaseCfgFILE << "--units_output_wind_height ft\n"; + } + } else + { + outHeightUnits = lengthUnits::meters; + if (writeCF) { + mainCaseCfgFILE << "--units_output_wind_height m\n"; + } + } velocityUnits::eVelocityUnits outputSpeedUnits; if(tree->output->outputSpeedUnitsCombo->currentIndex() == 0) + { outputSpeedUnits = velocityUnits::milesPerHour; - else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 1) + if (writeCF) { + mainCaseCfgFILE << "--output_speed_units mph\n"; + } + } else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 1) + { outputSpeedUnits = velocityUnits::metersPerSecond; - else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 2) + if (writeCF) { + mainCaseCfgFILE << "--output_speed_units mps\n"; + } + } else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 2) + { outputSpeedUnits = velocityUnits::kilometersPerHour; - else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 3) + if (writeCF) { + mainCaseCfgFILE << "--output_speed_units kph\n"; + } + } else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 3) + { outputSpeedUnits = velocityUnits::knots; + if (writeCF) { + mainCaseCfgFILE << "--output_speed_units kts\n"; + } + } //clip buffer? int clip = tree->output->bufferSpinBox->value(); + if (writeCF) { + //if (clip > 0) + //{ + mainCaseCfgFILE << "--output_buffer_clipping " << std::to_string(static_cast(clip)) << "\n"; + //} + } bool writeWxOutput; if( tree->output->wxModelOutputCheckBox->isEnabled() ) @@ -1831,22 +2069,53 @@ int mainWindow::solve() //google bool writeGoogle = tree->google->googleGroupBox->isChecked(); double googleRes = tree->google->googleResSpinBox->value(); + if (writeCF) { + if (writeGoogle) + { + mainCaseCfgFILE << "--write_goog_output true\n"; + if( initMethod == WindNinjaInputs::wxModelInitializationFlag) + { + mainCaseCfgFILE << "--write_wx_model_goog_output true\n"; + } + mainCaseCfgFILE << "--goog_out_resolution " << std::to_string(static_cast(googleRes)) << "\n"; + } + } double vectorWidth = tree->google->vectorWidthDoubleSpinBox->value(); lengthUnits::eLengthUnits googleUnits; KmlVector::egoogSpeedScaling googleScale; //bool writeLegend = tree->google->legendGroupBox->isChecked(); if(tree->google->googleMetersRadioButton->isChecked()) - googleUnits = lengthUnits::meters; - else - googleUnits = lengthUnits::feet; - + { + googleUnits = lengthUnits::meters; + if (writeCF) { + mainCaseCfgFILE << "--units_goog_out_resolution m\n"; + } + } else + { + googleUnits = lengthUnits::feet; + if (writeCF) { + mainCaseCfgFILE << "--units_goog_out_resolution ft\n"; + } + } if(tree->google->uniformRangeRadioButton->isChecked()) - googleScale = KmlVector::equal_interval; - else - googleScale = KmlVector::equal_color; + { + googleScale = KmlVector::equal_interval; + } else + { + googleScale = KmlVector::equal_color; + } std::string googleScheme; bool googVectorScaling = tree->google->applyVectorScaling->isChecked(); + if (writeCF) { + if (googVectorScaling) + { + mainCaseCfgFILE << "--goog_out_vector_scaling true\n"; + } else + { + mainCaseCfgFILE << "--goog_out_vector_scaling false\n"; + } + } if(tree->google->colorblindBox->isChecked()) { std::string googCheckScheme; @@ -1856,50 +2125,104 @@ int mainWindow::solve() if (googCheckScheme=="Default") { googleScheme="default"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme ROYGB\n"; + } } if (googCheckScheme=="ROPGW (Red Orange Pink Green White)") { googleScheme="ROPGW"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme ROPGW\n"; + } } if (googCheckScheme=="Oranges") { googleScheme="oranges"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme oranges\n"; + } } if (googCheckScheme=="Blues") { googleScheme="blues"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme blues\n"; + } } if (googCheckScheme=="Pinks") { googleScheme="pinks"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme pinks\n"; + } } if (googCheckScheme=="Greens") { googleScheme="greens"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme greens\n"; + } } if (googCheckScheme=="Magic Beans") { googleScheme="magic_beans"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme magic_beans\n"; + } } if (googCheckScheme=="Pink to Green") { googleScheme="pink_to_green"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme pink_to_green\n"; + } } - } - else + } else { googleScheme="default"; + if (writeCF) { + mainCaseCfgFILE << "--goog_out_color_scheme ROYGB\n"; + } } + //ascii raster fb files bool writeFb = tree->fb->fbGroupBox->isChecked(); double fbRes = tree->fb->fbResSpinBox->value(); + if (writeCF) { + if (writeFb) + { + mainCaseCfgFILE << "--write_ascii_output true\n"; + if (initMethod == WindNinjaInputs::wxModelInitializationFlag ) + { + mainCaseCfgFILE << "--write_wx_model_ascii_output true\n"; + } + mainCaseCfgFILE << "--ascii_out_resolution " << std::to_string(static_cast(fbRes)) << "\n"; + } + } lengthUnits::eLengthUnits fbUnits; if(tree->fb->fbMetersRadioButton->isChecked()) - fbUnits = lengthUnits::meters; - else - fbUnits = lengthUnits::feet; + { + fbUnits = lengthUnits::meters; + if (writeCF) { + mainCaseCfgFILE << "--units_ascii_out_resolution m\n"; + } + } else + { + fbUnits = lengthUnits::feet; + if (writeCF) { + mainCaseCfgFILE << "--units_ascii_out_resolution ft\n"; + } + } + //write atmosphere file? bool writeAtm = tree->fb->atmFileCheckBox->isChecked(); + if (writeCF) { + if (writeAtm) + { + mainCaseCfgFILE << "--write_farsite_atm true\n"; + } + } if(writeAtm && writeFb) { if((outHeight == 20 && outHeightUnits == lengthUnits::feet && @@ -1920,29 +2243,77 @@ int mainWindow::solve() progressDialog->cancel(); disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); setCursor(Qt::ArrowCursor); + casefile.closeCaseZipFile(); return false; } } //shape bool writeShape = tree->shape->shapeGroupBox->isChecked(); + // do for wx double shapeRes = tree->shape->shapeResSpinBox->value(); + if (writeCF) { + if (writeShape) + { + mainCaseCfgFILE << "--write_shapefile_output true\n"; + if( initMethod == WindNinjaInputs::wxModelInitializationFlag ) + { + mainCaseCfgFILE << "--write_wx_model_shapefile_output true\n"; + } + mainCaseCfgFILE << "--shape_out_resolution " << std::to_string(static_cast(shapeRes)) << "\n"; + } + } lengthUnits::eLengthUnits shapeUnits; if(tree->shape->shapeMetersRadioButton->isChecked()) - shapeUnits = lengthUnits::meters; - else - shapeUnits = lengthUnits::feet; + { + shapeUnits = lengthUnits::meters; + if (writeCF) { + mainCaseCfgFILE << "--units_shape_out_resolution m\n"; + } + } else + { + shapeUnits = lengthUnits::feet; + if (writeCF) { + mainCaseCfgFILE << "--units_shape_out_resolution ft\n"; + } + } + //pdf bool writePdf = tree->pdf->pdfGroupBox->isChecked(); double pdfRes = tree->pdf->pdfResSpinBox->value(); double pdfLineWidth = tree->pdf->vectorWidthDoubleSpinBox->value(); + if (writeCF) { + if (writePdf) + { + mainCaseCfgFILE << "--write_pdf_output true\n"; + mainCaseCfgFILE << "--pdf_out_resolution "<< std::to_string(static_cast(pdfRes)) << "\n"; + mainCaseCfgFILE << "--pdf_linewidth "<< std::to_string(static_cast(pdfLineWidth)) << "\n"; + } + } lengthUnits::eLengthUnits pdfUnits; if(tree->pdf->pdfMetersRadioButton->isChecked()) + { pdfUnits = lengthUnits::meters; - else + if (writeCF) { + mainCaseCfgFILE << "--units_pdf_out_resolution m\n"; + } + } else + { pdfUnits = lengthUnits::feet; + if (writeCF) { + mainCaseCfgFILE << "--units_pdf_out_resolution ft\n"; + } + } int pdfBase = tree->pdf->backgroundComboBox->currentIndex(); - + if (writeCF) { + if (pdfBase == 0) + { + mainCaseCfgFILE << "--pdf_basemap topofire\n"; + } else + { + mainCaseCfgFILE << "--pdf_basemap hillshade\n"; + } + } double pdfHeight, pdfWidth; int pdfSize = tree->pdf->sizeComboBox->currentIndex(); // Letter @@ -1950,18 +2321,27 @@ int mainWindow::solve() { pdfHeight = 11.0; pdfWidth = 8.5; + if (writeCF) { + mainCaseCfgFILE << "--pdf_size letter\n"; + } } // Legal else if( pdfSize == 1 ) { pdfHeight = 14.0; pdfWidth = 8.5; + if (writeCF) { + mainCaseCfgFILE << "--pdf_size legal\n"; + } } // Tabloid else if( pdfSize == 2 ) { pdfHeight = 17.0; pdfWidth = 11.0; + if (writeCF) { + mainCaseCfgFILE << "--pdf_size tabloid\n"; + } } if( tree->pdf->landscapeRadioButton->isChecked() ) { @@ -1972,9 +2352,18 @@ int mainWindow::solve() } bool writeVTK = tree->vtk->vtkGroupBox->isChecked(); + if (writeCF) { + if (writeVTK) + { + mainCaseCfgFILE << "--write_vtk_output true\n"; + } + } //number of processors int nThreads = tree->solve->numProcSpinBox->value(); + if (writeCF) { + mainCaseCfgFILE << "--num_threads " << std::to_string(static_cast(nThreads)) << "\n"; + } army = new ninjaArmy(); @@ -1991,6 +2380,119 @@ int mainWindow::solve() bool writeStationKML = tree->point->writeStationKmlButton->isChecked(); //Write a kml file bool writeStationCSV = tree->point->writeStationFileButton->isChecked(); //hidden for now + if (writeCF) + { + if (tree->point->xWidget != NULL && tree->point->xWidget->get_wasStationFetched() == true) + { + mainCaseCfgFILE << "--fetch_station true\n"; + mainCaseCfgFILE << "--fetch_type " << tree->point->xWidget->getType() << "\n"; + if (tree->point->xWidget->getType() == "bbox") + { + mainCaseCfgFILE << "--station_buffer " << tree->point->xWidget->getBuffer() << "\n"; + mainCaseCfgFILE << "--station_buffer_units " << tree->point->xWidget->getBufferUnits() << "\n"; + } + if (tree->point->xWidget->getType() == "stid") + { + mainCaseCfgFILE << "--fetch_station_name " << tree->point->xWidget->getStationIDS() << "\n"; + } + if (tree->point->xWidget->get_isTimeSeries() == false) + { + mainCaseCfgFILE << "--fetch_current_station_data true\n"; + } else + { + mainCaseCfgFILE << "--fetch_current_station_data false\n"; + mainCaseCfgFILE << "--number_time_steps " << std::to_string(static_cast(numTimeSteps)) << "\n"; + } + } else // if (tree->point->xWidget != NULL && tree->point->xWidget->get_wasStationFetched() == true) + { + mainCaseCfgFILE << "--fetch_station false\n"; + if (useTimeList) + { + mainCaseCfgFILE << "--fetch_current_station_data false\n"; + mainCaseCfgFILE << "--start_year " << xStartTime[0] << "\n"; + mainCaseCfgFILE << "--start_month " << xStartTime[1] << "\n"; + mainCaseCfgFILE << "--start_day " << xStartTime[2] << "\n"; + mainCaseCfgFILE << "--start_hour " << xStartTime[3] << "\n"; + mainCaseCfgFILE << "--start_minute " << xStartTime[4] << "\n"; + mainCaseCfgFILE << "--stop_year " << xEndTime[0] << "\n"; + mainCaseCfgFILE << "--stop_month " << xEndTime[1] << "\n"; + mainCaseCfgFILE << "--stop_day " << xEndTime[2] << "\n"; + mainCaseCfgFILE << "--stop_hour " << xEndTime[3] << "\n"; + mainCaseCfgFILE << "--stop_minute " << xEndTime[4] << "\n"; + mainCaseCfgFILE << "--number_time_steps " << std::to_string(static_cast(numTimeSteps)) << "\n"; + } else + { + mainCaseCfgFILE << "--fetch_current_station_data true\n"; + } + } // if (tree->point->xWidget != NULL && tree->point->xWidget->get_wasStationFetched() == true) + + if (writeStationKML) + { + mainCaseCfgFILE << "--write_wx_station_kml true\n"; + } else + { + mainCaseCfgFILE << "--write_wx_station_kml false\n"; + } + if (writeStationCSV) + { + mainCaseCfgFILE << "--write_wx_station_csv true\n"; + } else + { + mainCaseCfgFILE << "--write_wx_station_csv false\n"; + } + + // for each file append it to csv + // if any point file is under a wx station folder just copy the entire folder over - otherwise no + std::vector fullFileListPoint = tree->point->fullFileList; + for( size_t pIdx = 0; pIdx < fullFileListPoint.size(); pIdx++ ) + { + std::string pointFile = fullFileListPoint[pIdx]; + std::string pointFilename = CPLGetFilename( pointFile.c_str() ); + std::string pointPath = CPLGetPath( pointFile.c_str() ); + if (pointPath.find("WXSTATIONS-") != std::string::npos) + { + pointFilename = CPLFormFilename(CPLGetFilename(pointPath.c_str()), pointFilename.c_str(), ""); + } + std::string pointZipEntry = CPLFormFilename("PointInitialization", pointFilename.c_str(), ""); + casefile.addFileToZip(pointZipEntry, pointFile); + } + // setup list of actually used/selected stations + std::string selectedStationsFilename = "selected_stations.csv"; + std::string selectedStationsFile = CPLFormFilename(outputDir.c_str(), selectedStationsFilename.c_str(), ""); + std::ofstream selectedStationsFILE(selectedStationsFile); + if (!selectedStationsFILE) + { + QMessageBox::critical(this,tr("Failure."), + "Could not open the casefile " + QString(selectedStationsFile.c_str()) + " file for writing!", + QMessageBox::Ok | QMessageBox::Default); + disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + progressDialog->setValue( 0 ); + progressDialog->cancel(); + progressDialog->hide(); + disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + setCursor(Qt::ArrowCursor); //Restart everything + delete army; + casefile.closeCaseZipFile(); + return false; + } + for( size_t pIdx = 0; pIdx < pointFileList.size(); pIdx++ ) + { + std::string pointFile = pointFileList[pIdx]; + std::string pointFilename = CPLGetFilename( pointFile.c_str() ); + std::string pointPath = CPLGetPath( pointFile.c_str() ); + if (pointPath.find("WXSTATIONS-") != std::string::npos) + { + pointFilename = CPLFormFilename(CPLGetFilename(pointPath.c_str()), pointFilename.c_str(), ""); + } + selectedStationsFILE << pointFilename << "\n"; + } + selectedStationsFILE.close(); + std::string selectedStationsZipEntry = CPLFormFilename("PointInitialization", selectedStationsFilename.c_str(), ""); + casefile.addFileToZip(selectedStationsZipEntry, selectedStationsFile); + VSIUnlink( selectedStationsFile.c_str() ); + + } // if (writeCF) + /* * Note that pointFormat is not the same as stationFormat! * @@ -2036,6 +2538,9 @@ int mainWindow::solve() timeList.push_back(noTime); } try{ //Try to run windninja + if (writeCF) { + mainCaseCfgFILE << "--match_points true\n"; + } army->makePointArmy(timeList,timeZone, pointFile, demFile, true,false); }catch (exception& e) { @@ -2051,9 +2556,10 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; - }catch(...){ //catch all exceptions and tell the user, prevent segfaults - + }catch(...) + { //catch all exceptions and tell the user, prevent segfaults QMessageBox::critical(this,tr("Failure."), "An error occured in makePointArmy() - OldFormat! This is " "usually due to a failure in reading a " @@ -2066,6 +2572,7 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; } nRuns = army->getSize(); @@ -2106,6 +2613,9 @@ int mainWindow::solve() CPLDebug("STATION_FETCH","FILES STORED..."); try{ //try running with timelist + if (writeCF) { + mainCaseCfgFILE << "--match_points true\n"; + } army->makePointArmy(timeList,timeZone,pointFileList[0],demFile,true,false); //setting pointFileList[0] is just for header checks etc }catch (exception& e) { @@ -2121,9 +2631,10 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; - }catch(...){ //catch any and all exceptions and tell the user - + }catch(...) + { //catch any and all exceptions and tell the user QMessageBox::critical(this,tr("Failure."), "An error occured in makePointArmy() - timeSeries! This is " "usually due to a failure in reading a " @@ -2136,6 +2647,7 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; } nRuns = army->getSize(); @@ -2153,6 +2665,9 @@ int mainWindow::solve() timeList.push_back(singleTime); pointInitialization::storeFileNames(pointFileList); try{ //try making the army with current data + if (writeCF) { + mainCaseCfgFILE << "--match_points true\n"; + } army->makePointArmy(timeList,timeZone,pointFileList[0],demFile,true,false); }catch (exception& e) { @@ -2168,9 +2683,10 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; - }catch(...){ //catch any and all exceptions and tell the user - + }catch(...) + { //catch any and all exceptions and tell the user QMessageBox::critical(this,tr("Failure."), "An error occured in makePointArmy() - currentwxdata! This is " "usually due to a failure in reading a " @@ -2183,6 +2699,7 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; } nRuns = army->getSize(); @@ -2205,6 +2722,7 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; } @@ -2242,6 +2760,10 @@ int mainWindow::solve() std::string baseMeta = baseDem+"-metadata"; std::string metaPath = std::string(CPLFormFilename(pathDem.c_str(),baseMeta.c_str(),".csv")); CPLDebug("STATION_FETCH","Saving Metadata to: %s",metaPath.c_str()); + if (writeCF) { + mainCaseCfgFILE << "--fetch_metadata true\n"; + mainCaseCfgFILE << "--metadata_filename" << metaPath << "\n"; + } pointInitialization::fetchMetaData(metaPath,demFile,true); } } @@ -2271,11 +2793,50 @@ int mainWindow::solve() setCursor(Qt::ArrowCursor); tree->weather->checkForModelData(); progressDialog->cancel(); + casefile.closeCaseZipFile(); return false; } std::vector times = tree->weather->timeList(); /* This can throw a badForecastFile */ + + if (writeCF && times.size() > 0) + { + std::vector stringTimes; + //blt::time_zone_ptr zone = globalTimeZoneDB.time_zone_from_region(timeZone.c_str()); + blt::time_zone_ptr utc = globalTimeZoneDB.time_zone_from_region("UTC"); + + for( size_t tIdx = 0; tIdx < times.size(); tIdx++ ) + { + blt::local_date_time time = times[tIdx]; + + // Convert local_date_time to ptime using the UTC time zone + bpt::ptime pt = time.local_time_in(utc).local_time(); + // Convert local_date_time to ptime using the data time zone + //bpt::ptime pt = time.local_time_in(zone).local_time(); + + // Convert ptime to ISO 8601 string + std::string isoString = bpt::to_iso_string(pt); + stringTimes.push_back(isoString); + } + + std::string outstr; + for (size_t i = 0; i < stringTimes.size(); i++) + { + outstr += stringTimes[i]; + if (i != stringTimes.size() - 1) + { + outstr += "||"; + } + } + + mainCaseCfgFILE << "--forecast_times " << outstr << "\n"; + mainCaseCfgFILE << "--forecast_filename " << weatherFile << "\n"; + std::string weatherFilename = CPLGetFilename( weatherFile.c_str() ); + std::string weatherZipEntry = CPLFormFilename("WxModelInitialization", weatherFilename.c_str(), ""); + casefile.addFileToZip(weatherZipEntry, weatherFile); + } // if (writeCF && times.size() > 0) + try { #ifdef NINJAFOAM @@ -2295,6 +2856,7 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; } catch (...) { QMessageBox::critical( @@ -2309,37 +2871,88 @@ int mainWindow::solve() progressDialog->cancel(); progressDialog->hide(); delete army; + casefile.closeCaseZipFile(); return false; } + + //get times for casefile if no times are clicked by user + if (writeCF && times.size() == 0) + { + // this occurs if the user purposefully unselects all times to select no input time, while tree->weather->timeList() returns a list of 0 times, + // ninjaArmy replaces the empty times list with ALL the times in the forecast, the code runs ALL the weather forecast file times. + std::vector fullTimesList = tree->weather->getFullTimeList(); + + std::vector stringTimes; + //blt::time_zone_ptr zone = globalTimeZoneDB.time_zone_from_region(timeZone.c_str()); + blt::time_zone_ptr utc = globalTimeZoneDB.time_zone_from_region("UTC"); + + for( size_t tIdx = 0; tIdx < fullTimesList.size(); tIdx++ ) + { + blt::local_date_time time = fullTimesList[tIdx]; + + // Convert local_date_time to ptime using the UTC time zone + bpt::ptime pt = time.local_time_in(utc).local_time(); + // Convert local_date_time to ptime using the data time zone + //bpt::ptime pt = time.local_time_in(zone).local_time(); + + // Convert ptime to ISO 8601 string + std::string isoString = bpt::to_iso_string(pt); + stringTimes.push_back(isoString); + } + + std::string outstr; + for (size_t i = 0; i < stringTimes.size(); i++) + { + outstr += stringTimes[i]; + if (i != stringTimes.size() - 1) + { + outstr += "||"; + } + } + + mainCaseCfgFILE << "--forecast_times " << outstr << "\n"; + mainCaseCfgFILE << "--forecast_filename " << weatherFile << "\n"; + std::string weatherFilename = CPLGetFilename( weatherFile.c_str() ); + std::string weatherZipEntry = CPLFormFilename("WxModelInitialization", weatherFilename.c_str(), ""); + casefile.addFileToZip(weatherZipEntry, weatherFile); + } // if (writeCF && times.size() == 0) + nRuns = army->getSize(); - } + } // if( initMethod == WindNinjaInputs::wxModelInitializationFlag ) progressDialog->setValue( 0 ); //set progress dialog and initial value progressDialog->setRange(0, nRuns * 100); //Expand the dialog to the number of runs runProgress = new int[nRuns]; //I don't think this is needed anymore - std::string outputDir = tree->solve->outputDirectory().toStdString(); - if( outputDir == "" ) { - // This should never happen, so if it does, fix it. - progressDialog->cancel(); - progressDialog->hide(); - QMessageBox::critical( - this, tr("Failure."), - tr("no output directory specified in solve page"), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - tree->weather->checkForModelData(); - delete army; - return false; - } - //fill in the values - for(int i = 0;i < army->getSize(); i++) + for(int i = 0;i < army->getSize(); i++) { + army->setCaseFilePtr( i, casefile ); + + // add runs to files + std::string domainRunCfgFilename = "run" + std::to_string(static_cast(i)) + "-DomainAverage.cfg"; + std::string domainRunCfgFile = CPLFormFilename(outputDir.c_str(), domainRunCfgFilename.c_str(), ""); + std::ofstream domainRunFILE; + if (writeCF) + { + if( initMethod == WindNinjaInputs::domainAverageInitializationFlag ) + { + domainRunFILE.open(domainRunCfgFile); + domainRunFILE << "--input_speed " << tree->wind->windTable->speed[i]->value() << "\n"; + domainRunFILE << "--input_direction " << tree->wind->windTable->dir[i]->value() << "\n"; + domainRunFILE << "--year " << tree->wind->windTable->date[i]->date().year() << "\n"; + domainRunFILE << "--month " << tree->wind->windTable->date[i]->date().month() << "\n"; + domainRunFILE << "--day " << tree->wind->windTable->date[i]->date().day() << "\n"; + domainRunFILE << "--hour " << tree->wind->windTable->time[i]->time().hour() << "\n"; + domainRunFILE << "--minute " << tree->wind->windTable->time[i]->time().minute() << "\n"; + domainRunFILE << "--uni_air_temp " << tree->wind->windTable->airTemp[i]->value() << "\n"; + domainRunFILE << "--uni_cloud_cover " << tree->wind->windTable->cloudCover[i]->value() << "\n"; + domainRunFILE << "--cloud_cover_units percent" << "\n"; + } + } + army->setDEM( i, demFile ); #ifdef NINJAFOAM if(caseFile != ""){ @@ -2368,10 +2981,16 @@ int mainWindow::solve() else if( initMethod == WindNinjaInputs::domainAverageInitializationFlag ) { //get speed + if (writeCF) { + mainCaseCfgFILE << "--input_speed " << std::to_string(static_cast(tree->wind->windTable->speed[i]->value())) << "\n"; + } army->setInputSpeed( i, tree->wind->windTable->speed[i]->value(), inputSpeedUnits); //get direction + if (writeCF) { + mainCaseCfgFILE << "--input_direction " << std::to_string(static_cast(tree->wind->windTable->dir[i]->value())) << "\n"; + } army->setInputDirection( i, tree->wind->windTable->dir[i]->value() ); army->setInputWindHeight ( i, inHeight, inHeightUnits ); @@ -2390,7 +3009,7 @@ int mainWindow::solve() //diurnal, if needed army->setDiurnalWinds( i, useDiurnal ); - if( useDiurnal == true ) + if( useDiurnal == true ) { if( initMethod == WindNinjaInputs::domainAverageInitializationFlag ) { @@ -2455,9 +3074,26 @@ int mainWindow::solve() else { #ifdef NINJAFOAM - if(useNinjaFoam){ + if(useNinjaFoam) + { army->setMeshCount( i, ninjafoamMeshChoice ); + if (writeCF) + { + if(ninjafoamMeshChoice == WindNinjaInputs::coarse) + { + mainCaseCfgFILE << "--mesh_count 25000\n"; + } else if(meshChoice == WindNinjaInputs::medium) + { + mainCaseCfgFILE << "--mesh_count 50000\n"; + } else if(meshChoice == WindNinjaInputs::fine) + { + mainCaseCfgFILE << "--mesh_count 100000\n"; + } + } army->setNumberOfIterations( i, 300); + if (writeCF) { + mainCaseCfgFILE << "--number_of_iterations 300\n"; + } } else army->setMeshResolutionChoice( i, meshChoice ); @@ -2504,6 +3140,29 @@ int mainWindow::solve() //army.setOutputFilenames(); army->setNinjaComNumRuns( i, nRuns ); + + //add different input to config + if (writeCF) + { + if (initMethod == WindNinjaInputs::domainAverageInitializationFlag) + { + domainRunFILE.close(); + std::string domainRunZipEntry = CPLFormFilename("DomainAverageInitialization", domainRunCfgFilename.c_str(), ""); + casefile.addFileToZip(domainRunZipEntry, domainRunCfgFile ); + VSIUnlink( domainRunCfgFile.c_str() ); + } + } + + } // for(int i = 0;i < army->getSize(); i++) + + //set Casefile Directory and Input + if (writeCF) + { + mainCaseCfgFILE.close(); + std::string demFilename = CPLGetFilename( demFile.c_str() ); + casefile.addFileToZip(demFilename, demFile); + casefile.addFileToZip(mainCaseCfgFilename, mainCaseCfgFile); + VSIUnlink( mainCaseCfgFile.c_str() ); } army->set_writeFarsiteAtmFile( writeAtm && writeFb ); @@ -2558,6 +3217,8 @@ int mainWindow::solve() //start the army try { ninjaSuccess = army->startRuns( nThreads ); + casefile.closeCaseZipFile(); + casefile.renameCaseZipFile(); } catch (bad_alloc& e) { @@ -2569,6 +3230,7 @@ int mainWindow::solve() disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); setCursor(Qt::ArrowCursor); delete army; + casefile.closeCaseZipFile(); return false; } catch (cancelledByUser& e) @@ -2580,6 +3242,7 @@ int mainWindow::solve() disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); setCursor(Qt::ArrowCursor); delete army; + casefile.closeCaseZipFile(); return false; } catch (exception& e) @@ -2591,6 +3254,7 @@ int mainWindow::solve() disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); setCursor(Qt::ArrowCursor); delete army; + casefile.closeCaseZipFile(); return false; } catch (...) @@ -2602,6 +3266,7 @@ int mainWindow::solve() disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); setCursor(Qt::ArrowCursor); delete army; + casefile.closeCaseZipFile(); return false; } @@ -3191,13 +3856,13 @@ int mainWindow::checkGoogleItem() { if(checkSurfaceItem() != red) { - if((int)noGoogleCellSize > tree->google->googleResSpinBox->value()) + if(noGoogleCellSize > tree->google->googleResSpinBox->value()) { tree->googleItem->setIcon(0, tree->caution); tree->googleItem->setToolTip(0, "The resolution of the google file may be too fine."); status = amber; } - else if((int)GDALCellSize > tree->google->googleResSpinBox->value()) + else if(GDALCellSize > tree->google->googleResSpinBox->value()) { tree->googleItem->setIcon(0, tree->caution); tree->googleItem->setToolTip(0, "The output resolution is finer than the DEM resolution"); @@ -3241,7 +3906,7 @@ int mainWindow::checkFbItem() { if(checkSurfaceItem() == green || checkSurfaceItem() == amber) { - if((int)GDALCellSize > tree->fb->fbResSpinBox->value()) + if(GDALCellSize > tree->fb->fbResSpinBox->value()) { tree->fbItem->setIcon(0, tree->caution); tree->fbItem->setToolTip(0, "The output resolutions is finer than the DEM resolution"); @@ -3286,7 +3951,7 @@ int mainWindow::checkShapeItem() status = amber; } */ - if((int)GDALCellSize > tree->shape->shapeResSpinBox->value()) + if(GDALCellSize > tree->shape->shapeResSpinBox->value()) { tree->shapeItem->setIcon(0, tree->caution); tree->shapeItem->setToolTip(0, "The output resolutions is finer than the DEM resolution"); @@ -3302,7 +3967,7 @@ int mainWindow::checkShapeItem() else { tree->shapeItem->setIcon(0, tree->cross); - tree->shapeItem->setToolTip(0, "Cannot read input file") ; + tree->shapeItem->setToolTip(0, "Cannot read input file"); status = red; } } @@ -3330,7 +3995,7 @@ int mainWindow::checkPdfItem() status = amber; } */ - if((int)GDALCellSize > tree->pdf->pdfResSpinBox->value()) + if(GDALCellSize > tree->pdf->pdfResSpinBox->value()) { tree->pdfItem->setIcon(0, tree->caution); tree->pdfItem->setToolTip(0, "The output resolutions is finer than the DEM resolution"); @@ -3346,7 +4011,7 @@ int mainWindow::checkPdfItem() else { tree->pdfItem->setIcon(0, tree->cross); - tree->pdfItem->setToolTip(0, "Cannot read input file") ; + tree->pdfItem->setToolTip(0, "Cannot read input file"); status = red; } } @@ -3373,7 +4038,7 @@ int mainWindow::checkVtkItem() else { tree->vtkItem->setIcon(0, tree->cross); - tree->vtkItem->setToolTip(0, "Cannot read input file") ; + tree->vtkItem->setToolTip(0, "Cannot read input file"); status = red; } } diff --git a/src/gui/pointInput.cpp b/src/gui/pointInput.cpp index 4e744ea9..f88ce97e 100644 --- a/src/gui/pointInput.cpp +++ b/src/gui/pointInput.cpp @@ -37,6 +37,7 @@ static int wxStationFormat; pointInput::pointInput( QWidget *parent ) : QWidget( parent ) { + xWidget = NULL; pointGroupBox = new QGroupBox( "Point Initialization", this ); pointGroupBox->setCheckable( true ); pointGroupBox->setChecked(false); @@ -271,6 +272,44 @@ pointInput::~pointInput() } +void pointInput::collectAllIndexes(const QModelIndex &parent, std::vector &allIndexes) const +{ + for (int row = 0; row < sfModel->rowCount(parent); row++) + { + QModelIndex index = sfModel->index(row, 0, parent); + allIndexes.push_back(index); + if (sfModel->isDir(index)) + { + collectAllIndexes(index, allIndexes); // Recursively collect child indexes + } + } +} + +void pointInput::generateFullFileList() +{ + std::vector allIndexes; + std::vector fileList; + + // Collect all indexes starting from the root index + QModelIndex rootIndex = treeView->rootIndex(); + + collectAllIndexes(rootIndex, allIndexes); + + for( size_t qIdx = 0; qIdx < allIndexes.size(); qIdx++ ) + { + const QModelIndex &index = allIndexes[qIdx]; + + // Check if it is a file + if (!sfModel->isDir(index)) + { + QString filePath = sfModel->filePath(index); + fileList.push_back(filePath.toStdString()); + } + } + + fullFileList = fileList; +} + /** * @brief pointInput::readStationFiles * Reads the files on disk that the user selects @@ -302,6 +341,8 @@ void pointInput::readStationFiles(const QItemSelection &x ,const QItemSelection CPLDebug("STATION_FETCH","========================================"); CPLDebug("STATION_FETCH","NUMBER OF SELECTED STATIONS: %i",idx0.count()); + generateFullFileList(); + for(int i=0;ifileInfo(idx0[i]).isDir()==true) //If its a directory, make it so that it can't be selected @@ -997,7 +1038,6 @@ void pointInput::setDiurnalParam(bool diurnalCheck) CPLDebug("STATION_FETCH","DIURNAL/STABILITY STATUS: %i",isDiurnalChecked); } - /** * *@brief pointInput::checkForModelData * Applies filters to the tree diff --git a/src/gui/pointInput.h b/src/gui/pointInput.h index b920809e..760f2037 100644 --- a/src/gui/pointInput.h +++ b/src/gui/pointInput.h @@ -78,6 +78,7 @@ class pointInput : public QWidget QString demFileName; QString stationFileName; std::vector stationFileList; + std::vector fullFileList; std::vector stationFileTypes; int simType; bool pointGo; @@ -159,6 +160,10 @@ class pointInput : public QWidget void setOneStepTimeseries(); private slots: + + void generateFullFileList(); + void collectAllIndexes(const QModelIndex &parent, std::vector &allIndexes) const; + void readStationFiles(const QItemSelection &x ,const QItemSelection &y); void selChanged(const QItemSelection &x ,const QItemSelection &y); //Test Function void setInputFile( QString file ); diff --git a/src/gui/solvePage.cpp b/src/gui/solvePage.cpp index 2c30adf0..d975205b 100644 --- a/src/gui/solvePage.cpp +++ b/src/gui/solvePage.cpp @@ -76,6 +76,9 @@ solvePage::solvePage(QWidget *parent) : QWidget(parent) openOutputPathButton->setIcon( QIcon( ":folder.png" ) ); openOutputPathButton->setDisabled( true ); + CaseFileBox = new QCheckBox(tr("Generate Casefile"), this); + CaseFileBox->setChecked(true); + layout = new QVBoxLayout; layout->addWidget(availProcLabel); @@ -83,6 +86,7 @@ solvePage::solvePage(QWidget *parent) : QWidget(parent) pageLayout->addWidget(numProcLabel); pageLayout->addWidget(numProcSpinBox); pageLayout->addWidget(solveToolButton); + pageLayout->addWidget(CaseFileBox); pageLayout->addStretch(); outputPathLayout = new QHBoxLayout; diff --git a/src/gui/solvePage.h b/src/gui/solvePage.h index 1f8d61b9..89e3dade 100644 --- a/src/gui/solvePage.h +++ b/src/gui/solvePage.h @@ -37,6 +37,7 @@ #include #include +#include #include #ifdef _OPENMP @@ -58,6 +59,7 @@ class solvePage : public QWidget QString availProcString; QLabel *availProcLabel; QSpinBox *numProcSpinBox; + QCheckBox *CaseFileBox; QLabel *outputDirLabel; QLineEdit *outputDirLineEdit; diff --git a/src/gui/stationFetchWidget.cpp b/src/gui/stationFetchWidget.cpp index d7933312..8bf4486c 100644 --- a/src/gui/stationFetchWidget.cpp +++ b/src/gui/stationFetchWidget.cpp @@ -48,6 +48,8 @@ stationFetchWidget::stationFetchWidget(QWidget *parent) currentBox->setVisible(false); fetchMetaButton->setVisible(false); //Hide the metadata button from the gui + wasStationFetched = false; + stationFetchProgress = new QProgressDialog(this); //Sets up a mediocre progress bar that kind of works stationFetchProgress->setWindowModality(Qt::ApplicationModal); stationFetchProgress->setAutoReset(false); //Displays how far along the download process is @@ -196,6 +198,7 @@ void stationFetchWidget::updateFetchProgress() */ void stationFetchWidget::executeFetchStation() { + set_wasStationFetched(true); stationFetchProgress->setLabelText("Downloading Station Data!"); stationFetchProgress->setRange(0,0); //make it bounce back and forth stationFetchProgress->setCancelButtonText("Cancel"); @@ -256,6 +259,56 @@ std::string stationFetchWidget::demButcher()//Cleans up the DEM for use in the d // std::string demBetter=demRaw.substr(0,lastDot)+"/"; return demPath; } + +void stationFetchWidget::set_wasStationFetched(bool stationFetched) +{ + wasStationFetched = stationFetched; +} + +bool stationFetchWidget::get_wasStationFetched() +{ + return wasStationFetched; +} + +std::string stationFetchWidget::getType() +{ + if (geoLoc->currentIndex() == 0) + { + return "bbox"; + } else + { + return "stid"; + } +} + +double stationFetchWidget::getBuffer() +{ + return bufferSpin->text().toDouble(); +} + +std::string stationFetchWidget::getBufferUnits() +{ + return buffUnits->currentText().toStdString(); +} + +std::string stationFetchWidget::getStationIDS() +{ + return removeWhiteSpace(idLine->text().toStdString()); +} + +bool stationFetchWidget::get_isTimeSeries() +{ + if (timeLoc->currentIndex() == 1) + { + // is a time series + return true; + } else + { + // is a single time + return false; + } +} + /** * @brief stationFetchWidget::fetchStation * Fetches data from the Mesowest API based on GUI request @@ -420,7 +473,7 @@ int stationFetchWidget::fetchStation() int sY,sMo,sD,sH,sMi; int eY,eMo,eD,eH,eMi; int numSteps=10; //make up a number for now.... It really doesn't matter at this point - + std::string StartTime=startEdit->text().toStdString(); std::string EndTime=endEdit->text().toStdString(); diff --git a/src/gui/stationFetchWidget.h b/src/gui/stationFetchWidget.h index b8dbb815..f18af8aa 100644 --- a/src/gui/stationFetchWidget.h +++ b/src/gui/stationFetchWidget.h @@ -68,7 +68,14 @@ class stationFetchWidget : public QWidget, private Ui::stationFetchWidget void updatetz(QString tz); void fixTime(); std::string removeWhiteSpace(std::string str); - + void set_wasStationFetched(bool stationFetched); + bool get_wasStationFetched(); + std::string getType(); + double getBuffer(); + std::string getBufferUnits(); + std::string getStationIDS(); + bool get_isTimeSeries(); + protected: void closeEvent(QCloseEvent *event); @@ -93,7 +100,7 @@ class stationFetchWidget : public QWidget, private Ui::stationFetchWidget //Progress Bar Stuff QProgressDialog *stationFetchProgress; QFutureWatcher stationFutureWatcher; - + bool wasStationFetched; friend class pointInput; }; diff --git a/src/gui/weatherModel.cpp b/src/gui/weatherModel.cpp index 906e8f46..ac84fc94 100644 --- a/src/gui/weatherModel.cpp +++ b/src/gui/weatherModel.cpp @@ -561,6 +561,10 @@ std::vector weatherModel::timeList() { return tl; } +std::vector weatherModel::getFullTimeList() { + return timelist; +} + void weatherModel::clearTimes() { timelist.clear(); timeModel->setStringList(QStringList()); diff --git a/src/gui/weatherModel.h b/src/gui/weatherModel.h index 13d468ac..512eb18d 100644 --- a/src/gui/weatherModel.h +++ b/src/gui/weatherModel.h @@ -136,6 +136,7 @@ class weatherModel : public QWidget QString tzString; std::vector timeList(); + std::vector getFullTimeList(); private: void loadModelComboBox(); diff --git a/src/ninja/CMakeLists.txt b/src/ninja/CMakeLists.txt index 7cf4c896..cd381a3f 100644 --- a/src/ninja/CMakeLists.txt +++ b/src/ninja/CMakeLists.txt @@ -31,6 +31,7 @@ set(NINJA_SOURCES air.cpp ascii_grid.cpp Array2D.cpp Aspect.cpp + casefile.cpp cellDiurnal.cpp cli.cpp dbfopen.cpp diff --git a/src/ninja/Elevation.cpp b/src/ninja/Elevation.cpp index dcb98713..97b4a3db 100644 --- a/src/ninja/Elevation.cpp +++ b/src/ninja/Elevation.cpp @@ -166,7 +166,7 @@ void Elevation::smooth_elevation(const int smoothDist) { if( smoothDist < 1 ) { - throw std::runtime_error("input smoothDist "+std::to_string(smoothDist)+" for Elevation::smooth_elevation() is not 1 or greater!"); + throw std::runtime_error("input smoothDist "+std::to_string(static_cast(smoothDist))+" for Elevation::smooth_elevation() is not 1 or greater!"); } Elevation dem; // make a temporary copy to keep the calculation values the same diff --git a/src/ninja/casefile.cpp b/src/ninja/casefile.cpp new file mode 100644 index 00000000..a7d3b19e --- /dev/null +++ b/src/ninja/casefile.cpp @@ -0,0 +1,222 @@ +#include "casefile.h" + +CaseFile::CaseFile() +{ + caseZipFile = ""; + finalCaseZipFile = ""; + isZipOpen = false; + zipHandle = NULL; +} + +void CaseFile::setCaseZipFile(std::string caseZippFile) +{ + if (caseZipFile != "") + { + throw std::runtime_error("not allowed to run setCaseZipFile() twice on the same CaseFile instance!!!"); + } + + caseZipFile = caseZippFile; + finalCaseZipFile = caseZippFile; +} + +void CaseFile::updateCaseZipFile(std::string newCaseZipFile) +{ + if (caseZipFile == "") + { + throw std::runtime_error("updateCaseZipFile() called before setCaseZipFile()!!!"); + } + + // only updates the first time that there is a difference, use the first input newCaseZipFile instance for the final caseZipFile name + // this should only occur for the first run/ninja + if (strcmp( caseZipFile.c_str(), finalCaseZipFile.c_str() ) == 0) + { + finalCaseZipFile = newCaseZipFile; + } +} + +void CaseFile::renameCaseZipFile() +{ + if (isZipOpen == true) + { + throw std::runtime_error("renameCaseZipFile() called on a still open zip file!!!"); + } + + if (strcmp( caseZipFile.c_str(), finalCaseZipFile.c_str() ) != 0) + { + if (VSIRename(caseZipFile.c_str(), finalCaseZipFile.c_str()) == 0) + { + CPLDebug("NINJA", "Successfully renamed %s to %s", caseZipFile.c_str(), finalCaseZipFile.c_str()); + caseZipFile = finalCaseZipFile; + } else + { + CPLError(CE_Failure, CPLE_FileIO, "Failed to rename %s to %s", caseZipFile.c_str(), finalCaseZipFile.c_str()); + } + } +} + +void CaseFile::openCaseZipFile() +{ + CPLDebug("NINJA", "opening case zip file %s", caseZipFile.c_str()); + + if (isZipOpen == true) + { + throw std::runtime_error("openCaseZipFile() called on already open zip file!!! " + caseZipFile); + } + + bool doesZipExist = CPLCheckForFile((char*)caseZipFile.c_str(), NULL); + if (doesZipExist == true) + { + printf("WARNING: zip file %s already exists, replacing zip\n", caseZipFile.c_str()); + VSIUnlink( caseZipFile.c_str() ); + } + + zipHandle = CPLCreateZip(caseZipFile.c_str(), NULL); + if (zipHandle == NULL) + { + throw std::runtime_error("Failed to create or open zip file!!! " + caseZipFile); + } + isZipOpen = true; +} + +void CaseFile::closeCaseZipFile() +{ + // just skip if called on an unopened zip file, makes shutting WindNinja down unexpectedly easier to do + if (isZipOpen == true) + { + CPLDebug("NINJA", "closing case zip file %s", caseZipFile.c_str()); + CPLCloseZip(zipHandle); + zipHandle = NULL; + isZipOpen = false; + } +} + +void CaseFile::addFileToZip(const std::string& zipEntry, const std::string& fileToAdd) +{ + // Acquire a lock for the multithreading issue, to protect the non-thread safe zip read and write process +#ifdef _OPENMP + omp_guard netCDF_guard(netCDF_lock); +#endif + + try { + bool doesZipExist = CPLCheckForFile((char*)caseZipFile.c_str(), NULL); + + if (doesZipExist) + { + std::ifstream infile(caseZipFile); + if (!infile.good()) + { + CPLError(CE_Failure, CPLE_FileIO, "ZIP file REALLY does not exist: %s", caseZipFile.c_str()); + return; + } + } else + { + CPLError(CE_Failure, CPLE_FileIO, "ZIP file does not exist: %s", caseZipFile.c_str()); + return; + } + + if (zipHandle == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, "tried to add file to unopened ZIP: %s", caseZipFile.c_str()); + return; + } + + // read in the file data to be copied to the zip + VSILFILE *FILE = VSIFOpenL(fileToAdd.c_str(), "rb"); + if (FILE == nullptr) + { + CPLError(CE_Failure, CPLE_FileIO, "Could not open file for reading with VSIL: %s", fileToAdd.c_str()); + return; + } + + VSIFSeekL(FILE, 0, SEEK_END); + vsi_l_offset fileSize = VSIFTellL(FILE); + VSIFSeekL(FILE, 0, SEEK_SET); // rather than VSIRewindL(FILE);? + + char *data = (char*)CPLMalloc(fileSize); + if (data == nullptr) + { + CPLError(CE_Failure, CPLE_FileIO, "Failed to allocate memory for file data."); + VSIFCloseL(FILE); + return; + } + + if (VSIFReadL(data, 1, fileSize, FILE) != fileSize) + { + CPLError(CE_Failure, CPLE_FileIO, "Failed to read file contents: %s", fileToAdd.c_str()); + CPLFree(data); + VSIFCloseL(FILE); + return; + } + + VSIFCloseL(FILE); + + // add the file data to the zip + if (CPLCreateFileInZip(zipHandle, zipEntry.c_str(), NULL) != CE_None) + { + CPLError(CE_Failure, CPLE_FileIO, "Failed to create file in zip: %s", zipEntry.c_str()); + CPLFree(data); + return; + } + + if (CPLWriteFileInZip(zipHandle, data, static_cast(fileSize)) != CE_None) + { + CPLError(CE_Failure, CPLE_FileIO, "Failed to write data to file in zip: %s", zipEntry.c_str()); + CPLFree(data); + return; + } + + if (CPLCloseFileInZip(zipHandle) != CE_None) + { + CPLError(CE_Failure, CPLE_FileIO, "Failed to close file in zip: %s", zipEntry.c_str()); + CPLFree(data); + return; + } + + CPLFree(data); + + } catch (const std::exception& e) + { + CPLError(CE_Failure, CPLE_AppDefined, "Exception caught during casefile addFileToZip(): %s", e.what()); + } catch (...) + { + CPLError(CE_Failure, CPLE_AppDefined, "Exception caught during casefile addFileToZip(): Cannot determine exception type."); + } +} + +bool CaseFile::getIsZipOpen() +{ + return isZipOpen; +} + +std::string CaseFile::getCaseZipFile() +{ + return caseZipFile; +} + +std::string CaseFile::getCurrentTime() +{ + const boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + + boost::posix_time::time_facet* facet; + facet = new boost::posix_time::time_facet(); + facet->format("%Y-%m-%d_%H-%M-%S"); + + std::ostringstream oss; + oss.imbue( std::locale(std::locale::classic(), facet) ); + oss << now; + + return oss.str(); +} + +std::string CaseFile::convertDateTimeToStd(const boost::local_time::local_date_time& ninjaTime) +{ + boost::local_time::local_time_facet* facet; + facet = new boost::local_time::local_time_facet(); + facet->format("%Y-%m-%d_%H-%M-%S"); + + std::ostringstream oss; + oss.imbue( std::locale(std::locale::classic(), facet) ); + oss << ninjaTime; + + return oss.str(); +} diff --git a/src/ninja/casefile.h b/src/ninja/casefile.h new file mode 100644 index 00000000..27f7cdad --- /dev/null +++ b/src/ninja/casefile.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * $Id$ + * + * Project: WindNinja + * Purpose: Input/Output Handling of Casefile + * Author: Rui Zhang + * + ****************************************************************************** + * + * THIS SOFTWARE WAS DEVELOPED AT THE ROCKY MOUNTAIN RESEARCH STATION (RMRS) + * MISSOULA FIRE SCIENCES LABORATORY BY EMPLOYEES OF THE FEDERAL GOVERNMENT + * IN THE COURSE OF THEIR OFFICIAL DUTIES. PURSUANT TO TITLE 17 SECTION 105 + * OF THE UNITED STATES CODE, THIS SOFTWARE IS NOT SUBJECT TO COPYRIGHT + * PROTECTION AND IS IN THE PUBLIC DOMAIN. RMRS MISSOULA FIRE SCIENCES + * LABORATORY ASSUMES NO RESPONSIBILITY WHATSOEVER FOR ITS USE BY OTHER + * PARTIES, AND MAKES NO GUARANTEES, EXPRESSED OR IMPLIED, ABOUT ITS QUALITY, + * RELIABILITY, OR ANY OTHER CHARACTERISTIC. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#ifndef CASEFILE_H +#define CASEFILE_H + +#include +#include +#include +#include +#include + +#ifdef _OPENMP +#include +#include "omp_guard.h" +#endif + +#ifdef _OPENMP +extern omp_lock_t netCDF_lock; +#endif + +class CaseFile +{ + +private: + + std::string caseZipFile; + std::string finalCaseZipFile; + bool isZipOpen; + void* zipHandle; + +public: + + CaseFile(); + + void setCaseZipFile(std::string caseZippFile); + void updateCaseZipFile(std::string newCaseZipFile); + void renameCaseZipFile(); + + void openCaseZipFile(); + void closeCaseZipFile(); + + void addFileToZip(const std::string& zipEntry, const std::string& fileToAdd); + + bool getIsZipOpen(); + std::string getCaseZipFile(); + + std::string getCurrentTime(); + std::string convertDateTimeToStd(const boost::local_time::local_date_time& ninjaTime); + +}; + +#endif /* CASEFILE_H */ + diff --git a/src/ninja/cli.cpp b/src/ninja/cli.cpp index 40d68a61..c6ee9ec3 100644 --- a/src/ninja/cli.cpp +++ b/src/ninja/cli.cpp @@ -29,7 +29,6 @@ *****************************************************************************/ #include "cli.h" -#include /** * Function used to check that 'opt1' and 'opt2' are not specified @@ -97,7 +96,7 @@ pair at_option_parser(string const&s) // if we have an 'elevation_file' program option check if file exists and has a non-geographic srs. // if the srs is geographic, try to convert to a UTM file in the configured 'output_path' (or current dir if not set) // return address of a string that points to a valid non-geographic file or NULL if none was found or could be constructed -const std::string* get_checked_elevation_file (po::variables_map& vm) +const std::string* get_checked_elevation_file(po::variables_map& vm) { if (vm.count("elevation_file")) { const string* filename = &vm["elevation_file"].as(); @@ -162,8 +161,10 @@ int windNinjaCLI(int argc, char* argv[]) bool writeParsed = false; bool writeValues = false; + CaseFile casefile; + //initializeOptions(); - + // Moved to initializeOptions() try { // Declare a group of options that will be @@ -217,6 +218,7 @@ int windNinjaCLI(int argc, char* argv[]) // config file po::options_description config("Simulation options"); config.add_options() + ("write_casefile", po::value()->default_value(true), "generate a casefile of the run which will allow a history of your input and output") ("num_threads", po::value()->default_value(1), "number of threads to use during simulation") ("elevation_file", po::value(), "input elevation path/filename (*.asc, *.lcp, *.tif, *.img)") ("fetch_elevation", po::value(), "download an elevation file from an internet server and save to path/filename") @@ -394,6 +396,7 @@ int windNinjaCLI(int argc, char* argv[]) cout << endl; } } + store(opts_command, vm); //notify(vm); @@ -442,6 +445,108 @@ int windNinjaCLI(int argc, char* argv[]) } } + // helper for casefile output of CLI + if (vm["write_casefile"].as() == true) + { + std::string outputDir = vm.count("output_path") ? vm["output_path"].as().c_str() : ""; + if( vm.count("output_path") ) + { + outputDir = vm["output_path"].as(); + } else // if (outputDir == "") + { + // hrm, should work, but it isn't ideal. searches for "customOutputPath" in ninjafoam.cpp and ninja.cpp show + // that this is correct to keep paths all the same EXCEPT for with weather model initialization, which uses + // input.forecastFilename instead unless it is not set as an input, THEN it uses the dem file + // it is not an easy thing to make sure we have forecastFilename here in the code the same way as later in the code + outputDir = CPLGetPath( vm["elevation_file"].as().c_str() ); + } + + std::string zipFile = CPLFormFilename(outputDir.c_str(), "tmp_ninja", "zip"); + + std::string mainCaseCfgFilename = "config.cfg"; + std::string mainCaseCfgFile = CPLFormFilename(outputDir.c_str(), mainCaseCfgFilename.c_str(), ""); + + casefile.setCaseZipFile(zipFile); + casefile.openCaseZipFile(); + + std::ofstream mainCaseCfgFILE(mainCaseCfgFile); + if (!mainCaseCfgFILE) + { + std::cerr << "Error: Could not open the casefile " << mainCaseCfgFile << " file for writing!" << std::endl; + return 1; + } + + for( po::variables_map::iterator pair = vm.begin(); pair != vm.end(); pair++ ) + { + const std::string& option_name = pair->first; + const po::variable_value& option_value = pair->second; + + mainCaseCfgFILE << "--" << option_name << " "; + + try { + if (option_value.value().type() == typeid(int)) + { + mainCaseCfgFILE << option_value.as() << std::endl; + } else if (option_value.value().type() == typeid(bool)) + { + mainCaseCfgFILE << std::boolalpha << option_value.as() << std::endl; + } else if (option_value.value().type() == typeid(std::string)) + { + mainCaseCfgFILE << option_value.as() << std::endl; + } else if (option_value.value().type() == typeid(double)) + { + mainCaseCfgFILE << option_value.as() << std::endl; + } else if (option_value.value().type() == typeid(std::vector)) + { + std::vector vec = option_value.as>(); + for( size_t vIdx = 0; vIdx < vec.size(); vIdx++ ) + { + std::string str = vec[vIdx]; + mainCaseCfgFILE << str << " "; + } + mainCaseCfgFILE << std::endl; + } else + { + mainCaseCfgFILE << "Unknown type" << std::endl; + } + } catch (const boost::bad_any_cast& e) + { + mainCaseCfgFILE << "Bad cast: " << e.what() << std::endl; + } + } + // This flush is actually optional because close() will flush automatically + mainCaseCfgFILE.flush(); + mainCaseCfgFILE.close(); + + std::string inputCfgFile = vm["config_file"].as(); + std::string inputCfgFilename = CPLGetFilename( inputCfgFile.c_str() ); + casefile.addFileToZip(inputCfgFilename, inputCfgFile); + std::string demFile = vm["elevation_file"].as(); + std::string demFilename = CPLGetFilename( demFile.c_str() ); + casefile.addFileToZip(demFilename, demFile); + casefile.addFileToZip(mainCaseCfgFilename, mainCaseCfgFile); + VSIUnlink( mainCaseCfgFile.c_str() ); + if (vm.count("forecast_filename")) + { + std::string weatherFile = vm["forecast_filename"].as(); + std::string weatherFilename = CPLGetFilename( weatherFile.c_str() ); + std::string weatherZipEntry = CPLFormFilename("WxModelInitialization", weatherFilename.c_str(), ""); + casefile.addFileToZip(weatherZipEntry, weatherFile); + } + if (vm.count("wx_station_filename")) + { + std::string pointFile = vm["wx_station_filename"].as(); + std::string pointFilename = CPLGetFilename( pointFile.c_str() ); + std::string pointPath = CPLGetPath( pointFile.c_str() ); + if (pointPath.find("WXSTATIONS-") != std::string::npos) + { + pointFilename = CPLFormFilename(CPLGetFilename(pointPath.c_str()), pointFilename.c_str(), ""); + } + std::string pointZipEntry = CPLFormFilename("PointInitialization", pointFilename.c_str(), ""); + casefile.addFileToZip(pointZipEntry, pointFile); + } + } + if (vm.count("help")) { cout << visible << "\n"; return 0; @@ -1271,9 +1376,9 @@ int windNinjaCLI(int argc, char* argv[]) option_dependency(vm, "wx_station_filename", "start_day"); option_dependency(vm, "wx_station_filename", "start_hour"); option_dependency(vm, "wx_station_filename", "start_minute"); - option_dependency(vm, "wx_station_filename", "stop_year"); option_dependency(vm, "wx_station_filename", "stop_month"); option_dependency(vm, "wx_station_filename", "stop_day"); + option_dependency(vm, "wx_station_filename", "stop_year"); option_dependency(vm, "wx_station_filename", "stop_hour"); option_dependency(vm, "wx_station_filename", "stop_minute"); option_dependency(vm, "wx_station_filename", "number_time_steps"); @@ -1356,7 +1461,9 @@ int windNinjaCLI(int argc, char* argv[]) windsim.setDEM( i_, *elevation_file ); windsim.setPosition( i_ ); //get position from DEM file - + + windsim.setCaseFilePtr( i_, casefile ); + #ifdef NINJAFOAM if(vm["momentum_flag"].as()){ conflicting_options(vm, "mesh_choice", "mesh_count"); @@ -1881,7 +1988,7 @@ int windNinjaCLI(int argc, char* argv[]) } else { - cout << "Invalid pdf base map: " << pbm << ". Should be 'topofire' or 'hillshade'"; + cout << "Invalid pdf base map: " << pbm << ". Should be 'topofire' or 'hillshade'" << endl; } windsim.setPDFBaseMap( i_, pbs ); conflicting_options(vm, "pdf_size", "pdf_height"); @@ -1951,26 +2058,33 @@ int windNinjaCLI(int argc, char* argv[]) if(!windsim.startRuns(vm["num_threads"].as())) { cout << "ERROR: The simulations returned a bad value.\n"; + casefile.closeCaseZipFile(); return -1; } + casefile.closeCaseZipFile(); + casefile.renameCaseZipFile(); } - catch (badForecastFile& e - ) { //catch a badForecastFile + catch (badForecastFile& e) + { //catch a badForecastFile cout << "Exception badForecastFile caught: " << e.what() << "\n"; cout << "There was a problem downloading the forecast file or it had bad data.\n"; + casefile.closeCaseZipFile(); return -1; }catch (bad_alloc& e) { cout << "Exception bad_alloc caught: " << e.what() << endl; cout << "WindNinja appears to have run out of memory." << endl; + casefile.closeCaseZipFile(); return -1; }catch (exception& e) { cout << "Exception caught: " << e.what() << endl; + casefile.closeCaseZipFile(); return -1; }catch (...) { cout << "Exception caught: Cannot determine exception type." << endl; + casefile.closeCaseZipFile(); return -1; } diff --git a/src/ninja/cli.h b/src/ninja/cli.h index ba17522a..e58deaa6 100644 --- a/src/ninja/cli.h +++ b/src/ninja/cli.h @@ -59,6 +59,12 @@ void option_dependency(const po::variables_map& vm, const char* for_what, const void verify_option_set(const po::variables_map& vm, const char* optn); +//bool checkArgs(string arg1, string arg2, string arg3); + +pair at_option_parser(string const&s); + +const std::string* get_checked_elevation_file(po::variables_map& vm); + // this should be used instead of direct 'variables_map["key"].as()' calls since otherwise a single typo // in the key literal results in undefined behavior that can corrupt memory miles away. // Alternatively keys could be defined/used as constants to catch this at compile time @@ -72,6 +78,4 @@ inline T option_val (const po::variables_map& vm, const char* key) { } } -//bool checkArgs(string arg1, string arg2, string arg3); - #endif /* CLI_H */ diff --git a/src/ninja/ninja.cpp b/src/ninja/ninja.cpp index 43bb2a10..90ed11f3 100644 --- a/src/ninja/ninja.cpp +++ b/src/ninja/ninja.cpp @@ -83,6 +83,8 @@ ninja::ninja() input.inputsComType = ninjaComClass::ninjaDefaultCom; input.Com = new ninjaDefaultComHandler(); + casefilename = ""; + casefile = NULL; } /**Ninja destructor @@ -154,7 +156,6 @@ ninja::ninja(const ninja &rhs) endSolve=0.0; startWriteOut=0.0; endWriteOut=0.0; - //Pointers to dynamically allocated memory DIAG=NULL; PHI=NULL; @@ -170,6 +171,9 @@ ninja::ninja(const ninja &rhs) slope=NULL; shade=NULL; solar=NULL; + + casefilename = rhs.casefilename; + casefile = rhs.casefile; } /** @@ -247,6 +251,9 @@ ninja &ninja::operator=(const ninja &rhs) slope=NULL; shade=NULL; solar=NULL; + + casefilename = rhs.casefilename; + casefile = rhs.casefile; } return *this; } @@ -2822,17 +2829,17 @@ void ninja::setUvGrids (AsciiGrid& angGrid, AsciiGrid& velGrid, /**Writes output files. * Writes VTK, FARSITE ASCII Raster, text comparison, shape, and kmz output files. */ - void ninja::writeOutputFiles() { set_outputFilenames(mesh.meshResolution, mesh.meshResolutionUnits); - //Write volume data to VTK format (always in m/s?) - if(input.volVTKOutFlag) - { - try{ + //Write volume data to VTK format (always in m/s?) + //write to casefile regardless of if VTK is checked + if(input.volVTKOutFlag == true || casefile->getIsZipOpen()) + { + try{ bool vtk_out_as_utm = false; - if(CSLTestBoolean(CPLGetConfigOption("VTK_OUT_AS_UTM", "FALSE"))) + if(CSLTestBoolean(CPLGetConfigOption("VTK_OUT_AS_UTM", "FALSE"))) { vtk_out_as_utm = CPLGetConfigOption("VTK_OUT_AS_UTM", "FALSE"); } @@ -2843,19 +2850,48 @@ void ninja::writeOutputFiles() { vtkWriteFormat = found_vtkWriteFormat; } - volVTK VTK(u, v, w, mesh.XORD, mesh.YORD, mesh.ZORD, input.dem.get_xllCorner(), input.dem.get_yllCorner(), input.dem.get_nCols(), input.dem.get_nRows(), mesh.nlayers, input.volVTKFile, vtkWriteFormat, vtk_out_as_utm); - }catch (exception& e) - { - input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during volume VTK file writing: %s", e.what()); - }catch (...) - { - input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during volume VTK file writing: Cannot determine exception type."); - } - } + volVTK VTK(u, v, w, mesh.XORD, mesh.YORD, mesh.ZORD, input.dem.get_xllCorner(), input.dem.get_yllCorner(), input.dem.get_nCols(), input.dem.get_nRows(), mesh.nlayers, input.volVTKFile, vtkWriteFormat, vtk_out_as_utm); + + std::string volVtkFilename = CPLGetFilename(input.volVTKFile.c_str()); + std::string volVtkSurfFilename = CPLSPrintf("%s_surf.vtk",CPLGetBasename(input.volVTKFile.c_str())); + std::string volVtkSurfFile = CPLFormFilename(CPLGetPath(input.volVTKFile.c_str()), volVtkSurfFilename.c_str(), ""); + if( casefile->getIsZipOpen() ) + { + casefile->updateCaseZipFile(casefilename); + + std::string timestr = ""; + if( input.ninjaTime.is_not_a_date_time() ) + { + timestr = casefile->getCurrentTime(); + } else + { + timestr = casefile->convertDateTimeToStd(input.ninjaTime); + } + + std::string volVtkZipEntry = CPLFormFilename(timestr.c_str(), volVtkFilename.c_str(), ""); + casefile->addFileToZip(volVtkZipEntry, input.volVTKFile); + std::string volVtkSurfZipEntry = CPLFormFilename(timestr.c_str(), volVtkSurfFilename.c_str(), ""); + casefile->addFileToZip(volVtkSurfZipEntry, volVtkSurfFile); + } + + if( input.volVTKOutFlag == false ) + { + VSIUnlink( input.volVTKFile.c_str() ); + VSIUnlink( volVtkSurfFile.c_str() ); + } + + }catch (exception& e) + { + input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during volume VTK file writing: %s", e.what()); + }catch (...) + { + input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during volume VTK file writing: Cannot determine exception type."); + } + } - u.deallocate(); - v.deallocate(); - w.deallocate(); + u.deallocate(); + v.deallocate(); + w.deallocate(); #pragma omp parallel sections { @@ -4528,6 +4564,16 @@ void ninja::set_position(double lat_degrees, double lat_minutes, double lat_seco "less than -180 degrees in ninja::set_position()."); } +/** + * Set the pointer to the shared casefile. + * + * @param a casefile class passed in as a reference + */ +void ninja::set_casefilePtr( CaseFile &theCaseFile ) +{ + casefile = &theCaseFile; +} + void ninja::set_numberCPUs(int CPUs) { if(CPUs < 1) @@ -4876,7 +4922,7 @@ void ninja::set_outputFilenames(double& meshResolution, input.pdfResolution = meshResolution; //Do file naming string stuff for all output files - std::string rootFile, rootName, fileAppend, timeAppend, wxModelTimeAppend, kmz_fileAppend, \ + std::string rootFile, rootName, fileAppend, timeAppend, wxModelTimeAppend, case_fileAppend, kmz_fileAppend, \ shp_fileAppend, ascii_fileAppend, volVTK_fileAppend, mesh_units, kmz_mesh_units, \ shp_mesh_units, ascii_mesh_units, pdf_fileAppend, pdf_mesh_units; @@ -4960,13 +5006,15 @@ void ninja::set_outputFilenames(double& meshResolution, ascii_mesh_units = lengthUnits::getString( input.velOutputFileDistanceUnits ); pdf_mesh_units = lengthUnits::getString( input.pdfUnits ); - ostringstream os, os_kmz, os_shp, os_ascii, os_pdf; + ostringstream os, os_case, os_kmz, os_shp, os_ascii, os_pdf; + if( input.initializationMethod == WindNinjaInputs::domainAverageInitializationFlag || input.initializationMethod == WindNinjaInputs::foamDomainAverageInitializationFlag ) { double tempSpeed = input.inputSpeed; velocityUnits::fromBaseUnits(tempSpeed, input.inputSpeedUnits); os << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); + os_case << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); os_kmz << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); os_shp << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); os_ascii << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); @@ -4975,6 +5023,7 @@ void ninja::set_outputFilenames(double& meshResolution, else if( input.initializationMethod == WindNinjaInputs::pointInitializationFlag ) { os << "_point"; + os_case << "_point"; os_kmz << "_point"; os_shp << "_point"; os_ascii << "_point"; @@ -4995,6 +5044,7 @@ void ninja::set_outputFilenames(double& meshResolution, lengthUnits::fromBaseUnits(pdfResolutionTemp, input.pdfUnits); os << "_" << timeAppend << (long) (meshResolutionTemp+0.5) << mesh_units; + os_case << "_" << timeAppend << (long) (meshResolutionTemp+0.5) << mesh_units; os_kmz << "_" << timeAppend << (long) (kmzResolutionTemp+0.5) << kmz_mesh_units; os_shp << "_" << timeAppend << (long) (shpResolutionTemp+0.5) << shp_mesh_units; os_ascii << "_" << timeAppend << (long) (velResolutionTemp+0.5) << ascii_mesh_units; @@ -5003,6 +5053,7 @@ void ninja::set_outputFilenames(double& meshResolution, if( input.stabilityFlag == true && input.alphaStability != -1 ) { os << "_alpha_" << input.alphaStability; + os_case << "_alpha_" << input.alphaStability; os_kmz << "_alpha_" << input.alphaStability; os_shp << "_alpha_" << input.alphaStability; os_ascii << "_alpha_" << input.alphaStability; @@ -5011,6 +5062,7 @@ void ninja::set_outputFilenames(double& meshResolution, else if( input.stabilityFlag == true && input.alphaStability == -1 ) { os << "_non_neutral_stability"; + os_case << "_non_neutral_stability"; os_kmz << "_non_neutral_stability"; os_shp << "_non_neutral_stability"; os_ascii << "_non_neutral_stability"; @@ -5018,12 +5070,15 @@ void ninja::set_outputFilenames(double& meshResolution, } fileAppend = os.str(); + case_fileAppend = os_case.str(); kmz_fileAppend = os_kmz.str(); shp_fileAppend = os_shp.str(); ascii_fileAppend = os_ascii.str(); pdf_fileAppend = os_pdf.str(); + casefilename = rootFile + case_fileAppend + "_ninja.zip"; + input.kmlFile = rootFile + kmz_fileAppend + ".kml"; input.kmzFile = rootFile + kmz_fileAppend + ".kmz"; diff --git a/src/ninja/ninja.h b/src/ninja/ninja.h index 326a5138..dd671da6 100644 --- a/src/ninja/ninja.h +++ b/src/ninja/ninja.h @@ -65,6 +65,7 @@ #include "SurfProperties.h" #include "surfaceVectorField.h" #include "WindNinjaInputs.h" +#include "casefile.h" #include "KmlVector.h" #include "ShapeVector.h" #include "preconditioner.h" @@ -132,6 +133,9 @@ class ninja bool cancel; //if set to "false" during a simulation (ie when "simulate_wind()" is running), the simulation will attempt to end Mesh mesh; + std::string casefilename; + CaseFile* casefile; + //output grids to access the final wind grids (typically used by other programs running the windninja API such as WFDSS, FlamMap, etc. AsciiGridAngleGrid; AsciiGridVelocityGrid; @@ -308,6 +312,7 @@ class ninja void set_position(double lat_degrees, double lat_minutes, double long_degrees, double long_minutes); //input as degrees, decimal minutes void set_position(double lat_degrees, double lat_minutes, double lat_seconds, double long_degrees, double long_minutes, double long_seconds); //input as degrees, minutes, seconds + void set_casefilePtr( CaseFile &theCaseFile ); void set_numberCPUs(int CPUs); void set_outputSpeedGridResolution(double resolution, lengthUnits::eLengthUnits units); void set_outputDirectionGridResolution(double resolution, lengthUnits::eLengthUnits units); @@ -368,7 +373,7 @@ class ninja void checkInputs(); void dumpMemory(); - WindNinjaInputs input; //The place were all inputs (except mesh) are stored. + WindNinjaInputs input; //The place where all inputs (except mesh) are stored. protected: void checkCancel(); diff --git a/src/ninja/ninjaArmy.cpp b/src/ninja/ninjaArmy.cpp index 089b5705..7b00240d 100644 --- a/src/ninja/ninjaArmy.cpp +++ b/src/ninja/ninjaArmy.cpp @@ -1362,6 +1362,13 @@ int ninjaArmy::setPosition( const int nIndex, char ** papszOptions ) IF_VALID_INDEX_TRY( nIndex, ninjas, ninjas[ nIndex ]->set_position() ); } +int ninjaArmy::setCaseFilePtr( const int nIndex, CaseFile &casefile, + char ** papszOptions ) +{ + IF_VALID_INDEX_TRY( nIndex, ninjas, + ninjas[ nIndex ]->set_casefilePtr( casefile ) ); +} + int ninjaArmy::setNumberCPUs( const int nIndex, const int nCPUs, char ** papszOptions ) { IF_VALID_INDEX_TRY( nIndex, ninjas, ninjas[ nIndex ]->set_numberCPUs( nCPUs ) ); diff --git a/src/ninja/ninjaArmy.h b/src/ninja/ninjaArmy.h index 372cd4c1..b743a1cf 100644 --- a/src/ninja/ninjaArmy.h +++ b/src/ninja/ninjaArmy.h @@ -49,6 +49,7 @@ #include "boost/typeof/typeof.hpp" #endif #include "WindNinjaInputs.h" +#include "casefile.h" #include "fetch_factory.h" #include @@ -499,6 +500,16 @@ class ninjaArmy char ** papszOptions=NULL ); int setPosition( const int nIndex, char ** papszOptions=NULL ); + /** + * \brief Set the pointer to the shared casefile of a ninja + * + * \param nIndex index of a ninja + * \param a casefile class passed in as a reference + * \return errval Returns NINJA_SUCCESS upon success + */ + int setCaseFilePtr( const int nIndex, CaseFile &casefile, + char ** papszOptions=NULL ); + /** * \brief Set the input speed grid filename from a NinjaFOAM run for use with diurnal * diff --git a/src/ninja/ninjafoam.cpp b/src/ninja/ninjafoam.cpp index 815feb9b..307ea29a 100644 --- a/src/ninja/ninjafoam.cpp +++ b/src/ninja/ninjafoam.cpp @@ -3138,7 +3138,7 @@ void NinjaFoam::GenerateAndSampleMassMesh() // generate massMesh if required for other outputs try{ - if ( writeMassMesh == true ) { + if ( writeMassMesh == true || casefile->getIsZipOpen() ) { generateMassMesh(); } }catch (exception& e) @@ -3204,7 +3204,7 @@ void NinjaFoam::SetOutputResolution() void NinjaFoam::SetOutputFilenames() { //Do file naming string stuff for all output files - std::string rootFile, rootName, timeAppend, wxModelTimeAppend, fileAppend, kmz_fileAppend, \ + std::string rootFile, rootName, timeAppend, wxModelTimeAppend, fileAppend, case_fileAppend, kmz_fileAppend, \ shp_fileAppend, ascii_fileAppend, mesh_units, kmz_mesh_units, \ shp_mesh_units, ascii_mesh_units, pdf_fileAppend, pdf_mesh_units; @@ -3249,6 +3249,7 @@ void NinjaFoam::SetOutputFilenames() timeAppend = timestream.str(); + ostringstream wxModelTimestream; boost::local_time::local_time_facet* wxModelOutputFacet; wxModelOutputFacet = new boost::local_time::local_time_facet(); @@ -3265,12 +3266,13 @@ void NinjaFoam::SetOutputFilenames() ascii_mesh_units = lengthUnits::getString( input.velOutputFileDistanceUnits ); pdf_mesh_units = lengthUnits::getString( input.pdfUnits ); - ostringstream os, os_kmz, os_shp, os_ascii, os_pdf; + ostringstream os, os_case, os_kmz, os_shp, os_ascii, os_pdf; if( input.initializationMethod == WindNinjaInputs::domainAverageInitializationFlag ){ double tempSpeed = input.inputSpeed; velocityUnits::fromBaseUnits(tempSpeed, input.inputSpeedUnits); os << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); + os_case << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); os_kmz << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); os_shp << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); os_ascii << "_" << (long) (input.inputDirection+0.5) << "_" << (long) (tempSpeed+0.5); @@ -3292,17 +3294,21 @@ void NinjaFoam::SetOutputFilenames() lengthUnits::fromBaseUnits(pdfResolutionTemp, input.pdfUnits); os << "_" << timeAppend << (long) (meshResolutionTemp+0.5) << mesh_units; + os_case << "_" << timeAppend << (long) (meshResolutionTemp+0.5) << mesh_units; os_kmz << "_" << timeAppend << (long) (kmzResolutionTemp+0.5) << kmz_mesh_units; os_shp << "_" << timeAppend << (long) (shpResolutionTemp+0.5) << shp_mesh_units; os_ascii << "_" << timeAppend << (long) (velResolutionTemp+0.5) << ascii_mesh_units; os_pdf << "_" << timeAppend << (long) (pdfResolutionTemp+0.5) << pdf_mesh_units; fileAppend = os.str(); + case_fileAppend = os_case.str(); kmz_fileAppend = os_kmz.str(); shp_fileAppend = os_shp.str(); ascii_fileAppend = os_ascii.str(); pdf_fileAppend = os_pdf.str(); + casefilename = rootFile + case_fileAppend + "_ninja.zip"; + input.kmlFile = rootFile + kmz_fileAppend + ".kml"; input.kmzFile = rootFile + kmz_fileAppend + ".kmz"; @@ -3592,48 +3598,49 @@ void NinjaFoam::WriteOutputFiles() output.write(input.pdfFile, "PDF"); - if(angTempGrid) - { - delete angTempGrid; - angTempGrid=NULL; - } - if(velTempGrid) - { - delete velTempGrid; - velTempGrid=NULL; - } - } - }catch (exception& e) - { - input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during pdf file writing: %s", e.what()); - }catch (...) - { - input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during pdf file writing: Cannot determine exception type."); - } - - + if(angTempGrid) + { + delete angTempGrid; + angTempGrid=NULL; + } + if(velTempGrid) + { + delete velTempGrid; + velTempGrid=NULL; + } + } + }catch (exception& e) + { + input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during pdf file writing: %s", e.what()); + }catch (...) + { + input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during pdf file writing: Cannot determine exception type."); + } + + try{ - if ( input.volVTKOutFlag == true ) { - writeMassMeshVtkOutput(); - } - }catch (exception& e) - { - input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during NINJAFOAM mass mesh vtk file writing: %s", e.what()); - }catch (...) - { - input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during NINJAFOAM mass mesh vtk file writing: Cannot determine exception type."); - } - + // write mass mesh if casefile is turned on - casefile always needs a vtk + if( input.volVTKOutFlag == true || casefile->getIsZipOpen()) + { + writeMassMeshVtkOutput(); + } + }catch (exception& e) + { + input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during NINJAFOAM mass mesh vtk file writing: %s", e.what()); + }catch (...) + { + input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during NINJAFOAM mass mesh vtk file writing: Cannot determine exception type."); + } + } void NinjaFoam::writeMassMeshVtkOutput() { - CPLDebug("NINJAFOAM", "writing mass mesh vtk output for foam simulation."); - try { + CPLDebug("NINJAFOAM", "writing vtk file"); bool vtk_out_as_utm = false; - if(CSLTestBoolean(CPLGetConfigOption("VTK_OUT_AS_UTM", "FALSE"))) + if(CSLTestBoolean(CPLGetConfigOption("VTK_OUT_AS_UTM", "FALSE"))) { vtk_out_as_utm = CPLGetConfigOption("VTK_OUT_AS_UTM", "FALSE"); } @@ -3644,13 +3651,42 @@ void NinjaFoam::writeMassMeshVtkOutput() { vtkWriteFormat = found_vtkWriteFormat; } - volVTK VTK(massMesh_u, massMesh_v, massMesh_w, massMesh.XORD, massMesh.YORD, massMesh.ZORD, input.dem.get_xllCorner(), input.dem.get_yllCorner(), input.dem.get_nCols(), input.dem.get_nRows(), massMesh.nlayers, input.volVTKFile, vtkWriteFormat, vtk_out_as_utm); - } catch (exception& e) { - input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during volume VTK file writing: %s", e.what()); - } catch (...) { - input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during volume VTK file writing: Cannot determine exception type."); - } - + volVTK VTK(massMesh_u, massMesh_v, massMesh_w, massMesh.XORD, massMesh.YORD, massMesh.ZORD, input.dem.get_xllCorner(), input.dem.get_yllCorner(), input.dem.get_nCols(), input.dem.get_nRows(), massMesh.nlayers, input.volVTKFile, vtkWriteFormat, vtk_out_as_utm); + + std::string volVtkFilename = CPLGetFilename(input.volVTKFile.c_str()); + std::string volVtkSurfFilename = CPLSPrintf("%s_surf.vtk",CPLGetBasename(input.volVTKFile.c_str())); + std::string volVtkSurfFile = CPLFormFilename(CPLGetPath(input.volVTKFile.c_str()), volVtkSurfFilename.c_str(), ""); + if( casefile->getIsZipOpen() ) + { + casefile->updateCaseZipFile(casefilename); + + std::string timestr = ""; + if( input.ninjaTime.is_not_a_date_time() ) + { + timestr = casefile->getCurrentTime(); + } else + { + timestr = casefile->convertDateTimeToStd(input.ninjaTime); + } + + std::string volVtkZipEntry = CPLFormFilename(timestr.c_str(), volVtkFilename.c_str(), ""); + casefile->addFileToZip(volVtkZipEntry, input.volVTKFile); + std::string volVtkSurfZipEntry = CPLFormFilename(timestr.c_str(), volVtkSurfFilename.c_str(), ""); + casefile->addFileToZip(volVtkSurfZipEntry, volVtkSurfFile); + } + + if( input.volVTKOutFlag == false ) + { + VSIUnlink( input.volVTKFile.c_str() ); + VSIUnlink( volVtkSurfFile.c_str() ); + } + + } catch (exception& e) { + input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during volume VTK file writing: %s", e.what()); + } catch (...) { + input.Com->ninjaCom(ninjaComClass::ninjaWarning, "Exception caught during volume VTK file writing: Cannot determine exception type."); + } + } @@ -4127,7 +4163,7 @@ void NinjaFoam::SetMeshResolutionAndResampleDem() h = s.substr(pos+18, pos+23); } - meshResolution = atof(h.c_str()); + set_meshResolution( atof(h.c_str()), lengthUnits::meters ); } //otherwise, if the mesh resolution hasn't been set, calculate it else if(meshResolution < 0.0){ @@ -4178,7 +4214,9 @@ void NinjaFoam::SetMeshResolutionAndResampleDem() } - if ( writeMassMesh == true ) { + // write mass mesh if casefile is turned on - casefile always needs a vtk + if( writeMassMesh == true || casefile->getIsZipOpen() ) + { // need to setup mesh sizing BEFORE the dem gets resampled, but AFTER the mesh resolution gets set massMesh.set_numVertLayers(20); // done in cli.cpp calling ninja_army calling ninja calling this function, with windsim.setNumVertLayers( i_, 20); where i_ is ninjaIdx massMesh.set_meshResolution(meshResolution, meshResolutionUnits); diff --git a/src/ninja/windninja.h b/src/ninja/windninja.h index 7b84b5fa..299585a5 100644 --- a/src/ninja/windninja.h +++ b/src/ninja/windninja.h @@ -63,7 +63,12 @@ WN_C_START #include //#include +#ifdef WIN32 +#define false 0 +#define true 1 +#else #include +#endif //Use structs instead of void * for type checking by C compilier struct NinjaArmyH;