diff --git a/.builds/clang.yml b/.builds/clang.yml index 5a387c8d8..48db6096e 100644 --- a/.builds/clang.yml +++ b/.builds/clang.yml @@ -33,7 +33,7 @@ tasks: -D CMAKE_C_COMPILER=clang \ -D CMAKE_CXX_COMPILER=clang++ \ -D OPENMP_SUPPORT=OFF \ - -D NINJA_QTGUI=OFF \ + -D NINJA_GUI=OFF \ .. make -j 3 diff --git a/.builds/fedora.yml b/.builds/fedora.yml index a6b6f2a66..8fde14d7b 100644 --- a/.builds/fedora.yml +++ b/.builds/fedora.yml @@ -27,7 +27,7 @@ tasks: -D DISABLE_THREDDS_UPDATE=ON \ -D RUN_CFG_TESTS=ON \ -D STABILITY=ON \ - -D NINJA_QTGUI=OFF \ + -D NINJA_GUI=OFF \ -D CMAKE_BUILD_TYPE=debug \ .. make -j 3 diff --git a/.gitignore b/.gitignore index 412536450..fb04d8090 100644 --- a/.gitignore +++ b/.gitignore @@ -38,17 +38,13 @@ doxy.tag *.swp *~ -# Qt Creator files +# Qt Creator files & user files *.config *.creator* *.files *.includes - -# Weather model files -*UCAR* -*NCAR* -*NCEP* -*NOMADS* +*.user +*.user* # OpenFOAM *.dep diff --git a/CMakeLists.txt b/CMakeLists.txt index d40274f05..af0b08861 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,12 +15,21 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -cmake_minimum_required (VERSION 3.0) +cmake_minimum_required (VERSION 3.16) + +# Check if the user is using VCPKG on Windows +if(WIN32) + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE OR NOT CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg.cmake") + message(FATAL_ERROR + "WindNinja requires the Visual C++ Package Manager VCPKG.\n" + ) + endif() +endif(WIN32) project(WindNinja) -# let cmake figure out how to set the C++ level -set(CMAKE_CXX_STANDARD 11) +# Set C++ version for CMake +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED on) # Needed for FindNetCDF and GET_DATE() @@ -30,7 +39,7 @@ option(FIRELAB_PACKAGE "Build a windows installer with dlls from the firelab mac mark_as_advanced(FIRELAB_PACKAGE) if(FIRELAB_PACKAGE) - set(NINJA_QTGUI ON) + set(NINJA_GUI ON) set(NINJA_CLI ON) endif(FIRELAB_PACKAGE) @@ -82,96 +91,30 @@ set(WINDNINJA_VERSION_NAME WindNinja-${VER_STR}) message(STATUS "VERSION: ${VER_STR}") message(STATUS "PKG VERSION: ${WINDNINJA_VERSION_NAME}") -# Locate third party libs -include(FindBoost) -set(Boost_DEBUG OFF) -if(WIN32) +# Find Dependencies: VCPKG for Windows and ATP for Linux +if(MSVC) + find_package(shapelib CONFIG REQUIRED) + find_package(GDAL CONFIG REQUIRED) + find_package(netCDF CONFIG REQUIRED) + find_package(Boost REQUIRED COMPONENTS date_time program_options unit_test_framework) + set(GDAL_LIBRARY GDAL::GDAL) + set(NETCDF_LIBRARIES_C netCDF::netcdf) + set(Boost_DEBUG OFF) set(Boost_USE_STATIC_LIBS ON) -else(WIN32) + set(Boost_USE_MULTITHREAD OFF) +else() + include(FindBoost) + set(Boost_DEBUG OFF) set(Boost_USE_STATIC_LIBS OFF) -endif(WIN32) -set(Boost_USE_MULTITHREAD OFF) -find_package(Boost 1.41.1 COMPONENTS date_time program_options - unit_test_framework REQUIRED) - -# All GIS related libs. Windows uses GIS_INTERNALS_HOME -if(WIN32) - option(USE_GIS_INTERNALS "Use dependencies (as many as possible) from gisinternals.com" OFF) - if(USE_GIS_INTERNALS) - find_path(GIS_INTERNALS_HOME gdal CACHE PATH) - file(TO_NATIVE_PATH ${GIS_INTERNALS_HOME} GIS_INTERNALS_HOME) - string(REGEX REPLACE "\\\\" "/" GIS_INTERNALS_HOME "${GIS_INTERNALS_HOME}") - endif(USE_GIS_INTERNALS) -endif(WIN32) - -option(USE_VCPKG "Use VCPKG for boost and GDAL" ) -if(USE_VCPKG) - message("Using VCPKG") - find_package(GDAL CONFIG REQUIRED) - find_package(NETCDF CONFIG REQUIRED) -# target_link_libraries(main PRIVATE GDAL::GDAL) -ELSE(USE_VCPKG) + set(Boost_USE_MULTITHREAD OFF) + find_package(Boost 1.41.1 COMPONENTS date_time program_options unit_test_framework REQUIRED) include(FindNetCDF) include(FindGDAL) - if(WIN32) - if(NOT GIS_INTERNALS_HOME) - find_package(NetCDF REQUIRED) - find_package(GDAL REQUIRED) - else(NOT GIS_INTERNALS_HOME) - find_package(NetCDF) - find_package(GDAL) - endif(NOT GIS_INTERNALS_HOME) - else(WIN32) - find_package(NetCDF REQUIRED) - find_package(GDAL REQUIRED) - endif(WIN32) -endif(USE_VCPKG) -option(NINJA_QT5GUI "build Qt5 experimental GUI" OFF) -if(NINJA_QT5GUI) - message("building qt5 gui") - add_subdirectory(src/gui/ui) -endif(NINJA_QT5GUI) - -if(USE_VCPKG) - set(GDAL_LIBRARY GDAL::GDAL) - set(NETCDF_LIBRARIES_C netCDF::netcdf) -ELSE(USE_VCPKG) - if(WIN32) - if(NOT NETCDF_INCLUDES-NOTFOUND AND GIS_INTERNALS_HOME) - set(NETCDF_INCLUDES ${GIS_INTERNALS_HOME}/include CACHE TYPE PATH FORCE) - set(NETCDF_LIBRARIES NOT_USED CACHE TYPE PATH FORCE) - set(NETCDF_LIBRARIES_C ${GIS_INTERNALS_HOME}/lib/netcdf.lib CACHE TYPE NAME FORCE) - set(NETCDF_INCLUDES-FOUND TRUE) - set(NETCDF_LIBRARIES_C-FOUND TRUE) - endif(NOT NETCDF_INCLUDES-NOTFOUND AND GIS_INTERNALS_HOME) - endif(WIN32) - if(WIN32) - if(NOT GDAL_INCLUDE_DIR-NOTFOUND AND GIS_INTERNALS_HOME) - set(GDAL_INCLUDE_DIR ${GIS_INTERNALS_HOME}/include CACHE TYPE PATH FORCE) - set(GDAL_LIBRARY ${GIS_INTERNALS_HOME}/lib/gdal_i.lib CACHE TYPE NAME FORCE) - set(GDAL_INCLUDE_DIR-FOUND TRUE) - set(GDAL_LIBRARY-FOUND TRUE) - endif(NOT GDAL_INCLUDE_DIR-NOTFOUND AND GIS_INTERNALS_HOME) - endif(WIN32) -ENDIF(USE_VCPKG) + find_package(NetCDF) +endif(MSVC) # Ninja executables -option(NINJA_QTGUI "Build Qt GUI" ON) -if(NINJA_QTGUI) - set(QT_USE_QTMAIN_LIBRARY 1) - include(FindQt4) - find_package(Qt4 COMPONENTS QtCore QtGui QtWebKit QtNetwork REQUIRED) - include(${QT_USE_FILE}) - add_definitions(-DNINJA_GUI) - if(WIN32) - option(ENABLE_CONSOLE "Show the console for CLI/GUI runs" OFF) - option(ENABLE_FIDDLER "Use Fiddler2 proxy to monitor web traffic" OFF) - endif(WIN32) - if(ENABLE_FIDDLER) - add_definitions(-DENABLE_FIDDLER) - endif(ENABLE_FIDDLER) -endif(NINJA_QTGUI) - +option(NINJA_GUI "Build Qt GUI" ON) option(NINJA_CLI "Build Command Line Interface" ON) # Weather related options @@ -186,30 +129,37 @@ if(WITH_NOMADS_SUPPORT) if(NOT HAVE_TIMEGM) add_definitions(-DUSE_INTERNAL_TIME_GM) endif(NOT HAVE_TIMEGM) + option(NOMADS_USE_VSI_READ "Use VSI*L api instead of CPLHTTPFetch" OFF) if(NOMADS_USE_VSI_READ) add_definitions(-DNOMADS_USE_VSI_READ) endif(NOMADS_USE_VSI_READ) + option(NOMADS_RTMA "Enable the RTMA forecasts" OFF) if(NOMADS_RTMA) add_definitions(-DNOMADS_RTMA) endif(NOMADS_RTMA) + option(NOMADS_EXPER_FORECASTS "Enable NARRE and NEST forecasts." OFF) if(NOMADS_EXPER_FORECASTS) add_definitions(-DNOMADS_EXPER_FORECASTS) endif(NOMADS_EXPER_FORECASTS) + option(NOMADS_ENABLE_ASYNC "Enable multi-threading for NOMADS download" ON) if(NOMADS_ENABLE_ASYNC) add_definitions(-DNOMADS_ENABLE_ASYNC) endif(NOMADS_ENABLE_ASYNC) + option(NOMADS_USE_IP "Use the ip address for the nomads server, not a host" OFF) if(NOMADS_USE_IP) add_definitions(-DNOMADS_USE_IP) endif(NOMADS_USE_IP) + option(NOMADS_ENABLE_3D "Enable 3d initialization using NOMADS data." OFF) if(NOMADS_ENABLE_3D) add_definitions(-DNOMADS_ENABLE_3D) endif(NOMADS_ENABLE_3D) + option(NOMADS_INTERNAL_VRT "Use an internal copy of GDALAutoCreateVRT that allows for band subsetting" OFF) if(NOMADS_INTERNAL_VRT) add_definitions(-DNOMADS_INTERNAL_VRT) @@ -232,36 +182,35 @@ if(WITH_LCP_CLIENT) add_definitions(-DWITH_LCP_CLIENT) endif(WITH_LCP_CLIENT) -#disable for exception handling in the GUI and CLI -option(C_API "Enable WindNinja C API" OFF) -if(C_API) - add_definitions(-DC_API) -endif(C_API) +# Disable for exception handling in the GUI and CLI +# option(C_API "Enable WindNinja C API" OFF) +# if(C_API) +# add_definitions(-DC_API) +# endif(C_API) # Enable phone home and message server startup option(PHONE_HOME_QUERIES "Enable Phone Home Queries" ON) - if (PHONE_HOME_QUERIES) add_definitions(-DPHONE_HOME_QUERIES_ENABLED) - message(STATUS "Phone Home Queries are enabled.") -else() - message(STATUS "Phone Home Queries are disabled.") -endif() +endif(PHONE_HOME_QUERIES) # OpenFOAM related options option(NINJAFOAM "Enable OpenFOAM solver" ON) if(NINJAFOAM) - add_definitions(-DNINJAFOAM) + add_compile_definitions(NINJAFOAM) if(WIN32 AND FIRELAB_PACKAGE) - add_definitions(-DFIRELAB_PACKAGE) + add_compile_definitions(FIRELAB_PACKAGE) find_path(FOAM_MINGW_PATH OpenFOAM-2.2.x DOC "Path to x-compiled OpenFOAM directory") - if(FOAM_MINGW_PATH-NOTFOUND) - message(FATAL_ERROR "You must provide a path to a cross-compiled OpenFOAM directory for NINJAFOAM support and package building") - endif(FOAM_MINGW_PATH-NOTFOUND) - endif(WIN32 AND FIRELAB_PACKAGE) -endif(NINJAFOAM) -string(REGEX REPLACE "\\\\" "/" FOAM_MINGW_PATH "${FOAM_MINGW_PATH}") + + if(NOT FOAM_MINGW_PATH) + message(FATAL_ERROR + "You must provide a path to a cross-compiled OpenFOAM directory for NINJAFOAM support and package building") + endif() + + string(REGEX REPLACE "\\\\" "/" FOAM_MINGW_PATH "${FOAM_MINGW_PATH}") + endif() +endif() # Miscellaneous options option(NINJA_SPEED_TESTING "Enable initialization speed dampening" OFF) @@ -336,18 +285,26 @@ endif(NOT WIN32) # Optional utilities option(BUILD_FETCH_DEM "Build a standalone command line interface DEM utility" OFF) + option(BUILD_STL_CONVERTER "Build a standalone command line interface for STL file conversions" OFF ) + option(BUILD_CONVERT_OUTPUT "Build a standalone command line interface for xyz file conversions" OFF ) + option(BUILD_WRF_TO_KMZ "Build a standalone command line interface for converting WRF output to kmz" OFF ) mark_as_advanced(BUILD_WRF_TO_KMZ) + option(BUILD_HRRR_TO_KMZ "Build a standalone command line interface for converting hrrr output runs to kmz, without running full WindNinja" OFF ) mark_as_advanced(BUILD_HRRR_TO_KMZ) + option(BUILD_SLOPE_ASPECT_GRID "Build an application for building slope and aspect grids from a dem" OFF) mark_as_advanced(BUILD_SLOPE_ASPECT_GRID) + option(BUILD_FLOW_SEPARATION_GRID "Build an application for building flow separation" OFF) mark_as_advanced(BUILD_FLOW_SEPARATION_GRID) + option(BUILD_SOLAR_GRID "Build a application for building solar grids" OFF) mark_as_advanced(BUILD_SOLAR_GRID) + option(BUILD_SURFACE_INPUT_NODATA_FILLER "Build an application for filling input surface no data values" ON) mark_as_advanced(BUILD_SURFACE_INPUT_NODATA_FILLER) @@ -357,8 +314,6 @@ if(NINJA_GDAL_OUTPUT) add_definitions(-DNINJA_GDAL_OUTPUT) endif() -option(FIRE_BEHAVIOR_DATA_INSTALL "Only install files necessary for FireBehaviorModels" OFF) - # Recurse into subdirectories add_subdirectory(src) @@ -372,6 +327,26 @@ if(BUILD_TESTING) add_subdirectory(autotest) endif(BUILD_TESTING) +# Add the resource files for the .exe icon +if (MSVC) + target_sources(WindNinja PRIVATE ${PROJECT_SOURCE_DIR}/wn-ms-resource.rc) +endif() + +# Run windeployqt6.exe to provide all QT related DLLs +if(MSVC) + find_program(WINDEPLOYQT_EXE + NAMES windeployqt6 windeployqt + HINTS "${Qt6_DIR}/../../../bin" + REQUIRED + ) + + add_custom_target(deploy_windninja ALL + COMMAND "${WINDEPLOYQT_EXE}" + "$" + DEPENDS WindNinja + ) +endif() + # Package related options and settings. option(PACKAGE_DEBUG "Show some information about the package" OFF) mark_as_advanced(PACKAGE_DEBUG) @@ -382,18 +357,6 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "8") if(PACKAGE_DEBUG) message(STATUS "Building 64-bit installer") endif(PACKAGE_DEBUG) -elseif(CMAKE_SIZEOF_VOID_P MATCHES "4") - set(BUILD_64 FALSE) - add_definitions(-DNINJA_32BIT=1) - if(PACKAGE_DEBUG) - message(STATUS "Building 32-bit installer") - endif(PACKAGE_DEBUG) -else(CMAKE_SIZEOF_VOID_P MATCHES "8") - set(BUILD_64 FALSE) - add_definitions(-DNINJA_32BIT=1) - if(PACKAGE_DEBUG) - message(STATUS "Cannot determine architecture, using 32-bit by default") - endif(PACKAGE_DEBUG) endif(CMAKE_SIZEOF_VOID_P MATCHES "8") if(NOT CMAKE_BUILD_TYPE AND NOT MSVC_IDE) @@ -410,93 +373,92 @@ if(VERBOSE_CMAKE) include(CMakePrintSystemInformation) endif(VERBOSE_CMAKE) -if(FIRE_BEHAVIOR_DATA_INSTALL) - #FireBehaviorModels only requires date_time_zonespec.csv and tz_world.zip - message("Only installing data files necessary for FireBehaviorModels") - set(fire_behavior_model_files date_time_zonespec.csv tz_world.zip) - foreach(f ${fire_behavior_model_files}) - install(FILES ${PROJECT_SOURCE_DIR}/data/${f} DESTINATION - share/windninja COMPONENT apps) - endforeach(f ${fire_behavior_model_files}) - -else(FIRE_BEHAVIOR_DATA_INSTALL) -# install target related files -set(etc_files missoula_valley.tif - example_lcp.tif - cli_domainAverage.cfg - cli_domainAverage_diurnal.cfg - cli_pointInitialization_diurnal.cfg - cli_wxModelInitialization_diurnal.cfg - cli_momentumSolver_diurnal.cfg) -foreach(f ${etc_files}) - install(FILES ${PROJECT_SOURCE_DIR}/data/${f} DESTINATION - etc/windninja/example-files COMPONENT apps) -endforeach(f ${etc_files}) - -#add weather station files to example files -set(wx_station_current_files WXSTATIONS-2018-06-25-1237-missoula_valley/missoula_valley_stations_4.csv - WXSTATIONS-2018-06-25-1237-missoula_valley/KMSO-2018-06-25_1237-0.csv - WXSTATIONS-2018-06-25-1237-missoula_valley/PNTM8-2018-06-25_1237-2.csv - WXSTATIONS-2018-06-25-1237-missoula_valley/TR266-2018-06-25_1237-3.csv - WXSTATIONS-2018-06-25-1237-missoula_valley/TS934-2018-06-25_1237-1.csv) -foreach(f ${wx_station_current_files}) - install(FILES ${PROJECT_SOURCE_DIR}/data/${f} DESTINATION - etc/windninja/example-files/WXSTATIONS-2018-06-25-1237-missoula_valley COMPONENT apps) -endforeach(f ${wx_station_current_files}) - -#add weather station time series files -set(wx_station_ts_files WXSTATIONS-MDT-2018-06-20-2128-2018-06-21-2128-missoula_valley/missoula_valley_stations_4.csv - WXSTATIONS-MDT-2018-06-20-2128-2018-06-21-2128-missoula_valley/KMSO-MDT-2018-06-20_2128-2018-06-21_2128-0.csv - WXSTATIONS-MDT-2018-06-20-2128-2018-06-21-2128-missoula_valley/PNTM8-MDT-2018-06-20_2128-2018-06-21_2128-2.csv - WXSTATIONS-MDT-2018-06-20-2128-2018-06-21-2128-missoula_valley/TR266-MDT-2018-06-20_2128-2018-06-21_2128-3.csv - WXSTATIONS-MDT-2018-06-20-2128-2018-06-21-2128-missoula_valley/TS934-MDT-2018-06-20_2128-2018-06-21_2128-1.csv) -foreach(f ${wx_station_ts_files}) - install(FILES ${PROJECT_SOURCE_DIR}/data/${f} DESTINATION - etc/windninja/example-files/WXSTATIONS-MDT-2018-06-20-2128-2018-06-21-2128-missoula_valley COMPONENT apps) -endforeach(f ${wx_station_ts_files}) - -set(share_files date_time_zonespec.csv - config_options.csv - tz_world.zip - landfire.zip - map.htm - qt_certs_bundle.pem - thredds.csv - surface_data.zip - srtm_region.geojson - us_srtm_region.dbf - us_srtm_region.prj - us_srtm_region.shp - us_srtm_region.shx) -foreach(f ${share_files}) - install(FILES ${PROJECT_SOURCE_DIR}/data/${f} DESTINATION - share/windninja COMPONENT apps) -endforeach(f ${share_files}) - -# Leaflet data +# Install DEM and configuration files +set(etc_files + missoula_valley.tif + example_lcp.tif + cli_domainAverage.cfg + cli_domainAverage_diurnal.cfg + cli_pointInitialization_diurnal.cfg + cli_wxModelInitialization_diurnal.cfg + cli_momentumSolver_diurnal.cfg) +list(TRANSFORM etc_files PREPEND ${PROJECT_SOURCE_DIR}/data/) +install(FILES ${etc_files} + DESTINATION etc/windninja/example-files + COMPONENT apps) + +# Install wxstation current files (add weather station files to example files) +set(wx_station_current_files + missoula_valley_stations_4.csv + KMSO-2018-06-25_1237-0.csv + PNTM8-2018-06-25_1237-2.csv + TR266-2018-06-25_1237-3.csv + TS934-2018-06-25_1237-1.csv) +list(TRANSFORM wx_station_current_files + PREPEND ${PROJECT_SOURCE_DIR}/data/WXSTATIONS-2018-06-25-1237-missoula_valley/) +install(FILES ${wx_station_current_files} + DESTINATION etc/windninja/example-files/WXSTATIONS-2018-06-25-1237-missoula_valley + COMPONENT apps) + +# Install wxstation files (add weather station time series files) +set(wx_station_ts_files + missoula_valley_stations_4.csv + KMSO-MDT-2018-06-20_2128-2018-06-21_2128-0.csv + PNTM8-MDT-2018-06-20_2128-2018-06-21_2128-2.csv + TR266-MDT-2018-06-20_2128-2018-06-21_2128-3.csv + TS934-MDT-2018-06-20_2128-2018-06-21_2128-1.csv) +list(TRANSFORM wx_station_ts_files + PREPEND ${PROJECT_SOURCE_DIR}/data/WXSTATIONS-MDT-2018-06-20-2128-2018-06-21-2128-missoula_valley/) +install(FILES ${wx_station_ts_files} + DESTINATION etc/windninja/example-files/WXSTATIONS-MDT-2018-06-20-2128-2018-06-21-2128-missoula_valley + COMPONENT apps) + +# Install all share files +set(share_files + date_time_zonespec.csv + config_options.csv + tz_world.zip + landfire.zip + map.html + qt_certs_bundle.pem + thredds.csv + surface_data.zip + srtm_region.geojson + us_srtm_region.dbf + us_srtm_region.prj + us_srtm_region.shp + us_srtm_region.shx) +list(TRANSFORM share_files PREPEND ${PROJECT_SOURCE_DIR}/data/) +install(FILES ${share_files} + DESTINATION share/windninja + COMPONENT apps) + +# Install Leaflet data if(WIN32 AND FIRELAB_PACKAGE) - install(DIRECTORY ${PROJECT_SOURCE_DIR}/data/leaflet DESTINATION share) -else(WIN32 AND FIRELAB_PACKAGE) install(DIRECTORY ${PROJECT_SOURCE_DIR}/data/leaflet DESTINATION share/windninja) endif(WIN32 AND FIRELAB_PACKAGE) -set(doc_files CLI_instructions.pdf - displaying_wind_vectors_in_ArcGIS_Pro.pdf - download_elevation_file.pdf - fetch_dem_instructions.pdf) -foreach(f ${doc_files}) - install(FILES ${PROJECT_SOURCE_DIR}/doc/${f} DESTINATION - share/windninja/doc COMPONENT apps) -endforeach(f ${doc_files}) - -set(tutorials tutorials/WindNinja_tutorial1.pdf - tutorials/WindNinja_tutorial2.pdf - tutorials/WindNinja_tutorial3.pdf - tutorials/WindNinja_tutorial4.pdf) -foreach(f ${tutorials}) - install(FILES ${PROJECT_SOURCE_DIR}/doc/${f} DESTINATION - share/windninja/doc/tutorials COMPONENT apps) -endforeach(f ${tutorials}) +# Install all documentation files +set(doc_files + CLI_instructions.pdf + displaying_wind_vectors_in_ArcGIS_Pro.pdf + download_elevation_file.pdf + fetch_dem_instructions.pdf) +list(TRANSFORM doc_files PREPEND ${PROJECT_SOURCE_DIR}/doc/) +install(FILES ${doc_files} + DESTINATION share/windninja/doc + COMPONENT apps) + +# Install all tutorial files +set(tutorial_files + WindNinja_tutorial1.pdf + WindNinja_tutorial2.pdf + WindNinja_tutorial3.pdf + WindNinja_tutorial4.pdf) +list(TRANSFORM tutorial_files PREPEND ${PROJECT_SOURCE_DIR}/doc/tutorials/) +install(FILES ${tutorial_files} + DESTINATION share/windninja/doc/tutorials + COMPONENT apps) # Install the ninjafoam data install(DIRECTORY ${PROJECT_SOURCE_DIR}/data/ninjafoam DESTINATION share/windninja) @@ -506,18 +468,45 @@ install(FILES ${PROJECT_SOURCE_DIR}/data/wn-splash.png DESTINATION share/windnin install(FILES ${PROJECT_SOURCE_DIR}/data/relief.xml DESTINATION share/windninja) install(FILES ${PROJECT_SOURCE_DIR}/data/topofire_logo.png DESTINATION share/windninja) -set(ZIPFILES "") +# Installs specific to Windows NSIS installer +if(WIN32) + # Install DLLS for WindNinja.exe to the bin directory + install( + DIRECTORY "${CMAKE_BINARY_DIR}/src/gui/" + DESTINATION bin + FILES_MATCHING + PATTERN "*.exe" + PATTERN "*.dll" + PATTERN "plugins/*" + PATTERN "platforms/*" + PATTERN "resources/*" + PATTERN "*.qm") + + # Installs GDAL data from VCPKG to the gdal-data/data directory + install( + DIRECTORY "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-windows/share/gdal/" + DESTINATION share/windninja/gdal-data/data + COMPONENT apps) + + # Installs PROJ data from VCPKG to the proj-data/data directory + install( + DIRECTORY "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-windows/share/proj/" + DESTINATION share/windninja/proj-data/data + COMPONENT apps) + + if(NINJAFOAM) + install(DIRECTORY "${FOAM_MINGW_PATH}/etc" DESTINATION bin) + install(DIRECTORY "${FOAM_MINGW_PATH}/platforms" DESTINATION bin) + endif(NINJAFOAM) +endif(WIN32) -# GDAL data -if(WIN32 AND FIRELAB_PACKAGE AND MSVC AND GIS_INTERNALS_HOME) - install(DIRECTORY "${GIS_INTERNALS_HOME}/bin/gdal-data/" DESTINATION share/windninja/gdal-data/data) -endif(WIN32 AND FIRELAB_PACKAGE AND MSVC AND GIS_INTERNALS_HOME) +set(ZIPFILES "") if(BUILD_TYPE MATCHES "debug") if(PACKAGE_DEBUG) message(STATUS "Copying boost date time file into build/src/gui and build/src/cli") endif(PACKAGE_DEBUG) - file(COPY "${PROJECT_SOURCE_DIR}/data/date_time_zonespec.csv" DESTINATION "${PROJECT_BINARY_DIR}/src/gui") + file(COPY "${PROJECT_SOURCE_DIR}/data/date_time_zonespec.csv" DESTINATION "${PROJECT_BINARY_DIR}/src/gui/qt6") file(COPY "${PROJECT_SOURCE_DIR}/data/date_time_zonespec.csv" DESTINATION "${PROJECT_BINARY_DIR}/src/cli") endif(BUILD_TYPE MATCHES "debug") @@ -549,7 +538,7 @@ CreateDirectory \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Documents\\\" CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Tutorials\\\\Tutorial 1.lnk\\\" \\\"$INSTDIR\\\\share\\\\windninja\\\\doc\\\\tutorials\\\\WindNinja_Tutorial1.pdf\\\" CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Tutorials\\\\Tutorial 2.lnk\\\" \\\"$INSTDIR\\\\share\\\\windninja\\\\doc\\\\tutorials\\\\WindNinja_Tutorial2.pdf\\\" CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Tutorials\\\\Tutorial 3.lnk\\\" \\\"$INSTDIR\\\\share\\\\windninja\\\\doc\\\\tutorials\\\\WindNinja_Tutorial3.pdf\\\" -CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Tutorials\\\\Tutorial 4.lnk\\\" \\\"$INSTDIR\\\\share\\\\windninja\\\\doc\\\\tutorials\\\\WindNinja_Tutorial4.pdf\\\" +CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Tutorials\\\\Tutorial 4.lnk\\\" \\\"$INSTDIR\\\\share\\\\windninja\\\\doc\\\\tutorials\\\\WindNinja_Tutorial4.pdf\\\" CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Documents\\\\CLI Instructions.lnk\\\" \\\"$INSTDIR\\\\share\\\\windninja\\\\doc\\\\CLI_instructions.pdf\\\" CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Documents\\\\ArcMap Instructions.lnk\\\" \\\"$INSTDIR\\\\share\\\\windninja\\\\doc\\\\Displaying_wind_vectors_in_ArcMap.pdf\\\" CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Documents\\\\DEM Download Instructions.lnk\\\" \\\"$INSTDIR\\\\share\\\\windninja\\\\doc\\\\download_elevation_file.pdf\\\" @@ -567,74 +556,26 @@ Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Example Files.lnk\\\" RMDir \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Tutorials\\\" RMDir \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Documents\\\" ") -# Build the nsis installer on the firelab machine(s). Not for external use. -if(FIRELAB_PACKAGE AND WIN32 AND MSVC AND GIS_INTERNALS_HOME) - # Install the CURL_CA_BUNDLE file from the bin folder, and drop it in data/ - install(FILES ${GIS_INTERNALS_HOME}/bin/curl/curl-ca-bundle.crt DESTINATION - share/windninja COMPONENT apps) - # Install the dlls GDAL depends on - file(GLOB dlls ${GIS_INTERNALS_HOME}/bin/*.dll) - foreach(dll ${dlls}) - install(PROGRAMS ${dll} DESTINATION bin COMPONENT apps) - endforeach(dll ${dlls}) - install(PROGRAMS ${GIS_INTERNALS_HOME}/bin/gdal/plugins/gdal_netCDF.dll DESTINATION bin/gdalplugins COMPONENT app) - get_filename_component(qtbin ${QT_QMAKE_EXECUTABLE} DIRECTORY) - string(REGEX REPLACE "\\\\" "/" qtbin "${qtbin}") - if(BUILD_TYPE MATCHES "release") - install(PROGRAMS "${qtbin}/qtgui4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${qtbin}/qtcore4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${qtbin}/qtwebkit4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${qtbin}/phonon4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${qtbin}/qtnetwork4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${QT_PLUGINS_DIR}/imageformats/qjpeg4.dll" DESTINATION bin/imageformats) - else(BUILD_TYPE MATCHES "release") - install(PROGRAMS "${qtbin}/qtguid4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${qtbin}/qtcored4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${qtbin}/qtwebkitd4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${qtbin}/phonond4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${qtbin}/qtnetworkd4.dll" DESTINATION bin COMPONENT apps) - install(PROGRAMS "${QT_PLUGINS_DIR}/imageformats/qjpegd4.dll" DESTINATION bin/imageformats) - endif( BUILD_TYPE MATCHES "release") - # HACK FOR OPENMP - if(BUILD_64) - install(PROGRAMS ${MSVC_REDIST_DIR}/x64/Microsoft.VC100.OPENMP/vcomp100.dll DESTINATION bin COMPONENT apps) - else(BUILD_64) - install(PROGRAMS ${MSVC_REDIST_DIR}/x86/Microsoft.VC100.OPENMP/vcomp100.dll DESTINATION bin COMPONENT apps) - endif(BUILD_64) - if(NINJAFOAM) - install(DIRECTORY "${FOAM_MINGW_PATH}/etc" DESTINATION bin) - install(DIRECTORY "${FOAM_MINGW_PATH}/platforms" DESTINATION bin) - endif(NINJAFOAM) -endif(FIRELAB_PACKAGE AND WIN32 AND MSVC AND GIS_INTERNALS_HOME) set(CPACK_COMPONENT_APPS_REQUIRED TRUE) set(CPACK_COMPONENT_LIBS_HIDDEN TRUE) set(CPACK_COMPONENT_LIBS_DISABLED TRUE) set(CPACK_COMPONENT_INCLUDES_HIDDEN TRUE) set(CPACK_COMPONENT_INCLUDES_DISABLED TRUE) -# ***************************************************************************** # CPack call -# ***************************************************************************** include(CPack) include(CTest) -# ***************************************************************************** -# CPack groupings -# ***************************************************************************** +# CPack Groupings CPACK_ADD_COMPONENT(apps DISPLAY_NAME "WindNinja" DESCRIPTION "WindNinja binaries and documentation" - GROUP Application - ) + GROUP Application) + CPACK_ADD_COMPONENT(libs DISPLAY_NAME "WindNinja Libraries" - DESCRIPTION "Development libraries" - #GROUP Development - ) + DESCRIPTION "Development libraries") + CPACK_ADD_COMPONENT(includes DISPLAY_NAME "WindNinja Headers" - DESCRIPTION "Development include files" - #GROUP Development - ) - -endif(FIRE_BEHAVIOR_DATA_INSTALL) + DESCRIPTION "Development include files") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..fbe84cedd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,169 @@ +# Contributing to WindNinja + +Thank you for considering to contribute to WindNinja. As a crucial tool used in fire management, we appreciate any support to help the project. + +Please see the guides below for common contributions: +- Ask a question +- Report an issue +- Request a feature +- Write and run tests - coming soon + + +### Explain contributions you are NOT looking for (if any). + +Again, defining this up front means less work for you. If someone ignores your guide and submits something you don’t want, you can simply close it and point to your policy. + +> Please, don't use the issue tracker for [support questions]. Check whether the #pocoo IRC channel on Freenode can help with your issue. If your problem is not strictly Werkzeug or Flask specific, #python is generally more active. Stack Overflow is also worth considering. + +[source: [Flask](https://github.com/pallets/flask/blob/master/CONTRIBUTING.rst)] **Need more inspiration?** [1] [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CONTRIBUTING.md#about-to-create-a-new-github-issue) [2] [Read the Docs](http://read-the-docs.readthedocs.org/en/latest/open-source-philosophy.html#unsupported) + +# Ground Rules +### Set expectations for behavior (yours, and theirs). +This includes not just how to communicate with others (being respectful, considerate, etc) but also technical responsibilities (importance of testing, project dependencies, etc). Mention and link to your code of conduct, if you have one. + +> Responsibilities +> * Ensure cross-platform compatibility for every change that's accepted. Windows, Mac, Debian & Ubuntu Linux. +> * Ensure that code that goes into core meets all requirements in this checklist: https://gist.github.com/audreyr/4feef90445b9680475f2 +> * Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback. +> * Don't add any classes to the codebase unless absolutely needed. Err on the side of using functions. +> * Keep feature versions as small as possible, preferably one new feature per version. +> * Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/). + +[source: [cookiecutter](https://github.com/audreyr/cookiecutter/blob/master/CONTRIBUTING.rst)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#community-code-of-conduct) [2] [geocoder](https://github.com/alexreisner/geocoder#contributing) + +# Your First Contribution +Help people who are new to your project understand where they can be most helpful. This is also a good time to let people know if you follow a label convention for flagging beginner issues. + +> Unsure where to begin contributing to Atom? You can start by looking through these beginner and help-wanted issues: +> Beginner issues - issues which should only require a few lines of code, and a test or two. +> Help wanted issues - issues which should be a bit more involved than beginner issues. +> Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have. + +[source: [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#your-first-code-contribution)] **Need more inspiration?** [1] [Read the Docs](http://docs.readthedocs.org/en/latest/contribute.html#contributing-to-development) [2] [Django](https://docs.djangoproject.com/en/dev/internals/contributing/new-contributors/#first-steps) (scroll down to "Guidelines" as well) + +### Bonus points: Add a link to a resource for people who have never contributed to open source before. +Here are a couple of friendly tutorials you can include: http://makeapullrequest.com/ and http://www.firsttimersonly.com/ + +> Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). + +[source: [React](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#pull-requests)] + +As a side note, it helps to use newcomer-friendly language throughout the rest of your document. Here are a couple of examples from [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md): + +>At this point, you're ready to make your changes! Feel free to ask for help; everyone is a beginner at first :smile_cat: +> +>If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your branch so it's easier to merge. + +# Getting started +### Give them a quick walkthrough of how to submit a contribution. +How you write this is up to you, but some things you may want to include: + +* Let them know if they need to sign a CLA, agree to a DCO, or get any other legal stuff out of the way +* If tests are required for contributions, let them know, and explain how to run the tests +* If you use anything other than GitHub to manage issues (ex. JIRA or Trac), let them know which tools they’ll need to contribute + +>For something that is bigger than a one or two line fix: + +>1. Create your own fork of the code +>2. Do the changes in your fork +>3. If you like the change and think the project could use it: + * Be sure you have followed the code style for the project. + * Sign the Contributor License Agreement, CLA, with the jQuery Foundation. + * Note the jQuery Foundation Code of Conduct. + * Send a pull request indicating that you have a CLA on file. + +[source: [Requirejs](http://requirejs.org/docs/contributing.html)] **Need more inspiration?** [1] [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md#1-where-do-i-go-from-here) [2] [Node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#code-contributions) [3] [Ember.js](https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md#pull-requests) + +### If you have a different process for small or "obvious" fixes, let them know. + +> Small contributions such as fixing spelling errors, where the content is small enough to not be considered intellectual property, can be submitted by a contributor as a patch, without a CLA. +> +>As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following: +>* Spelling / grammar fixes +>* Typo correction, white space and formatting changes +>* Comment clean up +>* Bug fixes that change default return values or error codes stored in constants +>* Adding logging messages or debugging output +>* Changes to ‘metadata’ files like Gemfile, .gitignore, build scripts, etc. +>* Moving source files from one directory or package to another + +[source: [Chef](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#chef-obvious-fix-policy)] **Need more inspiration?** [1] [Puppet](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md#making-trivial-changes) + +# How to report a bug +### Explain security disclosures first! +At bare minimum, include this sentence: +> If you find a security vulnerability, do NOT open an issue. Email XXXX instead. + +If you don’t want to use your personal contact information, set up a “security@” email address. Larger projects might have more formal processes for disclosing security, including encrypted communication. (Disclosure: I am not a security expert.) + +> Any security issues should be submitted directly to security@travis-ci.org +> In order to determine whether you are dealing with a security issue, ask yourself these two questions: +> * Can I access something that's not mine, or something I shouldn't have access to? +> * Can I disable something for other people? +> +> If the answer to either of those two questions are "yes", then you're probably dealing with a security issue. Note that even if you answer "no" to both questions, you may still be dealing with a security issue, so if you're unsure, just email us at security@travis-ci.org. + +[source: [Travis CI](https://github.com/travis-ci/travis-ci/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#security) [2] [Express.js](https://github.com/expressjs/express/blob/master/Security.md) + +### Tell your contributors how to file a bug report. +You can even include a template so people can just copy-paste (again, less work for you). + +> When filing an issue, make sure to answer these five questions: +> +> 1. What version of Go are you using (go version)? +> 2. What operating system and processor architecture are you using? +> 3. What did you do? +> 4. What did you expect to see? +> 5. What did you see instead? +> General questions should go to the golang-nuts mailing list instead of the issue tracker. The gophers there will answer or ask you to file an issue if you've tripped over a bug. + +[source: [Go](https://github.com/golang/go/blob/master/CONTRIBUTING.md#filing-issues)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#other-bugs ) [2] [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#reporting-bugs) (includes template) + +# How to suggest a feature or enhancement +### If you have a particular roadmap, goals, or philosophy for development, share it here. +This information will give contributors context before they make suggestions that may not align with the project’s needs. + +> The Express philosophy is to provide small, robust tooling for HTTP servers, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs. +> +> Express does not force you to use any specific ORM or template engine. With support for over 14 template engines via Consolidate.js, you can quickly craft your perfect framework. + +[source: [Express](https://github.com/expressjs/express#philosophy)] **Need more inspiration?** [Active Admin](https://github.com/activeadmin/activeadmin#goals) + +### Explain your desired process for suggesting a feature. +If there is back-and-forth or signoff required, say so. Ask them to scope the feature, thinking through why it’s needed and how it might work. + +> If you find yourself wishing for a feature that doesn't exist in Elasticsearch, you are probably not alone. There are bound to be others out there with similar needs. Many of the features that Elasticsearch has today have been added because our users saw the need. Open an issue on our issues list on GitHub which describes the feature you would like to see, why you need it, and how it should work. + +[source: [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#feature-requests)] **Need more inspiration?** [1] [Hoodie](https://github.com/hoodiehq/hoodie/blob/master/CONTRIBUTING.md#feature-requests) [2] [Ember.js](https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md#requesting-a-feature) + +# Code review process +### Explain how a contribution gets accepted after it’s been submitted. +Who reviews it? Who needs to sign off before it’s accepted? When should a contributor expect to hear from you? How can contributors get commit access, if at all? + +> The core team looks at Pull Requests on a regular basis in a weekly triage meeting that we hold in a public Google Hangout. The hangout is announced in the weekly status updates that are sent to the puppet-dev list. Notes are posted to the Puppet Community community-triage repo and include a link to a YouTube recording of the hangout. +> After feedback has been given we expect responses within two weeks. After two weeks we may close the pull request if it isn't showing any activity. + +[source: [Puppet](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md#submitting-changes)] **Need more inspiration?** [1] [Meteor](https://meteor.hackpad.com/Responding-to-GitHub-Issues-SKE2u3tkSiH ) [2] [Express.js](https://github.com/expressjs/express/blob/master/Contributing.md#becoming-a-committer) + +# Community +If there are other channels you use besides GitHub to discuss contributions, mention them here. You can also list the author, maintainers, and/or contributors here, or set expectations for response time. + +> You can chat with the core team on https://gitter.im/cucumber/cucumber. We try to have office hours on Fridays. + +[source: [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CONTRIBUTING.md#talking-with-other-devs)] **Need more inspiration?** + [1] [Chef](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#-developer-office-hours) [2] [Cookiecutter](https://github.com/audreyr/cookiecutter#community) + +# BONUS: Code, commit message and labeling conventions +These sections are not necessary, but can help streamline the contributions you receive. + +### Explain your preferred style for code, if you have any. + +**Need inspiration?** [1] [Requirejs](http://requirejs.org/docs/contributing.html#codestyle) [2] [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#contributing-to-the-elasticsearch-codebase) + +### Explain if you use any commit message conventions. + +**Need inspiration?** [1] [Angular](https://github.com/angular/material/blob/master/.github/CONTRIBUTING.md#submit) [2] [Node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#step-3-commit) + +### Explain if you use any labeling conventions for issues. + +**Need inspiration?** [1] [StandardIssueLabels](https://github.com/wagenet/StandardIssueLabels#standardissuelabels) [2] [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#issue-and-pull-request-labels) \ No newline at end of file diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 513d04777..945a16042 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -31,3 +31,4 @@ Tomàs Margalef Hannah Gedlaman Nicholas Kim Rui Zhang +Jack Hayward diff --git a/autotest/CMakeLists.txt b/autotest/CMakeLists.txt index 744292886..2694b6f37 100644 --- a/autotest/CMakeLists.txt +++ b/autotest/CMakeLists.txt @@ -15,7 +15,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -#cmake_minimum_required(VERSION 2.6) +#cmake_minimum_required(VERSION 3.16) include_directories(${PROJECT_SOURCE_DIR}/src/ninja ${NETCDF_INCLUDES} @@ -129,11 +129,11 @@ if(WITH_LCP_CLIENT) endif(WITH_LCP_CLIENT) # input_points Test Suite -if(NOT NINJA_QTGUI) +if(NOT NINJA_GUI) # This test doesn't work #add_test(test_input_points_mackay # ${EXECUTABLE_OUTPUT_PATH}/test_main --run_test=input_points/mackay ) -endif(NOT NINJA_QTGUI) +endif(NOT NINJA_GUI) if(NINJA_GDAL_OUTPUT) add_test(test_gdal_output_shapefile diff --git a/autotest/api/test_capi_domain_average_wind.c b/autotest/api/test_capi_domain_average_wind.c index 4898cfd57..98933eb1e 100644 --- a/autotest/api/test_capi_domain_average_wind.c +++ b/autotest/api/test_capi_domain_average_wind.c @@ -38,36 +38,42 @@ int main() * Setting up the simulation */ NinjaArmyH* ninjaArmy = NULL; - const char * comType = "cli"; //communication type is always set to "cli" - const int nCPUs = 1; + const int nCPUs = 3; + const char * runType = "C-API autotest"; char ** papszOptions = NULL; - NinjaErr err = 0; - err = NinjaInit(papszOptions); //initialize global singletons and environments (GDAL_DATA, etc.) + NinjaErr err = 0; + err = NinjaInit(runType, papszOptions); //initialize global singletons and environments (GDAL_DATA, etc.) if(err != NINJA_SUCCESS) { - printf("NinjaInit: err = %d\n", err); + printf("NinjaInit: err = %d\n", err); + } + + /* + * Setting up a log file, for ninjaCom, if desired + */ + FILE* multiStream = NULL; + multiStream = fopen("/home/atw09001/src/wind/windninja/autotest/api/data/output/ninja.log", "w+"); + if(multiStream == NULL) + { + printf("error opening log file\n"); } /* * Set up domain average run */ /* inputs that do not vary among ninjas in an army */ - const char * demFile = "/home/mason/Documents/Git/WindNinja/windninja/autotest/api/data/missoula_valley.tif"; + const char * demFile = "/home/atw09001/src/wind/windninja/autotest/api/data/missoula_valley.tif"; const char * initializationMethod = "domain_average"; const char * meshChoice = "coarse"; const char * vegetation = "grass"; const int nLayers = 20; //layers in the mesh - const int diurnalFlag = 0; //diurnal slope wind parameterization not used + const int diurnalFlag = 1; //diurnal slope wind parameterization not used const double height = 10.0; const char * heightUnits = "m"; - bool momentumFlag = 0; //we're using the conservation of mass solver + //bool momentumFlag = 0; //we're using the conservation of mass solver + bool momentumFlag = 1; //we're using the conservation of momentum solver unsigned int numNinjas = 2; //two ninjas in the ninjaArmy - must be equal to array sizes - /* inputs that can vary among ninjas in an army */ - const double speedList[] = {5.5, 5.5}; // matches the size of numNinjas - const char * speedUnits = "mps"; - const double directionList[] = {220, 300}; // matches the size of numNinjas - /* inputs specific to output * Note: Outputs have default values if inputs are not specified (like resolution) */ @@ -75,16 +81,55 @@ int main() const char * units = "m"; const double width = 1.0; const char * scaling = "equal_color"; - const char * outputPath = "/home/mason/Documents/Git/WindNinja/windninja/autotest/api/data/output"; + const char * outputPath = "/home/atw09001/src/wind/windninja/autotest/api/data/output"; const bool outputFlag = 1; - /* - * Create the army + /* inputs that can vary among ninjas in an army */ + const double speedList[] = {5.5, 5.5}; // matches the size of numNinjas + const char * speedUnits = "mps"; + const double directionList[2] = {220.0, 300.0}; + const int year[2] = {2023, 2023}; + const int month[2] = {10, 11}; + const int day[2] = {10, 11}; + const int hour[2] = {12, 13}; + const int minute[2] = {30, 31}; + const char * timezone = "UTC"; + const double air[2] = {50.0, 50.0}; + const char * airUnits = "F"; + const double cloud[2] = {10.0, 10.0}; + const char * cloudUnits = "percent"; + + const double meshResolution = -1.0; //set to value > 0.0 to override meshChoice with meshResolution value + //const double meshResolution = 300.0; + const char * meshResolutionUnits = "m"; + + bool matchedPoints = true; // for point initialization, but currently required as an input to SetInitializationMethod(). Should the match points pointInitialization algorythm be run, or should it just run as a domainAvgRun on the input wind field. ALWAYS set to true unless you know what you are doing. + + /* + * Initialize the army */ - ninjaArmy = NinjaMakeDomainAverageArmy(numNinjas, momentumFlag, speedList, speedUnits, directionList, papszOptions); + ninjaArmy = NinjaInitializeArmy(); if( NULL == ninjaArmy ) { - printf("NinjaCreateArmy: ninjaArmy = NULL\n"); + printf("NinjaInitializeArmy: ninjaArmy = NULL\n"); + } + + /* + * Customize the ninja communication + */ + err = NinjaSetArmyMultiComStream(ninjaArmy, multiStream, papszOptions); + if(err != NINJA_SUCCESS) + { + printf("NinjaSetArmyMultiComStream: err = %d\n", err); + } + + /* + * Create the army + */ + err = NinjaMakeDomainAverageArmy(ninjaArmy, numNinjas, momentumFlag, speedList, speedUnits, directionList, year, month, day, hour, minute, timezone, air, airUnits, cloud, cloudUnits, papszOptions); + if( err != NINJA_SUCCESS) + { + printf("NinjaMakeDomainAverageArmy: err = %d\n", err); } /* @@ -95,19 +140,13 @@ int main() /* * Sets Simulation Variables */ - err = NinjaSetCommunication(ninjaArmy, i, comType, papszOptions); - if(err != NINJA_SUCCESS) - { - printf("NinjaSetCommunication: err = %d\n", err); - } - err = NinjaSetNumberCPUs(ninjaArmy, i, nCPUs, papszOptions); if(err != NINJA_SUCCESS) { printf("NinjaSetNumberCPUs: err = %d\n", err); } - err = NinjaSetInitializationMethod(ninjaArmy, i, initializationMethod, papszOptions); + err = NinjaSetInitializationMethod(ninjaArmy, i, initializationMethod, matchedPoints, papszOptions); if(err != NINJA_SUCCESS) { printf("NinjaSetInitializationMethod: err = %d\n", err); @@ -154,13 +193,24 @@ int main() { printf("NinjaSetUniVegetation: err = %d\n", err); } - - err = NinjaSetMeshResolutionChoice(ninjaArmy, i, meshChoice, papszOptions); - if(err != NINJA_SUCCESS) + + if( meshResolution > 0.0 ) { + err = NinjaSetMeshResolution(ninjaArmy, i, meshResolution, meshResolutionUnits, papszOptions); + if(err != NINJA_SUCCESS) + { + printf("NinjaSetMeshResolution: err = %d\n", err); + } + } + else // meshResolution not set, use meshChoice + { + err = NinjaSetMeshResolutionChoice(ninjaArmy, i, meshChoice, papszOptions); + if(err != NINJA_SUCCESS) + { printf("NinjaSetMeshResolutionChoice: err = %d\n", err); + } } - + err = NinjaSetNumVertLayers(ninjaArmy, i, nLayers, papszOptions); if(err != NINJA_SUCCESS) { @@ -185,6 +235,14 @@ int main() { printf("NinjaDestroyRuns: err = %d\n", err); } - + + if(multiStream != NULL) + { + if(fclose(multiStream) != 0) + { + printf("error closing log file\n"); + } + } + return NINJA_SUCCESS; } diff --git a/autotest/api/test_capi_fetching.c b/autotest/api/test_capi_fetching.c index 989a8a35c..68c37311f 100644 --- a/autotest/api/test_capi_fetching.c +++ b/autotest/api/test_capi_fetching.c @@ -53,9 +53,9 @@ int main() /* * Testing fetching from a DEM bounding box */ - const char * demFileBBox = "data/fetch/DEMBBox.tif"; // output file name - char * fetch_type = "gmted"; // can be srtm, gmted, relief - double resolution = 30; // 30 m resolution + const char * demFileBBox = "/home/atw09001/src/wind/windninja/autotest/api/data/fetch/DEMBBox.tif"; // output file name + char * fetch_type = "lcp"; // can be srtm, gmted, relief + double resolution = 30.0; // 30 m resolution double boundsBox [] = {40.07, -104.0, 40.0, -104.07}; // Bounding box (north, east, south, west) err = NinjaFetchDEMBBox(ninjaArmy, boundsBox, demFileBBox, resolution, fetch_type, papszOptions); if (err != NINJA_SUCCESS){ @@ -112,8 +112,8 @@ int main() int hour[2] = {2, 2}; int minute[2] = {2, 2}; int size = 2; - const char* output_path = "data/fetch/"; - const char* elevation_file = "data/missoula_valley.tif"; + const char* output_path = "/home/atw09001/src/wind/windninja/autotest/api/data/fetch/"; + const char* elevation_file = "/home/atw09001/src/wind/windninja/autotest/api/data/missoula_valley.tif"; const char* osTimeZone = "UTC"; bool fetchLatestFlag = 1; double buffer = 10; @@ -129,11 +129,11 @@ int main() /* * Testing fetching from a DEM point */ - double adfPoint[] = {104.0, 40.07}; // Point coordinates (longitude, latitude) - double adfBuff[] = {30, 30}; // Buffer to store the elevation value + double adfPoint[] = {-104.0, 40.07}; // Point coordinates (longitude, latitude) + double adfBuff[] = {1.5, 1.5}; // Buffer to store the elevation value double dfCellSize = 30.0; // Cell size in meters - char* pszDstFile = "data/fetch/DEMpoint.tif"; - char* fetchType = "gmted"; + char* pszDstFile = "/home/atw09001/src/wind/windninja/autotest/api/data/fetch/DEMpoint.tif"; + char* fetchType = "lcp"; err = NinjaFetchDEMPoint(ninjaArmy, adfPoint, adfBuff, units, dfCellSize, pszDstFile, fetchType, papszOptions); if (err != NINJA_SUCCESS) { printf("NinjaFetchDemPoint: err = %d\n", err); diff --git a/cmake/Modules/GetDate.cmake b/cmake/Modules/GetDate.cmake index 3808bd403..40f9d9362 100644 --- a/cmake/Modules/GetDate.cmake +++ b/cmake/Modules/GetDate.cmake @@ -1,4 +1,4 @@ -#cmake_minimum_required(VERSION 2.8) +#cmake_minimum_required(VERSION 3.16) macro(GET_DATE) # diff --git a/data/leaflet/L.Control.Layers.Tree.css b/data/leaflet/L.Control.Layers.Tree.css new file mode 100644 index 000000000..aa0334d41 --- /dev/null +++ b/data/leaflet/L.Control.Layers.Tree.css @@ -0,0 +1,33 @@ +.leaflet-control-layers-toggle.leaflet-layerstree-named-toggle { + margin: 2px 5px; + width: auto; + height: auto; + background-image: none; +} + +.leaflet-layerstree-header input { + margin-left: 0px; +} + +.leaflet-layerstree-header label { + display: inline-block; + cursor: pointer; +} + +.leaflet-layerstree-header-pointer, +.leaflet-layerstree-expand-collapse { + cursor: pointer; +} + +.leaflet-layerstree-children { + padding-left: 10px; +} + +.leaflet-layerstree-children-nopad { + padding-left: 0px; +} + +.leaflet-layerstree-hide, +.leaflet-layerstree-nevershow { + display: none; +} diff --git a/data/leaflet/L.Control.Layers.Tree.js b/data/leaflet/L.Control.Layers.Tree.js new file mode 100644 index 000000000..cfd08aa19 --- /dev/null +++ b/data/leaflet/L.Control.Layers.Tree.js @@ -0,0 +1,530 @@ +(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('leaflet')) : + typeof define === 'function' && define.amd ? define(['exports', 'leaflet'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.LayersTree = {}, global.L)); +})(this, (function(_exports, L) { 'use strict'; + + /* + * Control like L.Control.Layers, but showing layers in a tree. + * Do not forget to include the css file. + */ + + if (typeof L === 'undefined') { + throw new Error('Leaflet must be included first'); + } + + /* + * L.Control.Layers.Tree extends L.Control.Layers because it reuses + * most of its functionality. Only the HTML creation is different. + */ + L.Control.Layers.Tree = L.Control.Layers.extend({ + options: { + closedSymbol: '+', + openedSymbol: '−', + spaceSymbol: ' ', + selectorBack: false, + namedToggle: false, + collapseAll: '', + expandAll: '', + labelIsSelector: 'both', + }, + + // Class names are error prone texts, so write them once here + _initClassesNames: function() { + this.cls = { + children: 'leaflet-layerstree-children', + childrenNopad: 'leaflet-layerstree-children-nopad', + hide: 'leaflet-layerstree-hide', + closed: 'leaflet-layerstree-closed', + opened: 'leaflet-layerstree-opened', + space: 'leaflet-layerstree-header-space', + pointer: 'leaflet-layerstree-header-pointer', + header: 'leaflet-layerstree-header', + neverShow: 'leaflet-layerstree-nevershow', + node: 'leaflet-layerstree-node', + name: 'leaflet-layerstree-header-name', + label: 'leaflet-layerstree-header-label', + selAllCheckbox: 'leaflet-layerstree-sel-all-checkbox', + }; + }, + + initialize: function(baseTree, overlaysTree, options) { + this._scrollTop = 0; + this._initClassesNames(); + this._baseTree = null; + this._overlaysTree = null; + L.Util.setOptions(this, options); + L.Control.Layers.prototype.initialize.call(this, null, null, options); + this._setTrees(baseTree, overlaysTree); + }, + + setBaseTree: function(tree) { + return this._setTrees(tree); + }, + + setOverlayTree: function(tree) { + return this._setTrees(undefined, tree); + }, + + addBaseLayer: function(_layer, _name) { + throw 'addBaseLayer is disabled'; + }, + + addOverlay: function(_layer, _name) { + throw 'addOverlay is disabled'; + }, + + removeLayer: function(_layer) { + throw 'removeLayer is disabled'; + }, + + collapse: function() { + this._scrollTop = this._sect().scrollTop; + return L.Control.Layers.prototype.collapse.call(this); + }, + + expand: function() { + L.Control.Layers.prototype.expand.call(this); + this._sect().scrollTop = this._scrollTop; + }, + + onAdd: function(map) { + function changeName(layer) { + if (layer._layersTreeName) { + toggle.innerHTML = layer._layersTreeName; + } + } + + var ret = L.Control.Layers.prototype.onAdd.call(this, map); + if (this.options.namedToggle) { + var toggle = this._container.getElementsByClassName('leaflet-control-layers-toggle')[0]; + L.DomUtil.addClass(toggle, 'leaflet-layerstree-named-toggle'); + // Start with this value... + map.eachLayer(function(layer) {changeName(layer);}); + // ... and change it whenever the baselayer is changed. + map.on('baselayerchange', function(e) {changeName(e.layer);}, this); + } + return ret; + }, + + // Expands the whole tree (base other overlays) + expandTree: function(overlay) { + var container = overlay ? this._overlaysList : this._baseLayersList; + if (container) { + this._applyOnTree(container, false); + } + return this._localExpand(); + }, + + // Collapses the whole tree (base other overlays) + collapseTree: function(overlay) { + var container = overlay ? this._overlaysList : this._baseLayersList; + if (container) { + this._applyOnTree(container, true); + } + return this._localExpand(); + }, + + // Expands the tree, only to show the selected inputs + expandSelected: function(overlay) { + function iter(el) { + // Function to iterate the whole DOM upwards + var p = el.parentElement; + if (p) { + if (L.DomUtil.hasClass(p, that.cls.children) && + !L.DomUtil.hasClass(el, that.cls.childrenNopad)) { + L.DomUtil.removeClass(p, hide); + } + + if (L.DomUtil.hasClass(p, that.cls.node)) { + var h = p.getElementsByClassName(that.cls.header)[0]; + that._applyOnTree(h, false); + } + iter(p); + } + } + + var that = this; + var container = overlay ? this._overlaysList : this._baseLayersList; + if (!container) return this; + var hide = this.cls.hide; + var inputs = this._layerControlInputs || container.getElementsByTagName('input'); + for (var i = 0; i < inputs.length; i++) { + // Loop over every (valid) input. + var input = inputs[i]; + if (this._getLayer && !!this._getLayer(input.layerId).overlay != !!overlay) continue; + if (input.checked) { + // Get out of the header, + // to not open the possible (but rare) children + iter(input.parentElement.parentElement.parentElement.parentElement); + } + } + return this._localExpand(); + }, + + // "private" methods, not exposed in the API + _sect: function() { + // to keep compatibility after 1.3 https://github.com/Leaflet/Leaflet/pull/6380 + return this._section || this._form; + }, + + _setTrees: function(base, overlays) { + var id = 0; // to keep unique id + function iterate(tree, output, overlays) { + if (tree && tree.layer) { + if (!overlays) { + tree.layer._layersTreeName = tree.name || tree.label; + } + output[id++] = tree.layer; + } + if (tree && tree.children && tree.children.length) { + tree.children.forEach(function(child) { + iterate(child, output, overlays); + }); + } + return output; + } + + // We accept arrays, but convert into an object with children + function forArrays(input) { + if (Array.isArray(input)) { + return {noShow: true, children: input}; + } else { + return input; + } + } + + // Clean everything, and start again. + if (this._layerControlInputs) { + this._layerControlInputs = []; + } + for (var i = 0; i < this._layers.length; ++i) { + this._layers[i].layer.off('add remove', this._onLayerChange, this); + } + this._layers = []; + + if (base !== undefined) this._baseTree = forArrays(base); + if (overlays !== undefined) this._overlaysTree = forArrays(overlays); + + var bflat = iterate(this._baseTree, {}); + for (var j in bflat) { + this._addLayer(bflat[j], j); + } + + var oflat = iterate(this._overlaysTree, {}, true); + for (var k in oflat) { + this._addLayer(oflat[k], k, true); + } + return (this._map) ? this._update() : this; + }, + + // Used to update the vertical scrollbar + _localExpand: function() { + if (this._map && L.DomUtil.hasClass(this._container, 'leaflet-control-layers-expanded')) { + var top = this._sect().scrollTop; + this.expand(); + this._sect().scrollTop = top; // to keep the scroll location + this._scrollTop = top; + } + return this; + }, + + // collapses or expands the tree in the container. + _applyOnTree: function(container, collapse) { + var iters = [ + {cls: this.cls.children, hide: collapse}, + {cls: this.cls.opened, hide: collapse}, + {cls: this.cls.closed, hide: !collapse}, + ]; + iters.forEach(function(it) { + var els = container.getElementsByClassName(it.cls); + for (var i = 0; i < els.length; i++) { + var el = els[i]; + if (L.DomUtil.hasClass(el, this.cls.childrenNopad)) { + // do nothing + } else if (it.hide) { + L.DomUtil.addClass(el, this.cls.hide); + } else { + L.DomUtil.removeClass(el, this.cls.hide); + } + } + }, this); + }, + + // it is called in the original _update, and shouldn't do anything. + _addItem: function(_obj) { + }, + + // overwrite _update function in Control.Layers + _update: function() { + if (!this._container) { return this; } + L.Control.Layers.prototype._update.call(this); + this._addTreeLayout(this._baseTree, false); + this._addTreeLayout(this._overlaysTree, true); + return this._localExpand(); + }, + + // Create the DOM objects for the tree + _addTreeLayout: function(tree, overlay) { + if (!tree) return; + var container = overlay ? this._overlaysList : this._baseLayersList; + this._expandCollapseAll(overlay, this.options.collapseAll, this.collapseTree); + this._expandCollapseAll(overlay, this.options.expandAll, this.expandTree); + this._iterateTreeLayout(tree, container, overlay, [], tree.noShow); + if (this._checkDisabledLayers) { + // to keep compatibility + this._checkDisabledLayers(); + } + }, + + // Create the "Collapse all" or expand, if needed. + _expandCollapseAll: function(overlay, text, fn, ctx) { + var container = overlay ? this._overlaysList : this._baseLayersList; + ctx = ctx ? ctx : this; + if (text) { + var o = document.createElement('div'); + o.className = 'leaflet-layerstree-expand-collapse'; + container.appendChild(o); + o.innerHTML = text; + o.tabIndex = 0; + L.DomEvent.on(o, 'click keydown', function(e) { + if (e.type !== 'keydown' || e.keyCode === 32) { + o.blur(); + fn.call(ctx, overlay); + this._localExpand(); + } + }, this); + } + }, + + // recursive function to create the DOM children + _iterateTreeLayout: function(tree, container, overlay, selAllNodes, noShow) { + if (!tree) return; + function creator(type, cls, append, innerHTML) { + var obj = L.DomUtil.create(type, cls, append); + if (innerHTML) obj.innerHTML = innerHTML; + return obj; + } + + // create the header with it fields + var header = creator('div', this.cls.header, container); + var sel = creator('span'); + var entry = creator('span'); + var closed = creator('span', this.cls.closed, sel, this.options.closedSymbol); + var opened = creator('span', this.cls.opened, sel, this.options.openedSymbol); + var space = creator('span', this.cls.space, null, this.options.spaceSymbol); + if (this.options.selectorBack) { + sel.insertBefore(space, closed); + header.appendChild(entry); + header.appendChild(sel); + } else { + sel.appendChild(space); + header.appendChild(sel); + header.appendChild(entry); + } + + function updateSelAllCheckbox(ancestor) { + var selector = ancestor.querySelector('input[type=checkbox]'); + var selectedAll = true; + var selectedNone = true; + var inputs = ancestor.querySelectorAll('input[type=checkbox]'); + [].forEach.call(inputs, function(inp) { // to work in node for tests + if (inp === selector) { + // ignore + } else if (inp.indeterminate) { + selectedAll = false; + selectedNone = false; + } else if (inp.checked) { + selectedNone = false; + } else if (!inp.checked) { + selectedAll = false; + } + }); + if (selectedAll) { + selector.indeterminate = false; + selector.checked = true; + } else if (selectedNone) { + selector.indeterminate = false; + selector.checked = false; + } else { + selector.indeterminate = true; + selector.checked = false; + } + } + + function manageSelectorsAll(input, ctx) { + selAllNodes.forEach(function(ancestor) { + L.DomEvent.on(input, 'click', function(_ev) { + updateSelAllCheckbox(ancestor); + }, ctx); + }, ctx); + } + + var selAll; + if (tree.selectAllCheckbox) { + selAll = this._createCheckboxElement(false); + selAll.className += ' ' + this.cls.selAllCheckbox; + } + + var hide = this.cls.hide; // To toggle state + // create the children group, with the header event click + if (tree.children) { + var children = creator('div', this.cls.children, container); + var sensible = tree.layer ? sel : header; + L.DomUtil.addClass(sensible, this.cls.pointer); + sensible.tabIndex = 0; + L.DomEvent.on(sensible, 'click keydown', function(e) { + // leaflet internal flag to prevent click propagation and collapsing tree on mobile browsers + if (this._preventClick) { + return; + } + if (e.type === 'keydown' && e.keyCode !== 32) { + return; + } + sensible.blur(); + + if (L.DomUtil.hasClass(opened, hide)) { + // it is not opened, so open it + L.DomUtil.addClass(closed, hide); + L.DomUtil.removeClass(opened, hide); + L.DomUtil.removeClass(children, hide); + } else { + // close it + L.DomUtil.removeClass(closed, hide); + L.DomUtil.addClass(opened, hide); + L.DomUtil.addClass(children, hide); + } + this._localExpand(); + }, this); + if (selAll) { + selAllNodes.splice(0, 0, container); + } + tree.children.forEach(function(child) { + var node = creator('div', this.cls.node, children); + this._iterateTreeLayout(child, node, overlay, selAllNodes); + }, this); + if (selAll) { + selAllNodes.splice(0, 1); + } + } else { + // no children, so the selector makes no sense. + L.DomUtil.addClass(sel, this.cls.neverShow); + } + + // make (or not) the label clickable to toggle the layer + var labelType; + if (tree.layer) { + if ((this.options.labelIsSelector === 'both') || // if option is set to both + (overlay && this.options.labelIsSelector === 'overlay') || // if an overlay layer and options is set to overlay + (!overlay && this.options.labelIsSelector === 'base')) { // if a base layer and option is set to base + labelType = 'label'; + } else { // if option is set to something else + labelType = 'span'; + } + } else { + labelType = 'span'; + } + // create the input and label + var label = creator(labelType, this.cls.label, entry); + if (tree.layer) { + // now create the element like in _addItem + var checked = this._map.hasLayer(tree.layer); + var input; + var radioGroup = overlay ? tree.radioGroup : 'leaflet-base-layers_' + L.Util.stamp(this); + if (radioGroup) { + input = this._createRadioElement(radioGroup, checked); + } else { + input = this._createCheckboxElement(checked); + manageSelectorsAll(input, this); + } + if (this._layerControlInputs) { + // to keep compatibility with 1.0.3 + this._layerControlInputs.push(input); + } + input.layerId = L.Util.stamp(tree.layer); + L.DomEvent.on(input, 'click', this._onInputClick, this); + label.appendChild(input); + } + + function isText(variable) { + return (typeof variable === 'string' || variable instanceof String); + } + + function isFunction(functionToCheck) { + return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]'; + } + + function selectAllCheckboxes(select, ctx) { + var inputs = container.getElementsByTagName('input'); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if (input.type !== 'checkbox') continue; + input.checked = select; + input.indeterminate = false; + } + ctx._onInputClick(); + } + if (tree.selectAllCheckbox) { + // selAll is already created + label.appendChild(selAll); + if (isText(tree.selectAllCheckbox)) { + selAll.title = tree.selectAllCheckbox; + } + L.DomEvent.on(selAll, 'click', function(ev) { + ev.stopPropagation(); + selectAllCheckboxes(selAll.checked, this); + }, this); + updateSelAllCheckbox(container); + manageSelectorsAll(selAll, this); + } + + creator('span', this.cls.name, label, tree.label); + + // hide the button which doesn't fit the collapsed state, then hide children conditionally + L.DomUtil.addClass(tree.collapsed ? opened : closed, hide); + tree.collapsed && children && L.DomUtil.addClass(children, hide); + + if (noShow) { + L.DomUtil.addClass(header, this.cls.neverShow); + L.DomUtil.addClass(children, this.cls.childrenNopad); + } + + var eventeds = tree.eventedClasses; + if (!(eventeds instanceof Array)) { + eventeds = [eventeds]; + } + + for (var e = 0; e < eventeds.length; e++) { + var evented = eventeds[e]; + if (evented && evented.className) { + var obj = container.querySelector('.' + evented.className); + if (obj) { + L.DomEvent.on(obj, evented.event || 'click', (function(selectAll) { + return function(ev) { + ev.stopPropagation(); + var select = isFunction(selectAll) ? selectAll(ev, container, tree, this._map) : selectAll; + if (select !== undefined && select !== null) { + selectAllCheckboxes(select, this); + } + }; + })(evented.selectAll), this); + } + } + } + }, + + _createCheckboxElement: function(checked) { + var input = document.createElement('input'); + input.type = 'checkbox'; + input.className = 'leaflet-control-layers-selector'; + input.defaultChecked = checked; + return input; + }, + + }); + + L.control.layers.tree = function(base, overlays, options) { + return new L.Control.Layers.Tree(base, overlays, options); + }; + +})); diff --git a/data/leaflet/L.KML.js b/data/leaflet/L.KML.js new file mode 100755 index 000000000..2f05068d9 --- /dev/null +++ b/data/leaflet/L.KML.js @@ -0,0 +1,482 @@ +/*! + Copyright (c) 2011-2015, Pavel Shramov, Bruno Bergot - MIT licence +*/ + +L.KML = L.FeatureGroup.extend({ + + initialize: function (kml, kmlOptions) { + this._kml = kml; + this._layers = {}; + this._kmlOptions = kmlOptions; + + if (kml) { + this.addKML(kml, kmlOptions); + } + }, + + addKML: function (xml, kmlOptions) { + var layers = L.KML.parseKML(xml, kmlOptions); + if (!layers || !layers.length) return; + for (var i = 0; i < layers.length; i++) { + this.fire('addlayer', { + layer: layers[i] + }); + this.addLayer(layers[i]); + } + this.latLngs = L.KML.getLatLngs(xml); + this.fire('loaded'); + }, + + latLngs: [] +}); + +L.Util.extend(L.KML, { + + parseKML: function (xml, kmlOptions) { + var style = this.parseStyles(xml, kmlOptions); + this.parseStyleMap(xml, style); + var el = xml.getElementsByTagName('Folder'); + var layers = [], l; + for (var i = 0; i < el.length; i++) { + if (!this._check_folder(el[i])) { continue; } + l = this.parseFolder(el[i], style); + if (l) { layers.push(l); } + } + el = xml.getElementsByTagName('Placemark'); + for (var j = 0; j < el.length; j++) { + if (!this._check_folder(el[j])) { continue; } + l = this.parsePlacemark(el[j], xml, style); + if (l) { layers.push(l); } + } + el = xml.getElementsByTagName('GroundOverlay'); + for (var k = 0; k < el.length; k++) { + l = this.parseGroundOverlay(el[k]); + if (l) { layers.push(l); } + } + return layers; + }, + + // Return false if e's first parent Folder is not [folder] + // - returns true if no parent Folders + _check_folder: function (e, folder) { + e = e.parentNode; + while (e && e.tagName !== 'Folder') + { + e = e.parentNode; + } + return !e || e === folder; + }, + + parseStyles: function (xml, kmlOptions) { + var styles = {}; + var sl = xml.getElementsByTagName('Style'); + for (var i=0, len=sl.length; i 1) { + layer = new L.FeatureGroup(layers); + } + + this.addPlacePopup(place, layer); + return layer; + }, + + addPlacePopup: function(place, layer) { + var el, i, j, name, descr = ''; + el = place.getElementsByTagName('name'); + if (el.length && el[0].childNodes.length) { + name = el[0].childNodes[0].nodeValue; + } + el = place.getElementsByTagName('description'); + for (i = 0; i < el.length; i++) { + for (j = 0; j < el[i].childNodes.length; j++) { + descr = descr + el[i].childNodes[j].nodeValue; + } + } + + if (name) { + layer.bindPopup('

' + name + '

' + descr, { className: 'kml-popup'}); + } + }, + + parseCoords: function (xml) { + var el = xml.getElementsByTagName('coordinates'); + return this._read_coords(el[0]); + }, + + parseLineString: function (line, xml, options) { + var coords = this.parseCoords(line); + if (!coords.length) { return; } + return new L.Polyline(coords, options); + }, + + parseTrack: function (line, xml, options) { + var el = xml.getElementsByTagName('gx:coord'); + if (el.length === 0) { el = xml.getElementsByTagName('coord'); } + var coords = []; + for (var j = 0; j < el.length; j++) { + coords = coords.concat(this._read_gxcoords(el[j])); + } + if (!coords.length) { return; } + return new L.Polyline(coords, options); + }, + + parsePoint: function (line, xml, options) { + var el = line.getElementsByTagName('coordinates'); + if (!el.length) { + return; + } + var ll = el[0].childNodes[0].nodeValue.split(','); + return new L.KMLMarker(new L.LatLng(ll[1], ll[0]), options); + }, + + parsePolygon: function (line, xml, options) { + var el, polys = [], inner = [], i, coords; + el = line.getElementsByTagName('outerBoundaryIs'); + for (i = 0; i < el.length; i++) { + coords = this.parseCoords(el[i]); + if (coords) { + polys.push(coords); + } + } + el = line.getElementsByTagName('innerBoundaryIs'); + for (i = 0; i < el.length; i++) { + coords = this.parseCoords(el[i]); + if (coords) { + inner.push(coords); + } + } + if (!polys.length) { + return; + } + if (options.fillColor) { + options.fill = true; + } + if (polys.length === 1) { + return new L.Polygon(polys.concat(inner), options); + } + return new L.MultiPolygon(polys, options); + }, + + getLatLngs: function (xml) { + var el = xml.getElementsByTagName('coordinates'); + var coords = []; + for (var j = 0; j < el.length; j++) { + // text might span many childNodes + coords = coords.concat(this._read_coords(el[j])); + } + return coords; + }, + + _read_coords: function (el) { + var text = '', coords = [], i; + for (i = 0; i < el.childNodes.length; i++) { + text = text + el.childNodes[i].nodeValue; + } + text = text.split(/[\s\n]+/); + for (i = 0; i < text.length; i++) { + var ll = text[i].split(','); + if (ll.length < 2) { + continue; + } + coords.push(new L.LatLng(ll[1], ll[0])); + } + return coords; + }, + + _read_gxcoords: function (el) { + var text = '', coords = []; + text = el.firstChild.nodeValue.split(' '); + coords.push(new L.LatLng(text[1], text[0])); + return coords; + }, + + parseGroundOverlay: function (xml) { + var latlonbox = xml.getElementsByTagName('LatLonBox')[0]; + var bounds = new L.LatLngBounds( + [ + latlonbox.getElementsByTagName('south')[0].childNodes[0].nodeValue, + latlonbox.getElementsByTagName('west')[0].childNodes[0].nodeValue + ], + [ + latlonbox.getElementsByTagName('north')[0].childNodes[0].nodeValue, + latlonbox.getElementsByTagName('east')[0].childNodes[0].nodeValue + ] + ); + var attributes = {Icon: true, href: true, color: true}; + function _parse (xml) { + var options = {}, ioptions = {}; + for (var i = 0; i < xml.childNodes.length; i++) { + var e = xml.childNodes[i]; + var key = e.tagName; + if (!attributes[key]) { continue; } + var value = e.childNodes[0].nodeValue; + if (key === 'Icon') { + ioptions = _parse(e); + if (ioptions.href) { options.href = ioptions.href; } + } else if (key === 'href') { + options.href = value; + } else if (key === 'color') { + options.opacity = parseInt(value.substring(0, 2), 16) / 255.0; + options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4); + } + } + return options; + } + var options = {}; + options = _parse(xml); + if (latlonbox.getElementsByTagName('rotation')[0] !== undefined) { + var rotation = latlonbox.getElementsByTagName('rotation')[0].childNodes[0].nodeValue; + options.rotation = parseFloat(rotation); + } + return new L.RotatedImageOverlay(options.href, bounds, {opacity: options.opacity, angle: options.rotation}); + } + +}); + +L.KMLIcon = L.Icon.extend({ + options: { + iconSize: [32, 32], + iconAnchor: [16, 16], + }, + _setIconStyles: function (img, name) { + L.Icon.prototype._setIconStyles.apply(this, [img, name]); + }, + _createImg: function (src, el) { + el = el || document.createElement('img'); + el.onload = this.applyCustomStyles.bind(this,el) + el.src = src; + return el; + }, + applyCustomStyles: function(img) { + var options = this.options; + var width = options.iconSize[0]; + var height = options.iconSize[1]; + + this.options.popupAnchor = [0,(-0.83*height)]; + if (options.anchorType.x === 'fraction') + img.style.marginLeft = (-options.anchorRef.x * width) + 'px'; + if (options.anchorType.y === 'fraction') + img.style.marginTop = ((-(1 - options.anchorRef.y) * height) + 1) + 'px'; + if (options.anchorType.x === 'pixels') + img.style.marginLeft = (-options.anchorRef.x) + 'px'; + if (options.anchorType.y === 'pixels') + img.style.marginTop = (options.anchorRef.y - height + 1) + 'px'; + } +}); + + +L.KMLMarker = L.Marker.extend({ + options: { + icon: new L.KMLIcon.Default() + } +}); + +// Inspired by https://github.com/bbecquet/Leaflet.PolylineDecorator/tree/master/src +L.RotatedImageOverlay = L.ImageOverlay.extend({ + options: { + angle: 0 + }, + _reset: function () { + L.ImageOverlay.prototype._reset.call(this); + this._rotate(); + }, + _animateZoom: function (e) { + L.ImageOverlay.prototype._animateZoom.call(this, e); + this._rotate(); + }, + _rotate: function () { + if (L.DomUtil.TRANSFORM) { + // use the CSS transform rule if available + this._image.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)'; + } else if (L.Browser.ie) { + // fallback for IE6, IE7, IE8 + var rad = this.options.angle * (Math.PI / 180), + costheta = Math.cos(rad), + sintheta = Math.sin(rad); + this._image.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' + + costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')'; + } + }, + getBounds: function () { + return this._bounds; + } +}); diff --git a/data/leaflet/jszip.min.js b/data/leaflet/jszip.min.js new file mode 100755 index 000000000..ff4cfd5e8 --- /dev/null +++ b/data/leaflet/jszip.min.js @@ -0,0 +1,13 @@ +/*! + +JSZip v3.10.1 - A JavaScript class for generating and reading zip files + + +(c) 2009-2016 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. + +JSZip uses the library pako released under the MIT license : +https://github.com/nodeca/pako/blob/main/LICENSE +*/ + +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r - - - - - Leaflet.draw drawing and editing tools - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
-
-

Point and Radius:

-

Enter lat, lon, and radius, then choose Data Source and click Download File

-
- - - - e.g., 44.334 - - - - - e.g., -112.555 - - - - - e.g., 10 - -
-
-

Bounding Box Coordinates:

- - - - - - - - -
-
- - - - diff --git a/data/map.html b/data/map.html new file mode 100755 index 000000000..8f5883291 --- /dev/null +++ b/data/map.html @@ -0,0 +1,554 @@ + + + + + + + Leaflet Map + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/scripts/build_deps_rhel7.sh b/scripts/build_deps_rhel7.sh index d2c415a91..e946ee24e 100644 --- a/scripts/build_deps_rhel7.sh +++ b/scripts/build_deps_rhel7.sh @@ -90,7 +90,7 @@ export CXXFLAGS=-std=c++11 ccmake -DCMAKE_LIBRARY_PATH=$PREFIX/lib \ -DCMAKE_INCLUDE_DIR=$PREFIX/include \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ - -DNINJA_QTGUI=OFF \ + -DNINJA_GUI=OFF \ -DNINJAFOAM=OFF \ -DGDAL_CONFIG=$PREFIX/bin/gdal-config \ -DGDAL_INCLUDE_DIR=$PREFIX/include \ diff --git a/scripts/build_deps_ubuntu_2204.sh b/scripts/build_deps_ubuntu_2204.sh index 4d529f4f9..bf374aa6b 100755 --- a/scripts/build_deps_ubuntu_2204.sh +++ b/scripts/build_deps_ubuntu_2204.sh @@ -19,6 +19,8 @@ sudo apt install -y libfontconfig1-dev \ libopenjp2-7-dev \ libtiff-dev +# Install qt6 libs +sudo apt install qt6-base-dev qt6-base-dev-tools qt6-webengine-dev qt6-webengine-dev-tools libqt6webenginecore6-bin # Install Poppler for PDF support in GDAL wget https://poppler.freedesktop.org/$POPPLER.tar.xz @@ -54,13 +56,6 @@ make -j$(nproc) sudo make install cd .. -# Add qt4 libs from ppa -# See here for more info: -# https://ubuntuhandbook.org/index.php/2020/07/install-qt4-ubuntu-20-04/ -sudo add-apt-repository ppa:ubuntuhandbook1/ppa -sudo apt update -sudo apt install -y libqt4-dev libqtwebkit-dev - # Use OpenFOAM 9; OpenFOAM 8 not available for Ubuntu 22.04 # add the dl.openfoam.org repo and install OpenFOAM 9 sudo sh -c "wget -O - https://dl.openfoam.org/gpg.key > /etc/apt/trusted.gpg.d/openfoam.asc" diff --git a/scripts/firelab_build.bat b/scripts/firelab_build.bat index 57132c71e..420bd7253 100644 --- a/scripts/firelab_build.bat +++ b/scripts/firelab_build.bat @@ -35,7 +35,7 @@ REM -DGDAL_LIBRARY=c:/src/gdal/gdal-1.10.0/lib/gdal_i.lib ^ cmake c:/src/windninja/trunk ^ -G "NMake Makefiles JOM" ^ -DFIRELAB_PACKAGE=ON ^ - -DNINJA_QTGUI=ON ^ + -DNINJA_GUI=ON ^ -DNINJA_CLI=ON ^ -DENABLE_CONSOLE=FALSE ^ -DOPENMP_SUPPORT=ON ^ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 233d1b8db..3797c367c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,13 +15,13 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -#cmake_minimum_required(VERSION 2.6) +#cmake_minimum_required(VERSION 3.16) add_subdirectory(ninja) -if(NINJA_QTGUI) +if(NINJA_GUI) add_subdirectory(gui) -endif(NINJA_QTGUI) +endif(NINJA_GUI) if(NINJA_CLI) add_subdirectory(cli) endif(NINJA_CLI) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 7376568e8..c8c413d0d 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -15,7 +15,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -#cmake_minimum_required(VERSION 2.6) +#cmake_minimum_required(VERSION 3.10) include_directories(${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/ninja @@ -29,14 +29,6 @@ set(LINK_LIBS ${Boost_LIBRARIES} ${NETCDF_LIBRARIES_C} ${CURL_LIBRARIES}) -if(NINJA_QTGUI AND NINJA_CLI) - set(LINK_LIBS ${LINK_LIBS} - ${QT_LIBRARIES}) - if(PACKAGE_DEBUG) - message("CLI Linking to Qt Libraries") - endif(PACKAGE_DEBUG) -endif(NINJA_QTGUI AND NINJA_CLI) - if(WIN32 OR APPLE) set(LINK_LIBS ${LINK_LIBS} ${PROJECT_BINARY_DIR}/src/ninja/${CMAKE_CFG_INTDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}ninja${CMAKE_STATIC_LIBRARY_SUFFIX}) else(WIN32 OR APPLE) @@ -49,7 +41,19 @@ endif() add_executable(WindNinja_cli cmake_cli.cpp) + +if(WIN32) + target_link_libraries(WindNinja_cli PRIVATE shapelib::shp GDAL::GDAL) +endif() + target_link_libraries(WindNinja_cli ${LINK_LIBS} $<$:OpenMP::OpenMP_CXX>) add_dependencies(WindNinja_cli ninja) +if(MSVC) + target_compile_definitions(WindNinja_cli + PUBLIC + WIN32_LEAN_AND_MEAN + ) +endif() + install(TARGETS WindNinja_cli DESTINATION bin COMPONENT apps) diff --git a/src/cli/cmake_cli.cpp b/src/cli/cmake_cli.cpp index d9e39b8ec..4150005cf 100644 --- a/src/cli/cmake_cli.cpp +++ b/src/cli/cmake_cli.cpp @@ -2,7 +2,7 @@ * * $Id:$ * - * Project: WindNinja Qt GUI + * Project: WindNinja cli * Purpose: main() function to initiate cli * Author: Kyle Shannon * diff --git a/src/fetch_dem/CMakeLists.txt b/src/fetch_dem/CMakeLists.txt index 776301a20..9bb0a27e9 100644 --- a/src/fetch_dem/CMakeLists.txt +++ b/src/fetch_dem/CMakeLists.txt @@ -15,7 +15,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.16) include_directories(${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/ninja diff --git a/src/fetch_station/CMakeLists.txt b/src/fetch_station/CMakeLists.txt index 1d967ad07..2adef421d 100644 --- a/src/fetch_station/CMakeLists.txt +++ b/src/fetch_station/CMakeLists.txt @@ -15,7 +15,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.16) include_directories(${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/ninja diff --git a/src/flow_separation_grid/CMakeLists.txt b/src/flow_separation_grid/CMakeLists.txt index ebe468c07..2c24ca734 100644 --- a/src/flow_separation_grid/CMakeLists.txt +++ b/src/flow_separation_grid/CMakeLists.txt @@ -15,7 +15,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.16) include_directories(${PROJECT_SOURCE_DIR}/src/ninja ${GDAL_SYSTEM_INCLUDE} ${GDAL_INCLUDE_DIR}) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt deleted file mode 100644 index e78c2ea5a..000000000 --- a/src/gui/CMakeLists.txt +++ /dev/null @@ -1,133 +0,0 @@ -# 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. - -cmake_minimum_required(VERSION 2.6) - -QT4_WRAP_UI(QTUI_H_SRCS WidgetDownloadDEM.ui - setconfigdialog.ui - stationFetchWidget.ui) - -set(NINJA_GUI_INCLUDES consoleDockWidget.h - diurnalInput.h - stabilityInput.h - fbOutput.h - googleOutput.h - latLonWidget.h - mainWindow.h - metaWindWidget.h - nativeSolverInput.h - ninjafoamInput.h - outputHeightWidget.h - outputMetaData.h - pdfOutput.h - pointInput.h - shapeOutput.h - solvePage.h - solveThread.h - splash.h - surfaceInput.h - timeZoneWidget.h - #vec3f.h - vtkOutput.h - weatherModel.h - WidgetDownloadDEM.h - stationFetchWidget.h - windInput.h - windInputTable.h - WindNinjaTree.h - GoogleMapsInterface.h - setconfigdialog.h - ${QTUI_H_SRCS}) - -include_directories(${QT_INCLUDES} - ${Boost_INCLUDE_DIRS} - ${NETCDF_INCLUDES} - ${GDAL_SYSTEM_INCLUDE} ${GDAL_INCLUDE_DIR} - ${CURL_INCLUDE_DIR} - ${PROJECT_SOURCE_DIR}/src - ${PROJECT_SOURCE_DIR}/src/ninja - ${CMAKE_CURRENT_BINARY_DIR}) - -QT4_WRAP_CPP(NINJA_GUI_INCLUDES_MOC ${NINJA_GUI_INCLUDES}) -QT4_ADD_RESOURCES(NINJA_GUI_RESOURCES ${PROJECT_SOURCE_DIR}/wn-resources.qrc) - -set(NINJA_GUI_SOURCES cmake_gui.cpp - ${PROJECT_SOURCE_DIR}/src/ninja/cli.cpp - consoleDockWidget.cpp - diurnalInput.cpp - stabilityInput.cpp - fbOutput.cpp - googleOutput.cpp - latLonWidget.cpp - mainWindow.cpp - metaWindWidget.cpp - outputHeightWidget.cpp - outputMetaData.cpp - nativeSolverInput.cpp - ninjafoamInput.cpp - pdfOutput.cpp - pointInput.cpp - shapeOutput.cpp - solvePage.cpp - solveThread.cpp - splash.cpp - surfaceInput.cpp - timeZoneWidget.cpp - vec3f.cpp - vtkOutput.cpp - weatherModel.cpp - windInput.cpp - windInputTable.cpp - WindNinjaTree.cpp - WidgetDownloadDEM.cpp - stationFetchWidget.cpp - GoogleMapsInterface.cpp - setconfigdialog.cpp - ${QTUI_H_SRCS}) -if(WIN32) - set(NINJA_GUI_SOURCES ${NINJA_GUI_SOURCES} - ${PROJECT_SOURCE_DIR}/wn-ms-resource.rc) - add_definitions(-DQT_NO_DEBUG_OUTPUT) -endif(WIN32) - -set(LINK_LIBS ${QT_LIBRARIES} - ${GDAL_LIBRARY} - ${Boost_LIBRARIES} - ${CURL_LIBRARIES} - ${NETCDF_LIBRARIES_C}) - -if(WIN32) - set(LINK_LIBS ${LINK_LIBS} ${PROJECT_BINARY_DIR}/src/ninja/${CMAKE_CFG_INTDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}ninja${CMAKE_STATIC_LIBRARY_SUFFIX}) - if(NOT ENABLE_CONSOLE) - set(LINK_LIBS ${LINK_LIBS} ${QT_QTMAIN_LIBRARY}) - set(GUI_TYPE WIN32) - else(NOT ENABLE_CONSOLE) - set(GUI_TYPE "") - endif(NOT ENABLE_CONSOLE) -else(WIN32) - set(LINK_LIBS ${LINK_LIBS} ${PROJECT_BINARY_DIR}/src/ninja/${CMAKE_SHARED_LIBRARY_PREFIX}ninja${CMAKE_SHARED_LIBRARY_SUFFIX}) -endif(WIN32) - -add_executable(WindNinja ${GUI_TYPE} - ${NINJA_GUI_INCLUDES_MOC} - ${NINJA_GUI_RESOURCES} - ${NINJA_GUI_SOURCES}) - -target_link_libraries(WindNinja ${LINK_LIBS} $<$:OpenMP::OpenMP_CXX>) -add_dependencies(WindNinja ninja) -install(TARGETS WindNinja DESTINATION bin COMPONENT apps) - diff --git a/src/gui/WidgetDownloadDEM.cpp b/src/gui/WidgetDownloadDEM.cpp deleted file mode 100644 index 4df8c90c1..000000000 --- a/src/gui/WidgetDownloadDEM.cpp +++ /dev/null @@ -1,585 +0,0 @@ -/****************************************************************************** - * - * $Id: WidgetDownloadDEM.cpp 1757 2012-08-07 18:40:40Z kyle.shannon $ - * - * Project: WindNinja - * Purpose: DEM Downloader Window - * Author: Cody Posey - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "WidgetDownloadDEM.h" -//#include - -WidgetDownloadDEM::WidgetDownloadDEM(QWidget *parent) - : QWidget(parent) -{ - - //------------provide updated SSL certs in case system ones are outdated--------------------------------// - // qt_certs_bundle.pem generated on a windows system with the latest windows version of openssl installed - // certutil -generateSSTFromWU roots.sst - // certutil -split -f roots.sst - // type NUL > qt_certs_bundle.pem - // for %f in (*.crt) do (openssl x509 -inform der -in "%f" -out temp.pem && type temp.pem >> qt_certs_bundle.pem && del temp.pem) - // del *.crt - // for debugging on working systems, try disabling system certificates for testing, set it to an empty list of certificates - //QSslSocket::setDefaultCaCertificates(QList()); - // add the data folder SSL Ca certificates to the certificates list - std::string pathToSslCerts = FindDataPath("qt_certs_bundle.pem"); - QString pathToCerts = QString::fromStdString(pathToSslCerts); - QSslSocket::addDefaultCaCertificates( pathToCerts, QSsl::Pem, QRegExp::FixedString); - - demSelected = false; - - setupUi(this); - setupGM(); - initializeGoogleMapsInterface(); - connectInputs(); - this->readSettings(); - - progressBar = new QProgressDialog(this); - progressBar->setWindowModality(Qt::ApplicationModal); - progressBar->setAutoReset(false); - progressBar->setAutoClose(false); - - boundsError = NULL; - bufferError = NULL; - latlngError = NULL; - - //These are temporary values to store the points needed for DEM bounds - double northEast[2]; - double southEast[2]; - double southWest[2]; - double northWest[2]; - - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::SRTM,""); - fetcher->GetCorners(northEast, southEast, southWest, northWest); - srtm_northBound = northEast[1]; - srtm_eastBound = northEast[0]; - srtm_westBound = southWest[0]; - srtm_southBound = southWest[1]; - delete fetcher; - -#ifdef HAVE_GMTED - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::WORLD_GMTED,""); - fetcher->GetCorners(northEast, southEast, southWest, northWest); - world_gmted_northBound = northEast[1]; - world_gmted_eastBound = northEast[0]; - world_gmted_westBound = southWest[0]; - world_gmted_southBound = southWest[1]; - delete fetcher; -#endif - -#ifdef WITH_LCP_CLIENT - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::LCP,""); - fetcher->GetCorners(northEast, southEast, southWest, northWest); - lcp_northBound = northEast[1]; - lcp_eastBound = northEast[0]; - lcp_westBound = southWest[0]; - lcp_southBound = southWest[1]; - delete fetcher; -#endif - -#ifndef HAVE_GMTED - this->cbDEMSource->removeItem(1); -#endif - -#ifndef WITH_LCP_CLIENT - this->cbDEMSource->removeItem(2); -#endif - - cbDEMSource->setItemData(0, "Partial world coverage Shuttle Radar Topography Mission data (SRTM) at 30 meter resolution. Any existing holes in the data have been filled.", Qt::ToolTipRole); -#ifdef HAVE_GMTED - cbDEMSource->setItemData(1, "World coverage Global Multi-resolution Terrain Elevation Data 2010 (GMTED2010) at 250 meter resolution.", Qt::ToolTipRole); -#endif -#ifdef WITH_LCP_CLIENT - cbDEMSource->setItemData(2, "US coverage LANDFIRE 2023 Landscape data at 30 meter resolution.", Qt::ToolTipRole); -#endif - updateDEMSource(cbDEMSource->currentIndex()); - - fileSize = 0; - -#ifdef ENABLE_FIDDLER - QNetworkProxy proxy; - proxy.setType(QNetworkProxy::HttpProxy); - proxy.setHostName("127.0.0.1"); - proxy.setPort(8888); - QNetworkProxy::setApplicationProxy(proxy); -#endif - - this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); - this->show(); -} - -/** - * @brief Deletes all allocated memory on destruction of class object - * - */ -WidgetDownloadDEM::~WidgetDownloadDEM() -{ - delete progressBar; - delete fetcher; - delete latlngError; - delete bufferError; - delete boundsError; - delete gmInterface; -} - -/** - * @brief Connect all SLOTS and SIGNALS in the Qt GUI - * - */ -void WidgetDownloadDEM::connectInputs() -{ - connect(btnDownloadDEM, SIGNAL(clicked()), this, SLOT(saveDEM())); - connect(cbDEMSource, SIGNAL(currentIndexChanged(int)), this, SLOT(updateDEMSource(int))); -} - -/** - * @brief Initialize the google maps interface and interface object - * - */ -void WidgetDownloadDEM::initializeGoogleMapsInterface() -{ - gmInterface = new GoogleMapsInterface(); - this->wvGoogleMaps->page()->mainFrame()->addToJavaScriptWindowObject("GMInterface", gmInterface); - connect(gmInterface, SIGNAL(zoomExtents()), this, SLOT(zoomToMidpoint())); -} - -/** - * @brief Clears listeners when changing selection method - * - */ -void WidgetDownloadDEM::clearListeners() -{ - this->wvGoogleMaps->page()->mainFrame()->evaluateJavaScript("clearListeners(); null"); -} - - -/** - * @brief Sets up the google map - * - */ -void WidgetDownloadDEM::setupGM() -{ - this->wvGoogleMaps->settings()->setAttribute(QWebSettings::JavascriptEnabled, true); - this->wvGoogleMaps->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); - this->wvGoogleMaps->setPage(new QWebPage()); - wvGoogleMaps->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); - this->wvGoogleMaps->load(QUrl(QString(CPLFormFilename("file:///", (FindDataPath("map.htm").c_str()), NULL)))); - - //enable QWebInspector for degugging google maps widget - if(CSLTestBoolean(CPLGetConfigOption("ENABLE_QWEBINSPECTOR", "NO"))) - { - wvGoogleMaps->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); - QWebInspector *i = new QWebInspector(this); - i->setPage(wvGoogleMaps->page()); - i->setVisible(true); - - dlg.setLayout(new QVBoxLayout()); - dlg.layout()->addWidget(i); - dlg.setModal(false); - dlg.show(); - dlg.raise(); - dlg.activateWindow(); - } -} - -/** - * @brief Download and save the current DEM area - * - */ -void WidgetDownloadDEM::saveDEM() -{ - QVariant mbr = wvGoogleMaps->page()->mainFrame()->evaluateJavaScript("mbr()"); - if(mbr.isNull()) { - qDebug()<<"no mbr"; - QMessageBox mbox; - mbox.setText("DEM bounding box not set. Select the DEM bounding box by using the bounding box drawing tool on the upper right corner of the map, entering a point and radius, or entering the bounding box coordinates."); - mbox.exec(); - return; - } - QVariantList mbrl = mbr.toList(); - if(mbrl.size() == 0) - { - QMessageBox mbox; - mbox.setText("DEM bounding box not set. Select the DEM bounding box by using the bounding box drawing tool on the upper right corner of the map, entering a point and radius, or entering the bounding box coordinates."); - mbox.exec(); - return; - } - for(int i=0; i < mbrl.size(); i++) { - qDebug() << mbrl[i]; - } - northBound = mbrl[3].toDouble(); - southBound = mbrl[2].toDouble(); - eastBound = mbrl[1].toDouble(); - westBound = mbrl[0].toDouble(); - demSelected = true; - - QString fileName; - double boundArray[] = {this->northBound, this->eastBound, this->southBound, this->westBound}; - double *boundBox; - boundBox = boundArray; - - if(northBound == 0.0 || southBound == 0.0 || eastBound == 0.0 || westBound == 0.0) - { - QMessageBox noBoundsError; - noBoundsError.setText("Please Select an Area on the Map"); - noBoundsError.exec(); - } - else if(!demSelected) - { - QMessageBox noBoundsError2; - noBoundsError2.setText("Please select an area on the map."); - noBoundsError2.exec(); - } - //else if(!demBoundsCheck()) - //{ - // QMessageBox demBoundsError; - // demBoundsError.setText("Area is outside data bounds. Please select new data Source or new Area."); - // demBoundsError.exec(); - //} - else if((fileSize/1024) > 50) - { - QMessageBox demBoundsError; - demBoundsError.setText("File size is limited to 50mb. Please select a smaller area."); - demBoundsError.exec(); - } - else if(cbDEMSource->currentIndex() == 0 && (CPLGetConfigOption("CUSTOM_SRTM_API_KEY", NULL) == NULL || CPLGetConfigOption("NINJA_GUI_SRTM_API_KEY", NULL) != NULL)) - { - QMessageBox apiKeyError; - apiKeyError.setText("API Key not specified. Please specify the environment variables NINJA_GUI_SRTM_API_KEY or CUSTOM_SRTM_API_KEY"); - apiKeyError.exec(); - } - else - { - QFileDialog saveDialog(this, tr("Save File"), demFileDir.absolutePath(), currentSaveAsDesc); - saveDialog.setDefaultSuffix(currentSuffix); - saveDialog.setAcceptMode(QFileDialog::AcceptSave); - if(saveDialog.exec()) - { - fileName = saveDialog.selectedFiles().at(0); - - QByteArray file = fileName.toLocal8Bit(); - demFile = file.data(); - - progressBar->setLabelText("Downloading File..."); - progressBar->setRange(0,0); - progressBar->setCancelButtonText("Cancel"); - progressBar->reset(); - bool test = progressBar->wasCanceled(); - - connect(&futureWatcher, SIGNAL(finished()), this, SLOT(updateProgress())); - connect(progressBar, SIGNAL(canceled()), this, SLOT(updateProgress())); - - futureWatcher.setFuture(QtConcurrent::run(this, &WidgetDownloadDEM::fetchBoundBox, boundBox, demFile, currentResolution)); - - progressBar->exec(); - } - } -} - -/** - * @brief Updates the progress bar and fills no data values - * - */ -void WidgetDownloadDEM::updateProgress() -{ - disconnect(progressBar, SIGNAL(canceled()), this, SLOT(updateProgress())); - disconnect(&futureWatcher, SIGNAL(finished()), this, SLOT(updateProgress())); - - if(progressBar->wasCanceled()) - { - progressBar->setLabelText("Canceling..."); - progressBar->setCancelButton(0); - futureWatcher.waitForFinished(); - VSIUnlink(demFile); - progressBar->cancel(); - } - else - { - futureWatcher.waitForFinished(); - int result = futureWatcher.result(); - - if(result < 0) - { - progressBar->setLabelText("The surface data download failed. \nThis can happen when either the data source doesn't cover your region or the server that provides the surface data is down or under high usage. \nPlease try again later or try a different data source."); - progressBar->setRange(0,1); - progressBar->setValue( 0 ); - progressBar->setCancelButtonText("Close"); - VSIUnlink(demFile); - } - else - { - if(result) - this->fillNoDataValues(demFile); - - progressBar->setValue(1); - progressBar->setRange(0,100); - progressBar->setValue(100); - progressBar->setLabelText("Download Successful"); - progressBar->setCancelButtonText("Close"); - - this->doneDownloading(demFile); - } - } -} - - -/** - * @brief Closes the DEM downloader - * - */ -void WidgetDownloadDEM::closeDEM() -{ - this->close(); -} - -/** - * @brief Fetches bounding box for DEM - * - * @param boundsBox Lat/Lon Bounds - * @param fileName Name of saved DEM file - * @param resolution Resolution for DEM - */ -int WidgetDownloadDEM::fetchBoundBox(double *boundsBox, const char *fileName, double resolution) -{ - int result = fetcher->FetchBoundingBox(boundsBox, resolution, fileName, NULL); - - return result; -} - -/** - * @brief Updates the DEM Source - * - * @param index Current index of the DEM source combo box - */ -void WidgetDownloadDEM::updateDEMSource(int index) -{ - switch(index){ - case 0: //SRTM - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::SRTM, FindDataPath("/data")); - northDEMBound = srtm_northBound; - southDEMBound = srtm_southBound; - currentResolution = fetcher->GetXRes(); - currentSuffix = "tif"; - currentSaveAsDesc = "Elevation files (*.tif)"; - break; -#ifdef HAVE_GMTED - case 1: //GMTED - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::WORLD_GMTED, FindDataPath("/data")); - northDEMBound = world_gmted_northBound; - southDEMBound = world_gmted_southBound; - currentResolution = (fetcher->GetXRes() * 111325); // convert from lat/lon to m - currentSuffix = "tif"; - currentSaveAsDesc = "Elevation files (*.tif)"; - break; -#endif -#ifdef WITH_LCP_CLIENT - case 2: //LCP - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::LCP, FindDataPath("/data")); - northDEMBound = lcp_northBound; - southDEMBound = lcp_southBound; - /* this is in meters */ - currentResolution = fetcher->GetXRes(); - currentSuffix = "tif"; - currentSaveAsDesc = "Landscape files (*.tif)"; - break; -#endif - } -} - -/** - * @brief Checks whether the current selected area is withing the current DEM bounds - * - */ -bool WidgetDownloadDEM::demBoundsCheck() -{ - QString checkBounds; - QString wktPoly; - QByteArray byteArray; - int contains; - //std::string oFile = FindDataPath( "us_srtm_region.shp" ); - std::string oFile = FindDataPath( "srtm_region.geojson" ); - const char *pszWkt; - switch(cbDEMSource->currentIndex()){ - case 0: //SRTM - wktPoly = - QString("POLYGON( (%1 %2, %3 %4, ").arg(westBound).arg(northBound).arg(eastBound).arg(northBound) + - QString("%1 %2, %3 %4, ").arg(eastBound).arg(southBound).arg(westBound).arg(southBound) + - QString("%1 %2) )").arg(westBound).arg(northBound); - byteArray = wktPoly.toUtf8(); - pszWkt = byteArray.constData(); - contains = NinjaOGRContain(pszWkt, oFile.c_str(), NULL); - if(contains) - { - return true; - } - else - return false; - case 1: //GMTED - if(northDEMBound < northBound || southDEMBound > southBound) - return false; - else - return true; - break; - case 2: //LCP - if(northDEMBound < northBound || southDEMBound > southBound) - return false; - else - return true; - break; - } -} - -/** - * @brief Pans to lat/lng point - * - */ -void WidgetDownloadDEM::zoomToMidpoint() -{ - QString midPoint = QString("zoomFitBounds(%1,%2,%3,%4);").arg(southBound).arg(westBound).arg(northBound).arg(eastBound); - wvGoogleMaps->page()->mainFrame()->evaluateJavaScript(midPoint); -} - -/** - * @brief Updates area selected for Est File Size and for downloading a DEM - * - * @param selected Whether an area has been selected - */ -void WidgetDownloadDEM::demSelectedUpdate(bool selected) -{ - demSelected = selected; -} - -/** - * @brief Uses GDAL utilities to fill any no data values in the file - * - * @param file The file containing DEM data - */ -void WidgetDownloadDEM::fillNoDataValues(const char* file) -{ - GDALDataset *poDS; - poDS = (GDALDataset*)GDALOpen(demFile, GA_Update); - int nNoDataValues; - if(GDALHasNoData(poDS, 1)) - { - nNoDataValues = GDALFillBandNoData(poDS, 1, 100); - /* Should we fill again? */ - if(nNoDataValues) - {} - } - GDALClose((GDALDatasetH)poDS); -} - -/** - * @brief Saves the current DEM settings - * - */ -void WidgetDownloadDEM::writeSettings() -{ - QSettings settings(QSettings::UserScope, "Firelab", "WindNinja"); - settings.setDefaultFormat(QSettings::IniFormat); - //Saves DEM Source to settings - settings.setValue("demSource", cbDEMSource->currentIndex()); - //Saves Current Lat/Lon Positions - settings.setValue("North", northBound); - settings.setValue("South", southBound); - settings.setValue("East", eastBound); - settings.setValue("West", westBound); - settings.setValue("Latitude", latitude); - settings.setValue("Longitude", longitude); - //Saves Download Path to settings - settings.setValue("downloadPath", settingsDir.absolutePath()); -} - -/** - * @brief Reads previous DEM settings - * - */ -void WidgetDownloadDEM::readSettings() -{ - QSettings settings(QSettings::UserScope, "Firelab", "WindNinja"); - settings.setDefaultFormat(QSettings::IniFormat); - - if(settings.contains("demSource")) - { - cbDEMSource->setCurrentIndex(settings.value("demSource").toInt()); - } - if(settings.contains("inputFileDir")) - { - demFileDir = settings.value("inputFileDir").toString(); - } - - if(settings.contains("North")) - { - northBound = settings.value("North").toDouble(); - } - - else - northBound = 44.0249023401036; - - if(settings.contains("South")) - { - southBound = settings.value("South").toDouble(); - } - else - southBound = 43.7832152227745; - - if(settings.contains("East")) - { - eastBound = settings.value("East").toDouble(); - } - else - eastBound = -113.463446144564; - if(settings.contains("West")) - { - westBound = settings.value("West").toDouble(); - } - else - westBound = -113.749693430469; - - if(settings.contains("Latitude")) - { - latitude = settings.value("Latitude").toDouble(); - } - else - latitude = -113.613611; - if(settings.contains("Longitude")) - { - longitude = settings.value("Longitude").toDouble(); - } - else - longitude = 43.911944; -} - -/** - * @brief Handles close events of GUI such as clicking X close icon - * - */ -void WidgetDownloadDEM::closeEvent(QCloseEvent *event) -{ - event->ignore(); - this->writeSettings(); - exitDEM(); - event->accept(); -} diff --git a/src/gui/WidgetDownloadDEM.h b/src/gui/WidgetDownloadDEM.h deleted file mode 100644 index c3471d0d5..000000000 --- a/src/gui/WidgetDownloadDEM.h +++ /dev/null @@ -1,132 +0,0 @@ -/****************************************************************************** - * - * $Id: WidgetDownloadDEM.h 1757 2012-08-07 18:40:40Z kyle.shannon $ - * - * Project: WindNinja - * Purpose: DEM Downloader Window - * Author: Cody Posey - * - ****************************************************************************** - * - * 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 WIGDET_DOWNLOAD_DEM_H_ -#define WIGDET_DOWNLOAD_DEM_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "fetch_factory.h" -#include "gdal_util.h" -#include "ninja_conv.h" -#include -#include -#include "ui_WidgetDownloadDEM.h" -#include "GoogleMapsInterface.h" - -#ifndef PI -#define PI 3.14159 -#endif - -class WidgetDownloadDEM : public QWidget, private Ui::WidgetDownloadDEM -{ - Q_OBJECT - -public: - WidgetDownloadDEM(QWidget *parent = 0); - ~WidgetDownloadDEM(); - QDir settingsDir; - void initializeGoogleMapsInterface(); - void setupGM(); - void connectInputs(); - void fillNoDataValues(const char* file); - void writeSettings(); - void readSettings(); - int fetchBoundBox(double *boundsBox, const char *fileName, double resolution); - -protected: - void closeEvent(QCloseEvent *event); - - private slots: - void clearListeners(); - void saveDEM(); - void updateProgress(); - void updateDEMSource(int index); - bool demBoundsCheck(); - void zoomToMidpoint(); - void demSelectedUpdate(bool selected); - void closeDEM(); - - signals: - void doneDownloading(const char* file); - void exitDEM(); - - -private: - QDialog dlg; - - double latitude; - double longitude; - double northBound; - double southBound; - double eastBound; - double westBound; - double srtm_southBound; - double srtm_westBound; - double srtm_northBound; - double srtm_eastBound; - double world_gmted_southBound; - double world_gmted_westBound; - double world_gmted_northBound; - double world_gmted_eastBound; - double lcp_southBound; - double lcp_westBound; - double lcp_northBound; - double lcp_eastBound; - double currentResolution; - - double northDEMBound; - double southDEMBound; - - double fileSize; - bool demSelected; - - const char *demFile; - QDir demFileDir; - QFutureWatcher futureWatcher; - QProgressDialog *progressBar; - QMessageBox *boundsError; - QMessageBox *bufferError; - QMessageBox *latlngError; - SurfaceFetch *fetcher; - GoogleMapsInterface* gmInterface; - QString currentSaveAsDesc; - QString currentSuffix; -}; - -#endif /* WIGDET_DOWNLOAD_DEM_H_ */ - diff --git a/src/gui/WidgetDownloadDEM.ui b/src/gui/WidgetDownloadDEM.ui deleted file mode 100644 index b8ff6a70f..000000000 --- a/src/gui/WidgetDownloadDEM.ui +++ /dev/null @@ -1,177 +0,0 @@ - - - WidgetDownloadDEM - - - - 0 - 0 - 820 - 674 - - - - Download elevation file - - - - :/wn-icon.png:/wn-icon.png - - - - - - - - - 0 - 0 - - - - OpenHandCursor - - - - about:blank - - - - QPainter::SmoothPixmapTransform|QPainter::TextAntialiasing - - - - - - - - - - - Data Source - - - - - - - - 0 - 0 - - - - - 135 - 20 - - - - - 135 - 20 - - - - - 8 - 50 - false - - - - 0 - - - 10 - - - QComboBox::AdjustToContentsOnFirstShow - - - - WORLD SRTM (30m) - - - - - WORLD GMTED (250m) - - - - - Landscape File - - - - - - - - - 10 - 50 - false - - - - Download the elevation file. - - - Qt::LeftToRight - - - Download File - - - - :/server_go.png - - - - Qt::ToolButtonTextBesideIcon - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - <html><head/><body><p>Map data © <a href="https://www.openstreetmap.org/"><span style=" text-decoration: underline; color:#0000ff;">OpenStreetMap </span></a><a href="https://creativecommons.org/licenses/by-sa/2.0/"><span style=" text-decoration: underline; color:#0000ff;">CC-BY-SA</span></a><a href="https://www.openstreetmap.org/"><span style=" text-decoration: underline; color:#0000ff;">, Imagery © </span></a><a href="https://www.mapbox.com/"><span style=" text-decoration: underline; color:#0000ff;">Mapbox</span></a><a href="https://www.openstreetmap.org/"><span style=" text-decoration: underline; color:#0000ff;"></span></a></p></body></html> - - - Qt::TextBrowserInteraction - - - - - - - - QWebView - QWidget -
QtWebKitWidgets/QWebView
-
-
- - - - - - slot1() - -
diff --git a/src/gui/WindNinjaTree.cpp b/src/gui/WindNinjaTree.cpp deleted file mode 100644 index 61597e1e0..000000000 --- a/src/gui/WindNinjaTree.cpp +++ /dev/null @@ -1,294 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Tree display for selecting input/output widgets. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "WindNinjaTree.h" - -WindNinjaTree::WindNinjaTree(QWidget *parent) : QWidget(parent) -{ - createTree(); - createStack(); - createConnections(); - - layout = new QVBoxLayout; - layout->addWidget(tree); - layout->addWidget(stack); - setLayout(layout); - -} - -WindNinjaTree::~WindNinjaTree() -{ - -} - -void WindNinjaTree::createTree() -{ - createIcons(); - tree = new QTreeWidget; - tree->setColumnCount(1); - createInputItems(); -#ifdef NINJAFOAM - createSolverMethodItems(); -#endif - createOutputItems(); - - mainItem = new QTreeWidgetItem; - mainItem->setText(0, tr("WindNinja")); - - solveItem = new QTreeWidgetItem; - solveItem->setText(0, tr("Solve")); - solveItem->setIcon(0, blue); - - //add items in gui order - tree->setHeaderItem(mainItem); -#ifdef NINJAFOAM - tree->addTopLevelItem(solverMethodItem); - solverMethodItem->setExpanded(true); - solverMethodItem->setSelected(true); -#endif - tree->addTopLevelItem(inputItem); - inputItem->setExpanded(true); -#ifndef NINJAFOAM - surfaceItem->setSelected(true); -#endif - windItem->setExpanded(true); - tree->addTopLevelItem(diurnalItem); - tree->addTopLevelItem(stabilityItem); - tree->addTopLevelItem(outputItem); - outputItem->setExpanded(true); - tree->addTopLevelItem(solveItem); - solveItem->setExpanded(true); - - tree->setMinimumHeight(240); - //tree->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); -} - -void WindNinjaTree::createIcons() -{ - check.addFile(":tick.png"); - cross.addFile(":cross.png"); - caution.addFile(":jason_caution.png"); - blue.addFile(":bullet_blue.png"); - radio.addFile(":radio_bullet_blue.png"); - sun.addFile(":weather_sun.png"); -} - -#ifdef NINJAFOAM -void WindNinjaTree::createSolverMethodItems() -{ - solverMethodItem = new QTreeWidgetItem; - solverMethodItem->setIcon(0, blue); - solverMethodItem->setText(0, tr("Solver")); - - nativeSolverItem = new QTreeWidgetItem; - nativeSolverItem->setText(0, tr("Conservation of Mass")); - nativeSolverItem->setIcon(0, radio); - - solverMethodItem->addChild(nativeSolverItem); - - ninjafoamItem = new QTreeWidgetItem; - ninjafoamItem->setText(0, tr("Conservation of Mass and Momentum")); - ninjafoamItem->setIcon(0, radio); - - solverMethodItem->addChild(ninjafoamItem); -} -#endif //NINJAFOAM - -void WindNinjaTree::createInputItems() -{ - inputItem = new QTreeWidgetItem; - inputItem->setIcon(0, blue); - inputItem->setText(0, tr("Input")); - - //create input children, add them to input item - //surfaceItem - surfaceItem = new QTreeWidgetItem; - surfaceItem->setText(0, tr("Surface Input")); - surfaceItem->setIcon(0, blue); - - diurnalItem = new QTreeWidgetItem; - diurnalItem->setText(0, tr("Diurnal Input")); - diurnalItem->setIcon(0, blue); - - stabilityItem = new QTreeWidgetItem; - stabilityItem->setText(0, tr("Stability Input")); - stabilityItem->setIcon(0, blue); - - windItem = new QTreeWidgetItem; - windItem->setText(0, tr("Wind Input")); - windItem->setIcon(0, blue); - - spdDirItem = new QTreeWidgetItem; - spdDirItem->setText(0, tr("Domain Average Wind")); - spdDirItem->setIcon(0, radio); - - pointItem = new QTreeWidgetItem; - pointItem->setText(0, tr("Point Initialization")); - pointItem->setIcon(0, radio); - - modelItem = new QTreeWidgetItem; - modelItem->setText(0, tr("Weather Model")); - modelItem->setIcon(0, radio); - - windItem->addChild(spdDirItem); - windItem->addChild(pointItem); - windItem->addChild(modelItem); - - inputItem->addChild(surfaceItem); - inputItem->addChild(diurnalItem); - inputItem->addChild(stabilityItem); - inputItem->addChild(windItem); -} - -void WindNinjaTree::createOutputItems() -{ - outputItem = new QTreeWidgetItem; - outputItem->setText(0, tr("Output")); - outputItem->setIcon(0, blue); - - googleItem = new QTreeWidgetItem; - googleItem->setText(0, tr("Google Earth")); - googleItem->setIcon(0, blue); - fbItem = new QTreeWidgetItem; - fbItem->setText(0, tr("Fire Behavior")); - fbItem->setIcon(0, blue); - shapeItem = new QTreeWidgetItem; - shapeItem->setText(0, tr("Shape Files")); - shapeItem->setIcon(0, blue); - pdfItem = new QTreeWidgetItem; - pdfItem->setText(0, tr("Geospatial PDF Files")); - pdfItem->setIcon(0, blue); - vtkItem = new QTreeWidgetItem; - vtkItem->setText(0, tr("VTK Files")); - vtkItem->setIcon(0, blue); - - outputItem->addChild(googleItem); - outputItem->addChild(fbItem); - outputItem->addChild(shapeItem); - outputItem->addChild(pdfItem); - outputItem->addChild(vtkItem); - -} - -void WindNinjaTree::createStack() -{ -#ifdef NINJAFOAM - ninjafoam = new ninjafoamInput; - nativesolver = new nativeSolverInput; -#endif - surface = new surfaceInput; - diurnal = new diurnalInput; - stability = new stabilityInput; - - wind = new windInput; - point = new pointInput; - weather = new weatherModel; - output = new outputMetaData; - google = new googleOutput; - fb = new fbOutput; - shape = new shapeOutput; - pdf = new pdfOutput(this); - vtk = new vtkOutput; - solve = new solvePage; - - stack = new QStackedWidget; - -#ifdef NINJAFOAM - stack->addWidget(nativesolver); - stack->addWidget(ninjafoam); -#endif - stack->addWidget(surface); - stack->addWidget(diurnal); - stack->addWidget(stability); - - stack->addWidget(wind); - stack->addWidget(point); - stack->addWidget(weather); - stack->addWidget(output); - stack->addWidget(google); - stack->addWidget(fb); - stack->addWidget(shape); - stack->addWidget(pdf); - stack->addWidget(vtk); - stack->addWidget(solve); - - stack->setMinimumWidth(480); -} - -void WindNinjaTree::createConnections() -{ - connect(tree, SIGNAL(itemSelectionChanged()), this, - SLOT(updateInterface())); -} - -void WindNinjaTree::updateInterface() -{ - QTreeWidgetItem *item; - item = tree->currentItem(); - //figure out which item is selected, then change the stack widget to the proper - //input page - if(item == inputItem) - stack->setCurrentWidget(surface); - else if(item == surfaceItem) - stack->setCurrentWidget(surface); - else if(item == diurnalItem) - stack->setCurrentWidget(diurnal); - else if(item == stabilityItem) - stack->setCurrentWidget(stability); -#ifdef NINJAFOAM - else if(item == nativeSolverItem) - stack->setCurrentWidget(nativesolver); - else if(item == ninjafoamItem) - stack->setCurrentWidget(ninjafoam); - else if(item == solverMethodItem) - stack->setCurrentWidget(ninjafoam); -#endif - else if(item == windItem) - stack->setCurrentWidget(wind); - else if(item == spdDirItem) - stack->setCurrentWidget(wind); - else if(item == pointItem) - stack->setCurrentWidget(point); - else if(item == modelItem) - stack->setCurrentWidget(weather); - else if(item == outputItem) - stack->setCurrentWidget(output); - else if(item == googleItem) - stack->setCurrentWidget(google); - else if(item == fbItem) - stack->setCurrentWidget(fb); - else if(item == shapeItem) - stack->setCurrentWidget(shape); - else if(item == pdfItem) - stack->setCurrentWidget(pdf); - else if(item == vtkItem) - stack->setCurrentWidget(vtk); - else if(item == solveItem) - stack->setCurrentWidget(solve); -} diff --git a/src/gui/WindNinjaTree.h b/src/gui/WindNinjaTree.h deleted file mode 100644 index d120639e5..000000000 --- a/src/gui/WindNinjaTree.h +++ /dev/null @@ -1,139 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Tree display for selecting input/output widgets. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 WINDNINJATREE_H -#define WINDNINJATREE_H - -#include -#include -#include -#include - -#include "surfaceInput.h" -#include "diurnalInput.h" - -#include "stabilityInput.h" - -#ifdef NINJAFOAM -#include "ninjafoamInput.h" -#include "nativeSolverInput.h" -#endif - -#include "windInput.h" -#include "pointInput.h" -#include "weatherModel.h" -#include "outputMetaData.h" -#include "googleOutput.h" -#include "fbOutput.h" -#include "shapeOutput.h" -#include "pdfOutput.h" -#include "vtkOutput.h" -#include "solvePage.h" - -class WindNinjaTree : public QWidget -{ - Q_OBJECT - - public: - - WindNinjaTree(QWidget *parent = 0); - ~WindNinjaTree(); - //icons - QIcon check, cross, caution, blue, radio, sun; - //tree for navigating - QTreeWidget *tree; - - QSplitter *splitter; - //items - QTreeWidgetItem *mainItem; - QTreeWidgetItem *solverMethodItem; - QTreeWidgetItem *inputItem; - QTreeWidgetItem *surfaceItem; - - //make widgetItem for variable stuff... - QTreeWidgetItem *windItem; - QTreeWidgetItem *spdDirItem; - QTreeWidgetItem *pointItem; - QTreeWidgetItem *modelItem; - QTreeWidgetItem *diurnalItem; - QTreeWidgetItem *stabilityItem; -#ifdef NINJAFOAM - QTreeWidgetItem *ninjafoamItem; - QTreeWidgetItem *nativeSolverItem; -#endif - - //output file items... - QTreeWidgetItem *outputItem; - QTreeWidgetItem *googleItem; - QTreeWidgetItem *fbItem; - QTreeWidgetItem *shapeItem; - QTreeWidgetItem *pdfItem; - QTreeWidgetItem *vtkItem; - - QTreeWidgetItem *solveItem; - - void createTree(); - void createIcons(); - void createInputItems(); - void createSolverMethodItems(); - void createOutputItems(); - void createStack(); - void createConnections(); - - //add stack widget to hold the ui widgets - - QStackedWidget *stack; - - surfaceInput *surface; - diurnalInput *diurnal; - stabilityInput *stability; -#ifdef NINJAFOAM - ninjafoamInput *ninjafoam; - nativeSolverInput *nativesolver; -#endif - windInput *wind; - pointInput *point; - weatherModel *weather; - outputMetaData *output; - googleOutput *google; - fbOutput *fb; - shapeOutput *shape; - pdfOutput *pdf; - vtkOutput *vtk; - solvePage *solve; - - //layout the whole thing - QVBoxLayout *layout; - - public slots: - void updateInterface(); - -}; - -#endif /* WINDNINJATREE_H */ diff --git a/src/gui/consoleDockWidget.cpp b/src/gui/consoleDockWidget.cpp deleted file mode 100644 index 32dde1eb4..000000000 --- a/src/gui/consoleDockWidget.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Console output window - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "consoleDockWidget.h" - -/** - * \brief Construct a default console - * - * Set the docking options and default text edit options. - * - * \param parent Parent of the new widget - */ -ConsoleDockWidget::ConsoleDockWidget(QWidget *parent) : QDockWidget(parent) -{ - setWindowTitle(tr("Console Output")); - consoleTextEdit = new QTextEdit(this); - consoleTextEdit->setDocumentTitle(tr("Console Output")); - consoleTextEdit->setMaximumHeight(80); - consoleTextEdit->setMinimumWidth(480); - consoleTextEdit->setSizePolicy(QSizePolicy::Preferred, - QSizePolicy::Maximum); - setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea | - Qt::NoDockWidgetArea); - - //connect dockAreaChanged and checkSize() - connect(this, SIGNAL(topLevelChanged(bool)), this, SLOT(checkSize(bool))); - - setWidget(consoleTextEdit); -} - -/** - * \briefChange the allowable size of the widget if it is floating - * - * \param float status - */ -void ConsoleDockWidget::checkSize(bool floating) -{ - if(!floating) - { - consoleTextEdit->setMaximumHeight(80); - consoleTextEdit->setMinimumWidth(480); - consoleTextEdit->setSizePolicy(QSizePolicy::Preferred, - QSizePolicy::Maximum); - } - else - { - consoleTextEdit->setMaximumHeight(720); - consoleTextEdit->setMinimumWidth(480); - consoleTextEdit->setSizePolicy(QSizePolicy::Preferred, - QSizePolicy::Preferred); - } -} - diff --git a/src/gui/consoleDockWidget.h b/src/gui/consoleDockWidget.h deleted file mode 100644 index d71a4ffc2..000000000 --- a/src/gui/consoleDockWidget.h +++ /dev/null @@ -1,63 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Console output window - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 CONSOLEDOCKWIDGET_H_ -#define CONSOLEDOCKWIDGET_H_ - -#include -#include -#include - -/** - * \brief Console for the GUI - * - * Provides a method of transferring information to the user - * that may not be in need of alerts such as dialogs. Can - * be useful for debugging. - */ -class ConsoleDockWidget : public QDockWidget -{ - - Q_OBJECT - - public: - ConsoleDockWidget(QWidget *parent = 0); - - QString prompt; /*!< Characters that act as a prompt */ - - QTextEdit *consoleTextEdit; /*!< Holds the edit area */ - QAction *editPromptAction; /*!< Change the prompt */ - QMenu *contextMenu; /*!< Allow the user to print console */ - - public slots: - void checkSize(bool floating); -}; - -#endif /* CONSOLEDOCKWIDGET_H_ */ - diff --git a/src/gui/diurnalInput.cpp b/src/gui/diurnalInput.cpp deleted file mode 100644 index 5b91156b2..000000000 --- a/src/gui/diurnalInput.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: OpenGL implementation for viewing DEM inputs - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "diurnalInput.h" - -/** - * \brief Construct and layout the diurnalInput widget. - * - * This is only a checkable option now. - * - * \param parent parent widget - */ -diurnalInput::diurnalInput(QWidget *parent) : QWidget(parent) -{ - diurnalGroupBox = new QGroupBox(tr("Use Diurnal Wind")); - diurnalGroupBox->setCheckable(true); - diurnalGroupBox->setChecked(false); - diurnalLayout = new QVBoxLayout; - - diurnalGroupBox->setLayout(diurnalLayout); - - layout = new QVBoxLayout; - layout->addWidget(diurnalGroupBox); - layout->addStretch(); - setLayout(layout); -} - diff --git a/src/gui/diurnalInput.h b/src/gui/diurnalInput.h deleted file mode 100644 index d805b4386..000000000 --- a/src/gui/diurnalInput.h +++ /dev/null @@ -1,72 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: OpenGL implementation for viewing DEM inputs - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 DIURNALINPUT_H_ -#define DIURNALINPUT_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "gdal_priv.h" -#include "ogr_srs_api.h" - -#ifndef Q_MOC_RUN -#include "boost/date_time/local_time/local_time.hpp" -#include "boost/date_time/posix_time/posix_time_types.hpp" -#endif - -#include "latLonWidget.h" -#include "timeZoneWidget.h" - -#include "qdebug.h" - -class diurnalInput : public QWidget -{ - Q_OBJECT - - public: - - diurnalInput(QWidget *parent = 0); - QGroupBox *diurnalGroupBox; - QVBoxLayout *diurnalLayout; - QVBoxLayout *layout; - -}; - -#endif /* DIURNALINPUT_H_ */ - diff --git a/src/gui/fbOutput.cpp b/src/gui/fbOutput.cpp deleted file mode 100644 index fab279833..000000000 --- a/src/gui/fbOutput.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Fire behavior output selection widget - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "fbOutput.h" - -/** - * \brief Create the widget for fire behavior outputs. - * - * \param parent parent widget - */ -fbOutput::fbOutput(QWidget *parent) : QWidget(parent) -{ - fbGroupBox = new QGroupBox(tr("Create Fire Behavior Files (*.asc)"), this); - fbGroupBox->setCheckable(true); - fbGroupBox->setChecked(false); - - fbResGroupBox = new QGroupBox(tr("Resolution"), this); - - fbResSpinBox = new QDoubleSpinBox(this); - fbResSpinBox->setRange(1, 5000); - fbResSpinBox->setDecimals(2); - fbResSpinBox->setAccelerated(true); - fbResSpinBox->setValue(200); - fbResSpinBox->setDisabled(true); - - fbMetersRadioButton = new QRadioButton(tr("Meters"), this); - fbMetersRadioButton->setChecked(true); - fbMetersRadioButton->setDisabled(true); - fbFeetRadioButton = new QRadioButton(tr("Feet")); - fbFeetRadioButton->setDisabled(true); - - useMeshResCheckBox = new QCheckBox(tr("Use Mesh Resolution"), this); - useMeshResCheckBox->setChecked(true); - - atmFileCheckBox = new QCheckBox(tr("Create *.atm file(s)"),this); - - //connect spinbox and checkbox - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - fbResSpinBox, SLOT(setDisabled(bool))); - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - fbMetersRadioButton, SLOT(setDisabled(bool))); - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - fbFeetRadioButton, SLOT(setDisabled(bool))); - - fbResLayout = new QGridLayout; - fbResLayout->addWidget(fbResSpinBox, 0, 0); - fbResLayout->addWidget(fbMetersRadioButton, 0, 1); - fbResLayout->addWidget(fbFeetRadioButton, 0, 2); - fbResLayout->addWidget(useMeshResCheckBox, 1, 0); - - fbResGroupBox->setLayout(fbResLayout); - - fbLayout = new QVBoxLayout; - fbLayout->addWidget(fbResGroupBox); - fbLayout->addWidget(atmFileCheckBox); - - fbGroupBox->setLayout(fbLayout); - - mainLayout = new QVBoxLayout; - mainLayout->addWidget(fbGroupBox); - mainLayout->addStretch(); - - setLayout(mainLayout); -} - diff --git a/src/gui/fbOutput.h b/src/gui/fbOutput.h deleted file mode 100644 index 516908524..000000000 --- a/src/gui/fbOutput.h +++ /dev/null @@ -1,63 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Fire behavior output selection widget - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 FBOUTPUT_H_ -#define FBOUTPUT_H_ - -#include -#include -#include -#include - -#include - -#include "WindNinjaInputs.h" - -class fbOutput : public QWidget -{ - Q_OBJECT - -public: - fbOutput(QWidget *parent = 0); - - QGroupBox *fbGroupBox; - QGroupBox *fbResGroupBox; - QDoubleSpinBox *fbResSpinBox; - QRadioButton *fbMetersRadioButton, *fbFeetRadioButton; - QCheckBox *useMeshResCheckBox; - - QCheckBox *atmFileCheckBox; - - QGridLayout *fbResLayout; - QVBoxLayout *fbLayout; - QVBoxLayout *mainLayout; -}; - -#endif /* FBOUTPUT_H_ */ - diff --git a/src/gui/googleOutput.cpp b/src/gui/googleOutput.cpp deleted file mode 100644 index 78d2b33d4..000000000 --- a/src/gui/googleOutput.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Google earth output selection widgetx - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "googleOutput.h" - -googleOutput::googleOutput(QWidget *parent) : QWidget(parent) -{ - googleGroupBox = new QGroupBox(tr("Create Google Earth Files (*.kmz)")); - googleGroupBox->setCheckable(true); - googleGroupBox->setChecked(false); - - vectorGroupBox = new QGroupBox(tr("Vectors")); - //vectorGroupBox->setCheckable(true); - //vectorGroupBox->setChecked(true); - - vectorWidthLabel = new QLabel(tr("Line Width")); - - vectorWidthDoubleSpinBox = new QDoubleSpinBox; - vectorWidthDoubleSpinBox->setRange(1.0, 10.0); - vectorWidthDoubleSpinBox->setDecimals(1); - vectorWidthDoubleSpinBox->setValue(1.0); - vectorWidthDoubleSpinBox->setSingleStep(0.1); - vectorWidthDoubleSpinBox->setAccelerated(true); - - applyVectorScaling = new QCheckBox("Apply Vector Scaling"); - applyVectorScaling->setToolTip("Enable vector scaling to increase line width with wind speed."); - applyVectorScaling->setCheckable(true); - applyVectorScaling->setChecked(false); - - vectorLayout = new QHBoxLayout; - vectorLayout->addWidget(vectorWidthLabel); - vectorLayout->addWidget(vectorWidthDoubleSpinBox); - vectorLayout->addWidget(applyVectorScaling); - vectorLayout->addStretch(); - - vectorGroupBox->setLayout(vectorLayout); - - legendGroupBox = new QGroupBox(tr("Legend")); - //legendGroupBox->setCheckable(true); - //legendGroupBox->setChecked(true); - - uniformRangeRadioButton = new QRadioButton; - uniformRangeRadioButton->setText(tr("Uniform Range")); - uniformRangeRadioButton->setChecked(true); - equalCountRadioButton = new QRadioButton; - equalCountRadioButton->setText(tr("Equal Count")); - - legendOptionLayout = new QVBoxLayout; - legendOptionLayout->addWidget(uniformRangeRadioButton); - legendOptionLayout->addWidget(equalCountRadioButton); - // legendOptionLayout->addStretch(); - legendGroupBox->setLayout(legendOptionLayout); - - contourCheckBox = new QCheckBox(tr("Contours (beta)")); - - contourCheckBox->setChecked(false); - - //hide contour check box for now. - contourCheckBox->setDisabled(true); - - applyConsistentColorScale = new QCheckBox(tr("Use Consistent Color Scale")); - applyConsistentColorScale->setToolTip("Use a consistent color scale across simulations."); - applyConsistentColorScale->setCheckable(true); - applyConsistentColorScale->setChecked(false); - - optionLayout = new QVBoxLayout; - optionLayout->addWidget(vectorGroupBox); - optionLayout->addWidget(legendGroupBox); - optionLayout->addWidget(applyConsistentColorScale); - //optionLayout->addWidget(contourCheckBox); - optionLayout->addStretch(); - - googleResGroupBox = new QGroupBox(tr("Resolution")); - - googleResSpinBox = new QDoubleSpinBox(this); - googleResSpinBox->setRange(1, 5000); - googleResSpinBox->setDecimals(2); - googleResSpinBox->setAccelerated(true); - googleResSpinBox->setValue(200); - googleResSpinBox->setDisabled(true); - //googleResSpinBox->setMaximumSize(googleResSpinBox->sizeHint()); - - googleMetersRadioButton = new QRadioButton(tr("Meters")); - googleMetersRadioButton->setChecked(true); - googleMetersRadioButton->setDisabled(true); - googleFeetRadioButton = new QRadioButton(tr("Feet")); - googleFeetRadioButton->setDisabled(true); - - useMeshResCheckBox = new QCheckBox(tr("Use Mesh Resolution")); - useMeshResCheckBox->setChecked(true); - - //connect checkbox with spin box - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - googleResSpinBox, SLOT(setDisabled(bool))); - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - googleFeetRadioButton, SLOT(setDisabled(bool))); - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - googleMetersRadioButton, SLOT(setDisabled(bool))); - - //color options - colorblindBox= new QGroupBox(tr("Alternative Color Schemes")); - colorblindBox->setCheckable(true); - colorblindBox->setChecked(false); - inputColorblindComboBox = new QComboBox(); - inputColorblindComboBox->addItem(tr("Default")); - inputColorblindComboBox->addItem(tr("ROPGW (Red Orange Pink Green White)")); - inputColorblindComboBox->addItem(tr("Oranges")); - inputColorblindComboBox->addItem(tr("Blues")); - inputColorblindComboBox->addItem(tr("Pinks")); - inputColorblindComboBox->addItem(tr("Greens")); - inputColorblindComboBox->addItem(tr("Magic Beans")); - inputColorblindComboBox->addItem(tr("Pink to Green")); - - colorLayout=new QGridLayout; - colorLayout->addWidget(inputColorblindComboBox,0,0); - colorblindBox->setLayout(colorLayout); - //end color options - - resLayout = new QGridLayout; - resLayout->addWidget(googleResSpinBox, 0, 0); - resLayout->addWidget(googleMetersRadioButton, 0, 1); - resLayout->addWidget(googleFeetRadioButton, 0, 2); - resLayout->addWidget(useMeshResCheckBox, 1, 0); - - googleResGroupBox->setLayout(resLayout); - - pageLayout = new QVBoxLayout; - pageLayout->addLayout(optionLayout); - pageLayout->addWidget(googleResGroupBox); - pageLayout->addWidget(colorblindBox); - pageLayout->addStretch(); - - googleGroupBox->setLayout(pageLayout); - - mainLayout = new QVBoxLayout; - mainLayout->addWidget(googleGroupBox); - mainLayout->addStretch(); - - setLayout(mainLayout); -} - -void googleOutput::setColorScheme(int choice) -{ - if(choice==1) - { - - } -} diff --git a/src/gui/googleOutput.h b/src/gui/googleOutput.h deleted file mode 100644 index 34ba1ecda..000000000 --- a/src/gui/googleOutput.h +++ /dev/null @@ -1,82 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Google earth output selection widgetx - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 GOOGLEOUTPUT_H -#define GOOGLEOUTPUT_H - -#include -#include -#include -#include -#include -#include - -#include -#include - -class googleOutput : public QWidget -{ - Q_OBJECT - - public: - googleOutput(QWidget *parent = 0); - - QGroupBox *googleGroupBox; - QGroupBox *vectorGroupBox; - QLabel *vectorWidthLabel; - QDoubleSpinBox *vectorWidthDoubleSpinBox; - QCheckBox *applyVectorScaling; - QGroupBox *legendGroupBox; - QRadioButton *uniformRangeRadioButton,* equalCountRadioButton; - QCheckBox *applyConsistentColorScale; - QCheckBox *contourCheckBox; - QGroupBox *googleResGroupBox; - QDoubleSpinBox *googleResSpinBox; - QRadioButton *googleMetersRadioButton, *googleFeetRadioButton; - QCheckBox *useMeshResCheckBox; - - //alternative color Options - QGroupBox *colorblindBox; - QComboBox *inputColorblindComboBox; - - QGridLayout *colorLayout; - - QHBoxLayout *vectorLayout; - QVBoxLayout *legendOptionLayout; - QVBoxLayout *optionLayout; - QGridLayout *resLayout; - QVBoxLayout *pageLayout; - QVBoxLayout *mainLayout; - - public slots: - void setColorScheme(int choice); - -}; - -#endif /* GOOGLEOUTPUT_H */ diff --git a/src/gui/latLonWidget.cpp b/src/gui/latLonWidget.cpp deleted file mode 100644 index 5672790b9..000000000 --- a/src/gui/latLonWidget.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Handle lat/lon dd:mm:ss/dd:mm.mm/dd.dd conversions for diurnal - * inputs - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "latLonWidget.h" -dmsWidget::dmsWidget( QWidget *parent ) : QWidget( parent ) -{ - - //latitude - latGroupBox = new QGroupBox( tr( "Latitude" ), this ); - - latDegrees = new QSpinBox; - latDegrees->setRange( -90, 90 ); - latDegrees->setValue( 0 ); - latDegrees->setSuffix( "\x00B0" ); - latDegrees->setAccelerated( true ); - - latMinutes = new QSpinBox; - latMinutes->setRange( 0, 60 ); - latMinutes->setValue( 0 ); - latMinutes->setSuffix( "'" ); - latMinutes->setAccelerated( true ); - - latSeconds = new QDoubleSpinBox; - latSeconds->setDecimals( 3 ); - latSeconds->setRange( 0, 60 ); - latSeconds->setValue( 0 ); - latSeconds->setSuffix( "\"" ); - latSeconds->setAccelerated( true ); - - latLayout = new QHBoxLayout; - latLayout->addWidget( latDegrees ); - latLayout->addWidget( latMinutes ); - latLayout->addWidget( latSeconds ); - - latGroupBox->setLayout( latLayout ); - - //longitude - lonGroupBox = new QGroupBox( tr( "Longitude" ) ); - - lonDegrees = new QSpinBox; - lonDegrees->setRange( -180, 180 ); - lonDegrees->setValue( 0 ); - lonDegrees->setSuffix( "\x00B0" ); - lonDegrees->setAccelerated( true ); - - lonMinutes = new QSpinBox; - lonMinutes->setRange( 0, 60 ); - lonMinutes->setValue( 0 ); - lonMinutes->setSuffix( "'" ); - lonMinutes->setAccelerated( true ); - - lonSeconds = new QDoubleSpinBox; - lonSeconds->setDecimals( 3 ); - lonSeconds->setRange( 0, 60 ); - lonSeconds->setValue( 0 ); - lonSeconds->setSuffix( "\"" ); - lonSeconds->setAccelerated( true ); - - lonLayout = new QHBoxLayout; - lonLayout->addWidget( lonDegrees ); - lonLayout->addWidget( lonMinutes ); - lonLayout->addWidget( lonSeconds ); - - lonGroupBox->setLayout( lonLayout ); - - layout =new QHBoxLayout; - layout->addWidget( latGroupBox ); - layout->addWidget( lonGroupBox ); - - setLayout( layout ); -} - -dmWidget::dmWidget( QWidget *parent ) : QWidget( parent ) -{ - //latitude - latGroupBox = new QGroupBox( tr( "Latitude" ) ); - - latDegrees = new QSpinBox; - latDegrees->setRange( -90, 90 ); - latDegrees->setValue( 0 ); - latDegrees->setSuffix( "\x00B0" ); - latDegrees->setAccelerated( true ); - - latMinutes = new QDoubleSpinBox; - latMinutes->setDecimals( 5 ); - latMinutes->setRange( 0, 60 ); - latMinutes->setValue( 0 ); - latMinutes->setSuffix( "'" ); - latMinutes->setAccelerated( true ); - - latLayout = new QHBoxLayout; - latLayout->addWidget( latDegrees ); - latLayout->addWidget( latMinutes ); - - latGroupBox->setLayout( latLayout ); - - //longitude - lonGroupBox = new QGroupBox( tr( "Longitude" ) ); - - lonDegrees = new QSpinBox; - lonDegrees->setRange( -180, 180 ); - lonDegrees->setValue( 0 ); - lonDegrees->setSuffix( "\x00B0" ); - lonDegrees->setAccelerated( true ); - - lonMinutes = new QDoubleSpinBox; - lonMinutes->setDecimals( 5 ); - lonMinutes->setRange( 0, 60 ); - lonMinutes->setValue( 0 ); - lonMinutes->setSuffix( "'" ); - lonMinutes->setAccelerated( true ); - - lonLayout = new QHBoxLayout; - lonLayout->addWidget( lonDegrees ); - lonLayout->addWidget( lonMinutes ); - - lonGroupBox->setLayout( lonLayout ); - - layout = new QHBoxLayout; - layout->addWidget( latGroupBox ); - layout->addWidget( lonGroupBox ); - - setLayout( layout ); -} - -ddWidget::ddWidget( QWidget *parent ) : QWidget( parent ) -{ - //latitude - latGroupBox = new QGroupBox( tr( "Latitude" ) ); - - latDegrees = new QDoubleSpinBox; - latDegrees->setDecimals( 10 ); - latDegrees->setRange( -90.0, 90.0 ); - latDegrees->setValue( 0 ); - latDegrees->setSuffix( "\x00B0" ); - latDegrees->setAccelerated( true ); - - latLayout = new QHBoxLayout; - latLayout->addWidget( latDegrees ); - - latGroupBox->setLayout( latLayout ); - - //longitude - lonGroupBox = new QGroupBox( tr( "Longitude" ) ); - - lonDegrees = new QDoubleSpinBox; - lonDegrees->setDecimals( 10 ); - lonDegrees->setRange( -180.0, 180.0 ); - lonDegrees->setValue( 0 ); - lonDegrees->setSuffix( "\x00B0" ); - lonDegrees->setAccelerated( true ); - - lonLayout = new QHBoxLayout; - lonLayout->addWidget( lonDegrees ); - - lonGroupBox->setLayout( lonLayout ); - - layout = new QHBoxLayout; - layout->addWidget( latGroupBox ); - layout->addWidget( lonGroupBox ); - - setLayout( layout ); -} - -latLonWidget::latLonWidget( QString title, QWidget *parent ) : QWidget( parent ) -{ - latitude = longitude = 0; - - latLonGroupBox = new QGroupBox( title ); - - latLonFormatGroupBox = new QGroupBox( tr( "Lat/Lon Format" ) ); - - dmsRadio = new QRadioButton( tr( "DD MM SS.SS" ) ); - dmsRadio->setChecked( true ); - dmRadio = new QRadioButton( tr( "DD MM.MM" ) ); - ddRadio = new QRadioButton( tr( "DD.DD" ) ); - - stackedWidget = new QStackedWidget( this ); - dms = new dmsWidget; - dm = new dmWidget; - dd = new ddWidget; - - stackedWidget->addWidget( dms ); - stackedWidget->addWidget( dm ); - stackedWidget->addWidget( dd ); - - connect( dmsRadio, SIGNAL( toggled( bool ) ), - this, SLOT( checkLatLonFormat( ) ) ); - connect( dmRadio, SIGNAL( toggled( bool ) ), - this, SLOT( checkLatLonFormat( ) ) ); - connect( ddRadio, SIGNAL( toggled( bool ) ), - this, SLOT( checkLatLonFormat( ) ) ); - - //connect all location spin boxes to the update( ) methods - //dms lat - connect( dms->latDegrees, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - connect( dms->latMinutes, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - connect( dms->latSeconds, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - //dms lon - connect( dms->lonDegrees, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - connect( dms->lonMinutes, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - connect( dms->lonSeconds, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - //dm lat - connect( dm->latDegrees, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - connect( dm->latMinutes, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - //dm lon - connect( dm->lonDegrees, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - connect( dm->lonMinutes, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - - //dd lat - connect( dd->latDegrees, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - //dd lon - connect( dd->lonDegrees, SIGNAL( editingFinished( ) ), - this, SLOT( updateLatLon( ) ) ); - - datumComboBox = new QComboBox( this ); - datumComboBox->addItem( "Datum..." ); - datumComboBox->addItem( "WGS84" ); - datumComboBox->addItem( "NAD83" ); - datumComboBox->addItem( "NAD27" ); - - latLonFormatLayout = new QHBoxLayout; - latLonFormatLayout->addWidget( dmsRadio ); - latLonFormatLayout->addWidget( dmRadio ); - latLonFormatLayout->addWidget( ddRadio ); - - latLonFormatGroupBox->setLayout( latLonFormatLayout ); - - latLonLayout = new QVBoxLayout; - latLonLayout->addWidget( latLonFormatGroupBox ); - latLonLayout->addWidget( stackedWidget ); - latLonLayout->addWidget( datumComboBox ); - latLonGroupBox->setLayout( latLonLayout ); - - layout = new QVBoxLayout; - layout->addWidget( latLonGroupBox ); - setLayout( layout ); -} - -void latLonWidget::checkLatLonFormat() -{ - int index = -1; - if( dmsRadio->isChecked( ) ) - index = 0; - else if( dmRadio->isChecked( ) ) - index = 1; - else if( ddRadio->isChecked( ) ) - index = 2; - else - index = 0; - - stackedWidget->setCurrentIndex( index ); -} - -void latLonWidget::updateLatLon( ) -{ - //check lat/lon format - if( dmsRadio->isChecked( ) ) { - latitude = dms2ddLatitude( dms->latDegrees->value( ), - dms->latMinutes->value( ), - dms->latSeconds->value( ) ); - longitude = dms2ddLongitude( dms->lonDegrees->value( ), - dms->lonMinutes->value( ), - dms->lonSeconds->value( ) ); - updateLatLonWidget( 0 ); - } - else if( dmRadio->isChecked( ) ) { - latitude = dm2ddLatitude( dm->latDegrees->value( ), - dm->latMinutes->value( ) ); - longitude = dm2ddLongitude( dm->lonDegrees->value( ), - dm->lonMinutes->value( ) ); - updateLatLonWidget( 1 ); - } - else if( ddRadio->isChecked( ) ) { - latitude = dd->latDegrees->value( ); - longitude = dd->lonDegrees->value( ); - updateLatLonWidget( 2 ); - } - //latLonFoundLabel->setText( "" ); -} - -double latLonWidget::dms2ddLatitude( int dd, int mm, double ss ) -{ - double x, xx, lat = 0.0; - double d = dd; - double m = mm; - double s = ss; - if( d >= 0 ){ - xx = s / 60.0; - m += xx; - x = m / 60.0; - lat = d + x; - } - else { - xx = s / 60.0; - m += xx; - x = m / 60.0; - lat = d - x; - } - return lat; -} - -double latLonWidget::dms2ddLongitude( int dd, int mm, double ss ) -{ - double d = dd; - double m = mm; - double s = ss; - double x, xx, lon = 0.0; - if( d >= 0 ) { - xx = s / 60.0; - m += xx; - x = m / 60.0; - lon = d + x; - } - else { - xx = s / 60.0; - m += xx; - x = m / 60.0; - lon = d - x; - } - return lon; -} - -double latLonWidget::dm2ddLatitude( int dd, double mm ) -{ - double x, lat = 0.0; - double d = dd; - double m = mm; - - if( d >= 0.0 ) { - x = m / 60.0; - lat = d + x; - } - else { - x = m / 60.0; - lat = d - x; - } - return lat; -} - -double latLonWidget::dm2ddLongitude( int dd, double mm ) -{ - double x, lon = 0.0; - double d = dd; - double m = mm; - - if( d >= 0.0 ) { - x = m / 60.0; - lon = d + x; - } - else { - x = m / 60.0; - lon = d - x; - } - return lon; -} - -void latLonWidget::updateLatLonWidget( int notMe ) -{ - if( notMe == 0 ) { - dm->latDegrees->setValue( ( int )latitude ); - dm->latMinutes->setValue( ( fabs( latitude ) - ( int )fabs( latitude ) ) * 60 ); - - dm->lonDegrees->setValue( ( int )longitude ); - dm->lonMinutes->setValue( ( fabs( longitude ) - ( int )fabs( longitude ) ) * 60 ); - - dd->latDegrees->setValue( latitude ); - dd->lonDegrees->setValue( longitude ); - } - else if( notMe == 1 ) { - double d, m, s; - d = ( int )latitude; - m = ( fabs( latitude ) - fabs( d ) ) * 60; - s = ( m - ( int )m ) * 60; - m = ( int )m; - - dms->latDegrees->setValue( d ); - dms->latMinutes->setValue( m ); - dms->latSeconds->setValue( s ); - - d = ( int )longitude; - m = ( fabs( longitude ) - fabs( d ) ) * 60; - s = ( m - ( int )m ) * 60; - m = ( int )m; - - - dms->lonDegrees->setValue( d ); - dms->lonMinutes->setValue( m ); - dms->lonSeconds->setValue( s ); - - dd->latDegrees->setValue( latitude ); - dd->lonDegrees->setValue( longitude ); - } - else if( notMe == 2 ) { - dm->latDegrees->setValue( ( int )latitude ); - dm->latMinutes->setValue( ( fabs( latitude ) - ( int )fabs( latitude ) ) * 60 ); - - dm->lonDegrees->setValue( ( int )longitude ); - dm->lonMinutes->setValue( ( fabs( longitude ) - ( int )fabs( longitude ) ) * 60 ); - - double d, m, s; - d = ( int )latitude; - m = ( fabs( latitude ) - fabs( d ) ) * 60; - s = ( m - ( int )m ) * 60; - m = ( int )m; - - dms->latDegrees->setValue( d ); - dms->latMinutes->setValue( m ); - dms->latSeconds->setValue( s ); - - d = ( int )longitude; - m = ( fabs( longitude ) - fabs( d ) ) * 60; - s = ( m - ( int )m ) * 60; - m = ( int )m; - - dms->lonDegrees->setValue( d ); - dms->lonMinutes->setValue( m ); - dms->lonSeconds->setValue( s ); - } - else { - //update all from internal lat/lon - //degrees/minutes/seconds - double d, m, s; - d = ( int )latitude; - m = ( fabs( latitude ) - fabs( d ) ) * 60; - s = ( m - ( int )m ) * 60; - m = ( int )m; - - dms->latDegrees->setValue( d ); - dms->latMinutes->setValue( m ); - dms->latSeconds->setValue( s ); - - d = ( int )longitude; - m = ( fabs( longitude ) - fabs( d ) ) * 60; - s = ( m - ( int )m ) * 60; - m = ( int )m; - - dms->lonDegrees->setValue( d ); - dms->lonMinutes->setValue( m ); - dms->lonSeconds->setValue( s ); - - //degrees minutes - dm->latDegrees->setValue( ( int )latitude ); - dm->latMinutes->setValue( ( fabs( latitude ) - ( int )fabs( latitude ) ) * 60 ); - - dm->lonDegrees->setValue( ( int )longitude ); - dm->lonMinutes->setValue( ( fabs( longitude ) - ( int )fabs( longitude ) ) * 60 ); - - //decimal degrees - dd->latDegrees->setValue( latitude ); - dd->lonDegrees->setValue( longitude ); - } -} diff --git a/src/gui/latLonWidget.h b/src/gui/latLonWidget.h deleted file mode 100644 index 06eda7d32..000000000 --- a/src/gui/latLonWidget.h +++ /dev/null @@ -1,127 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Handle lat/lon dd:mm:ss/dd:mm.mm/dd.dd conversions for diurnal - * inputs - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 LATLONWIDGET_H -#define LATLONWIDGET_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -class dmsWidget : public QWidget -{ - Q_OBJECT - -public: - dmsWidget(QWidget *parent = 0); - QGroupBox *latGroupBox, *lonGroupBox; - QSpinBox *latDegrees, *lonDegrees; - QSpinBox *latMinutes, *lonMinutes; - - QDoubleSpinBox *latSeconds, *lonSeconds; - - QHBoxLayout *latLayout, *lonLayout, *layout; - -}; - -class dmWidget : public QWidget -{ - Q_OBJECT -public: - dmWidget(QWidget *parent = 0); - QGroupBox *latGroupBox, *lonGroupBox; - QSpinBox *latDegrees, *lonDegrees; - QDoubleSpinBox *latMinutes, *lonMinutes; - - QHBoxLayout *latLayout, *lonLayout, *layout; -}; - -class ddWidget : public QWidget -{ - Q_OBJECT - -public: - ddWidget(QWidget *parent = 0); - QGroupBox *latGroupBox, *lonGroupBox; - QDoubleSpinBox *latDegrees, *lonDegrees; - - QHBoxLayout *latLayout, *lonLayout, *layout; - -}; - -class latLonWidget : public QWidget -{ - Q_OBJECT -public: - latLonWidget(QString title, QWidget *parent = 0); - - //store lat lon in dd always - double latitude, longitude; - - QGroupBox *latLonGroupBox; - QGroupBox *latLonFormatGroupBox; - QLabel *latLonFormatLabel; - QRadioButton *dmsRadio, *dmRadio, *ddRadio; - - //conversions - double dms2ddLatitude(int, int, double); - double dms2ddLongitude(int, int, double); - double dm2ddLatitude(int, double); - double dm2ddLongitude(int, double); - - QStackedWidget *stackedWidget; - - dmsWidget *dms; - dmWidget *dm; - ddWidget *dd; - - QComboBox *datumComboBox; - - QHBoxLayout *latLonFormatLayout; - QVBoxLayout *latLonLayout; - - QVBoxLayout *layout; - -public slots: - void checkLatLonFormat(); - void updateLatLon(); - void updateLatLonWidget(int notMe); - -}; - -#endif /* LATLONWIDGET_H */ diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp deleted file mode 100644 index 3d19431b6..000000000 --- a/src/gui/mainWindow.cpp +++ /dev/null @@ -1,3704 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Main window and parent to all other widgets - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "mainWindow.h" - -mainWindow::mainWindow(QWidget *parent) -: QMainWindow(parent) -{ - std::string tzfile = FindDataPath( "date_time_zonespec.csv" ); - if( tzfile == "" ) - { - throw std::runtime_error( "Could not find supporting data files, " \ - "try setting WINDNINJA_DATA." ); - } - lineNumber = 1; - GDALProjRef = ""; - - progressDialog = new QProgressDialog(this); - progressDialog->setMinimumDuration(1); - progressDialog->setAutoClose(false); - progressDialog->setAutoReset(false); - progressDialog->setWindowModality(Qt::ApplicationModal); - - runProgress = 0; - totalProgress = 0; - mainWindow::progressLog; - - noGoogleCellSize = 30.0; - - //set defaults for some variables -#ifdef NINJAFOAM - existingCaseDir = ""; -#endif - inputFileName = ""; - inputFileDir = ""; - inputFileType = -1; - shortInputFileName = ""; - prjFileName = ""; - hasPrj = false; - - GDALCenterLat = GDALCenterLon = 0; - hasGDALCenter = false; - - tree = new WindNinjaTree; - - createConsole(); - createActions(); - createMenus(); - - createTimers(); - - createConnections(); - - setCentralWidget(tree); - - //threading - sThread = new solveThread; - - meshCellSize = 200.0; - -#ifdef PHONE_HOME_QUERIES_ENABLED - checkMessages(); -#endif - - QString v(NINJA_VERSION_STRING); - v = "Welcome to WindNinja " + v; - - - writeToConsole(v, Qt::blue); - - //get and set working directory for open dialogs and tutorials. - cwd = QDir::current(); - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - //pwd = QDir::current(); - pwd.cdUp(); - - writeToConsole(cwd.currentPath()); - cwd.cd("../example-files"); - //writeToConsole(cwd.absolutePath()); - - readSettings(); - - okToContinueCheck = false; - - computeCellSize(tree->surface->meshResComboBox->currentIndex()); - - checkAllItems(); - army = NULL; -} - -/* -** Check for version updates, or messages from the server. -*/ -#ifdef PHONE_HOME_QUERIES_ENABLED -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; - } - } - } -} - -#endif - -bool mainWindow::okToContinue() -{ - if(okToContinueCheck) - { - int r = QMessageBox::warning(this, tr("WindNinja"), - tr("Are you sure you want to exit?"), - QMessageBox::Yes | - QMessageBox::No | - QMessageBox::Cancel); - if(r == QMessageBox::Yes) - return true; - else if(r == QMessageBox::No || r == QMessageBox::Cancel) - return false; - else - return false; - } - else - return true; -} - -void mainWindow::closeEvent(QCloseEvent *event) -{ - if(okToContinue()) - writeSettings(); - else - event->ignore(); -} - -void mainWindow::writeSettings() -{ - writeToConsole("Saving settings..."); - QSettings settings(QSettings::UserScope, "Firelab", "WindNinja"); - settings.setDefaultFormat(QSettings::IniFormat); - //input file path - writeToConsole(inputFileDir.absolutePath()); - settings.setValue("inputFileDir", inputFileDir.absolutePath()); - //veg choice - settings.setValue("vegChoice", tree->surface->roughnessComboBox-> - currentIndex()); - //mesh choice - settings.setValue("meshChoice", tree->surface->meshResComboBox-> - currentIndex()); - //mesh units - settings.setValue("meshUnits", tree->surface->meshMetersRadioButton-> - isChecked()); - //number of processors - settings.setValue("nProcessors", tree->solve->numProcSpinBox->value()); - - //time zone - settings.setValue("timeZone", - tree->surface->timeZone->tzComboBox->currentText() ); - - settings.setValue("pointFile", tree->point->stationFileName ); - - settings.setValue("customRes", tree->surface->meshResDoubleSpinBox->value()); - - writeToConsole("Settings saved."); -} - -void mainWindow::readSettings() -{ - QSettings settings(QSettings::UserScope, "Firelab", "WindNinja"); - settings.setDefaultFormat(QSettings::IniFormat); - if(settings.contains("inputFileDir")) - { - inputFileDir = settings.value("inputFileDir").toString(); - } - else - { - std::string oTmpPath = FindNinjaRootDir(); - inputFileDir = CPLFormFilename(oTmpPath.c_str(), "etc/windninja/example-files", NULL); - } - if(settings.contains("vegChoice")) - { - tree->surface->roughnessComboBox-> - setCurrentIndex(settings.value("vegChoice").toInt()); - } - if(settings.contains("meshChoice")) - { - int choice = settings.value("meshChoice").toInt(); - tree->surface->meshResComboBox->setCurrentIndex(choice); - if(choice == 4 && settings.contains("customRes")) - { - double r = settings.value("customRes").toDouble(); - tree->surface->meshResDoubleSpinBox->setValue(r); - } - } - if(settings.contains("meshUnits")) - { - if(settings.value("meshUnits").toBool()) - tree->surface->meshMetersRadioButton->setChecked(true); - else - tree->surface->meshFeetRadioButton->setChecked(true); - } - if(settings.contains("nProcessors")) - { - tree->solve->numProcSpinBox-> - setValue(settings.value("nProcessors").toInt()); - } - if(settings.contains("timeZone")) - { - QString v = settings.value("timeZone").toString(); - int index = tree->surface->timeZone->tzComboBox->findText(v); - if(index == -1) - tree->surface->timeZone->tzCheckBox->setChecked( true ); - index = tree->surface->timeZone->tzComboBox->findText(v); - if( index == 0 ) - tree->surface->timeZone->tzComboBox->setCurrentIndex(index + 1); - tree->surface->timeZone->tzComboBox->setCurrentIndex(index); - } - else - { - tree->surface->timeZone->tzComboBox->setCurrentIndex(2); - tree->surface->timeZone->tzComboBox->setCurrentIndex(1); - } - if(settings.contains("pointFile")) - { - QString f = settings.value("pointFile").toString(); - tree->point->stationFileName = f; - } -} - -void mainWindow::createActions() -{ - //open surface file action - openInputFileAction = new QAction(tr("Open &Elevation Input File"), this); - openInputFileAction->setIcon(QIcon(":folder_page.png")); - openInputFileAction->setShortcut(tr("Ctrl+D")); - openInputFileAction->setStatusTip(tr("Open Surface Input File")); - connect(openInputFileAction, SIGNAL(triggered()), - this, SLOT(openInputFile())); - - //exitAction - exitAction = new QAction(tr("E&xit"), this); - exitAction->setIcon(QIcon(":cancel.png")); - exitAction->setShortcut(tr("Alt+F4")); - exitAction->setStatusTip(tr("Exit WindNinja")); - connect(exitAction, SIGNAL(triggered()), - this, SLOT(close())); - - //write console output action - writeConsoleOutputAction = new QAction(tr("Write console output to file..."), this); - writeConsoleOutputAction->setIcon(QIcon(":disk.png")); - writeConsoleOutputAction->setShortcut(tr("Ctrl+W")); - writeConsoleOutputAction->setStatusTip(tr("Write the console text to disk")); - connect(writeConsoleOutputAction, SIGNAL(triggered()), this, - SLOT(writeConsoleOutput())); - - //resample data action - resampleAction = new QAction(tr("&Resample Data"), this); - resampleAction->setIcon(QIcon(":resample.png")); - resampleAction->setShortcut(tr("Ctrl+R")); - resampleAction->setStatusTip(tr("Resample Existing Data")); - connect(resampleAction, SIGNAL(triggered()), this, SLOT(resampleData())); - - //write a blank weather station file for point initialization - - writeBlankStationFileAction = new QAction(tr("Write a blank station file"), - this); - writeBlankStationFileAction->setIcon(QIcon(":disk.png")); - writeBlankStationFileAction->setShortcut(tr("Ctrl+Alt+W")); - writeBlankStationFileAction->setStatusTip(tr("Write a blank station file for point initialization")); - connect(writeBlankStationFileAction, SIGNAL(triggered()), this, - SLOT(writeBlankStationFile())); - - setConfigAction = new QAction(tr("Set Configuration Option"), this); - setConfigAction->setIcon(QIcon(":cog_go.png")); - setConfigAction->setStatusTip(tr("Set advanced runtime configuration options")); - connect(setConfigAction, SIGNAL(triggered()), this, SLOT(SetConfigOption())); - - //wind ninja help action - windNinjaHelpAction = new QAction(tr("WindNinja &Help"), this); - windNinjaHelpAction->setIcon(QIcon(":help.png")); - windNinjaHelpAction->setShortcut(tr("Ctrl+H")); - windNinjaHelpAction->setStatusTip(tr("Get Help with the WindNinja")); - connect(windNinjaHelpAction, SIGNAL(triggered()), this, - SLOT(windNinjaHelp())); - - //arcMap action - displayShapeFileProAction = new QAction(tr("How to Display Shapefiles in ArcGIS Pro"), this); - displayShapeFileProAction->setIcon(QIcon(":page_white_acrobat.png")); - connect(displayShapeFileProAction, SIGNAL(triggered()), this, - SLOT(displayArcGISPro())); - - //open wind ninja tutorial 1 action - tutorial1Action = new QAction(tr("Tutorial &1:The Basics"), this); - tutorial1Action->setIcon(QIcon(":page_white_acrobat.png")); - tutorial1Action->setShortcut(tr("Ctrl+1")); - tutorial1Action->setStatusTip(tr("Get started using the WindNinja")); - connect(tutorial1Action, SIGNAL(triggered()), this, SLOT(tutorial1())); - - //open wind ninja tutorial 2 action - tutorial2Action = new QAction(tr("Tutorial &2: Diurnal Winds and Non-neutral Stability"), this); - tutorial2Action->setIcon(QIcon(":page_white_acrobat.png")); - tutorial2Action->setShortcut(tr("Ctrl+2")); - tutorial2Action->setStatusTip(tr("Using Diurnal Winds in WindNinja")); - connect(tutorial2Action, SIGNAL(triggered()), this, SLOT(tutorial2())); - - //open wind ninja tutorial 3 action - tutorial3Action = new QAction(tr("Tutorial &3:Point Initialization"), this); - tutorial3Action->setIcon(QIcon(":page_white_acrobat.png")); - tutorial3Action->setShortcut(tr("Ctrl+3")); - tutorial3Action->setStatusTip(tr("Using Point Initialization in WindNinja")); - connect(tutorial3Action, SIGNAL(triggered()), this, SLOT(tutorial3())); - - //open wind ninja tutorial 4 action - tutorial4Action = new QAction(tr("Tutorial &4:Weather Model Initialization"), this); - tutorial4Action->setIcon(QIcon(":page_white_acrobat.png")); - tutorial4Action->setShortcut(tr("Ctrl+4")); - tutorial4Action->setStatusTip(tr("Using Weather Model Initialization in WindNinja")); - connect(tutorial4Action, SIGNAL(triggered()), this, SLOT(tutorial4())); - - //dem downloader - downloadDemAction = new QAction(tr("DEM Download Instructions"), this); - downloadDemAction->setIcon(QIcon(":page_white_acrobat.png")); - downloadDemAction->setStatusTip(tr("How to download DEM data with WindNinja")); - connect(downloadDemAction, SIGNAL(triggered()), this, SLOT(demDownload())); - - //dem downloader cli - fetchDemAction = new QAction(tr("fetch_dem Instructions"), this); - fetchDemAction->setIcon(QIcon(":page_white_acrobat.png")); - fetchDemAction->setStatusTip(tr("How to download DEM data with fetch_dem")); - connect(fetchDemAction, SIGNAL(triggered()), this, SLOT(fetchDem())); - - - //open wind ninja tutorial 4 action - cliInstructionsAction = new QAction(tr("Command Line Interface Instructions"), - this); - cliInstructionsAction->setIcon(QIcon(":page_white_acrobat.png")); - cliInstructionsAction->setShortcut(tr("Ctrl+l")); - cliInstructionsAction->setStatusTip(tr("Using the Command Line Interface")); - connect(cliInstructionsAction, SIGNAL(triggered()), - this, SLOT(cliInstructions())); - - //about wn action - aboutWindNinjaAction = new QAction(tr("&About WindNinja"), this); - aboutWindNinjaAction->setIcon(QIcon(":help.png")); - aboutWindNinjaAction->setShortcut(tr("Ctrl+A")); - aboutWindNinjaAction->setStatusTip(tr("About the WindNinja")); - connect(aboutWindNinjaAction, SIGNAL(triggered()), this, - SLOT(aboutWindNinja())); - - //citation wn action - citeWindNinjaAction = new QAction(tr("&Citation"), this); - citeWindNinjaAction->setIcon(QIcon(":citation.png")); - citeWindNinjaAction->setShortcut(tr("Ctrl+T")); - citeWindNinjaAction->setStatusTip(tr("How to cite WindNinja")); - connect(citeWindNinjaAction, SIGNAL(triggered()), this, - SLOT(citeWindNinja())); - - //support email action - supportEmailAction = new QAction(tr("&Email Us"), this); - supportEmailAction->setIcon(QIcon(":email.png")); - supportEmailAction->setShortcut(tr("Ctrl+E")); - supportEmailAction->setStatusTip(tr("Email bugs/comments/questions to the WindNinja team")); - connect(supportEmailAction, SIGNAL(triggered()), this, - SLOT(supportEmail())); - - submitBugReportAction = new QAction(tr("Submit Bug Report"), this); - submitBugReportAction->setIcon(QIcon(":bug_link.png")); - submitBugReportAction->setShortcut(tr("Ctrl+B")); - submitBugReportAction->setStatusTip(tr("Submit a bug report via GitHub (requires GitHub ID)")); - connect(submitBugReportAction, SIGNAL(triggered()), this, - SLOT(bugReport())); - - //about qt action - aboutQtAction = new QAction(tr("About &Qt"), this); - aboutQtAction->setStatusTip(tr("Show the Qt library's About box")); - connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); - - //test action to test slots. - testAction = new QAction(tr("Test"), this); - connect(testAction, SIGNAL(triggered()), this, SLOT(test())); -} - -void mainWindow::createMenus() -{ - //file menu - fileMenu = menuBar()->addMenu(tr("&File")); - fileMenu->addAction(openInputFileAction); - fileMenu->addSeparator(); - fileMenu->addAction(writeConsoleOutputAction); - fileMenu->addSeparator(); - fileMenu->addAction(exitAction); - //options menu, used member in QDockWidget to return QAction to toggle. - optionsMenu = menuBar()->addMenu(tr("&Options")); - optionsMenu->addAction(console->toggleViewAction()); - - //tools menu - toolsMenu = menuBar()->addMenu(tr("&Tools")); - //toolsMenu->addAction(resampleAction); - toolsMenu->addAction(writeBlankStationFileAction); - toolsMenu->addAction(setConfigAction); - - //help/tutorial menus - helpMenu = menuBar()->addMenu(tr("&Help")); - shapeSubMenu = helpMenu->addMenu(tr("Displaying Shapefiles")); - shapeSubMenu->addAction(displayShapeFileProAction); - QMenu *shapeSubMenu; - tutorialSubMenu = helpMenu->addMenu(tr("Tutorials")); - tutorialSubMenu->addAction(tutorial1Action); - tutorialSubMenu->addAction(tutorial2Action); - tutorialSubMenu->addAction(tutorial3Action); - tutorialSubMenu->addAction(tutorial4Action); - helpMenu->addAction(downloadDemAction); - helpMenu->addAction(fetchDemAction); - helpMenu->addAction(cliInstructionsAction); - helpMenu->addAction(aboutWindNinjaAction); - helpMenu->addAction(citeWindNinjaAction); - helpMenu->addAction(supportEmailAction); - helpMenu->addAction(submitBugReportAction); - - //context menu for text edit - //console->consoleTextEdit->addAction(writeConsoleOutputAction); - //console->consoleTextEdit->setContextMenuPolicy(Qt::ActionsContextMenu); - - - //helpMenu->addAction(aboutQtAction); - //helpMenu->addAction(testAction); -} - -/** - * Create connections for mainWindow - * - */ -void mainWindow::createConnections() -{ - // Connections for DEM Downloader Button - connect(tree->surface->downloadDEMButton, SIGNAL(clicked()), - this, SLOT(openDEMDownloader())); - // - connect(&fileWatcher, SIGNAL(fileChanged(QString)), - this, SLOT(inputFileDeleted())); - -#ifdef NINJAFOAM - //Connect input file open button to dialog box - connect(tree->surface->foamCaseOpenToolButton, SIGNAL(clicked()), - this, SLOT(openExistingCase())); -#endif - //Connect input file open button to dialog box - connect(tree->surface->inputFileOpenToolButton, SIGNAL(clicked()), - this, SLOT(openInputFile())); - //connect signals/slots - //connect combo to change mesh res - connect(tree->surface->meshResComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(checkMeshCombo())); - //also connect the radio button for feet display - connect(tree->surface->meshMetersRadioButton, SIGNAL(toggled(bool)), - this, SLOT(checkMeshUnits(bool))); - //connect the mesh resolution on the surface input page to the output - //'use mesh resolution' sections. - connect(tree->surface->meshResDoubleSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(updateOutRes())); - //connect the getLatLon button to the fx - //connect(tree->location->getLatLonToolButton, SIGNAL(clicked()), - // this, SLOT(getLatLon())); - //also connect the toggle on the check box on output pages to update - - /* - ** When we update the mesh resolution, update the outputs if the output is - ** enabled. We also update the output resolutions when the various outputs - ** are enabled. Additionally when either of the radio buttons are checked. - */ - connect(tree->google->useMeshResCheckBox, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->fb->useMeshResCheckBox, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->shape->useMeshResCheckBox, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->pdf->useMeshResCheckBox, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->google->googleGroupBox, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->fb->fbGroupBox, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->shape->shapeGroupBox, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->pdf->pdfGroupBox, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->surface->meshMetersRadioButton, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - connect(tree->surface->meshFeetRadioButton, SIGNAL(toggled(bool)), this, - SLOT(updateOutRes())); - - connect(tree->diurnal->diurnalGroupBox, SIGNAL(toggled(bool)), - tree->wind->windTable, SLOT(enableDiurnalCells(bool))); - connect(tree->diurnal->diurnalGroupBox, SIGNAL(toggled(bool)), - this, SLOT(enablePointDate(bool))); - connect(tree->stability->stabilityGroupBox, SIGNAL(toggled(bool)), - this, SLOT(enablePointDate(bool))); - connect(tree->stability->stabilityGroupBox, SIGNAL(toggled(bool)), - tree->wind->windTable, SLOT(enableStabilityCells(bool))); -#ifdef NINJAFOAM - connect(tree->ninjafoam->ninjafoamGroupBox, SIGNAL(toggled(bool)), - this, SLOT(enableNinjafoamOptions(bool))); -#endif - //connect change in tree to the checkers - connect(tree->tree, SIGNAL(currentItemChanged(QTreeWidgetItem *, - QTreeWidgetItem *)), this, SLOT(checkAllItems())); - - //connect the diurnalGroupBox->toggled to checkers - connect(tree->diurnal->diurnalGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect(tree->stability->stabilityGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); -#ifdef NINJAFOAM - //connect the solver method check boxes for mutex - connect(tree->ninjafoam->ninjafoamGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect(tree->nativesolver->nativeSolverGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect( tree->nativesolver->nativeSolverGroupBox, SIGNAL( toggled( bool ) ), - this, SLOT( selectNativeSolver( bool ) ) ); - connect( tree->ninjafoam->ninjafoamGroupBox, SIGNAL( toggled( bool ) ), - this, SLOT( selectNinjafoamSolver( bool ) ) ); -#endif - - //connect the speed and direction in each row to the checkers - for(int i=0;iwind->windTable->nRuns;i++) - { - connect(tree->wind->windTable->speed[i], SIGNAL(valueChanged(double)), this, - SLOT(checkAllItems())); - connect(tree->wind->windTable->dir[i], SIGNAL(valueChanged(int)), this, - SLOT(checkAllItems())); - } - - //connect the initialization check boxes to checkers - connect(tree->wind->windGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect(tree->point->pointGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect(tree->point->pointGroupBox, SIGNAL(toggled(bool)), - this, SLOT(enablePointDate(bool))); - connect(tree->weather->weatherGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - - //Connects making changes in pointInput to checkers - //Makes the validation happen instantaneously - connect(tree->point->treeView, - SIGNAL(clicked(const QModelIndex &)), - this,SLOT(checkAllItems())); - //When the user changes the date time, check to make sure it is sane - connect(tree->point->startTime,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(checkAllItems())); - connect(tree->point->stopTime,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(checkAllItems())); - - connect(tree->point->refreshToolButton,SIGNAL(clicked(bool)),this,SLOT(checkAllItems())); - - //connect selection change in weather to checkers - connect(tree->weather->treeView->selectionModel(), - SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(checkAllItems())); - - //connect the output check boxes with the checkers - connect(tree->google->googleGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect(tree->fb->fbGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect(tree->shape->shapeGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect(tree->pdf->pdfGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - connect(tree->vtk->vtkGroupBox, SIGNAL(toggled(bool)), - this, SLOT(checkAllItems())); - - //and the spinboxes - connect(tree->google->googleResSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(checkAllItems())); - connect(tree->fb->fbResSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(checkAllItems())); - connect(tree->shape->shapeResSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(checkAllItems())); - connect(tree->pdf->pdfResSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(checkAllItems())); - - //check the google res, make sure not bad - connect(tree->google->googleResSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(checkKmlLimit(double))); - - //solve button and solve() - connect(tree->solve->solveToolButton, SIGNAL(clicked()), - this, SLOT(solve())); - - connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - - connect(tree->solve->solveToolButton, SIGNAL(clicked()), progressDialog, - SLOT(forceShow())); - - //connect double clicks on trees to main action for that item - connect(tree->tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), - this, SLOT(treeDoubleClick(QTreeWidgetItem*, int))); - - //connect inputFileChanged to anything that wants it - connect(this, SIGNAL(inputFileChanged(QString)), - tree->weather, SLOT(setInputFile(QString))); - connect(this, SIGNAL(inputFileChanged(QString)), - tree->point, SLOT(setInputFile(QString))); - connect(this,SIGNAL(inputFileChanged(QString)), - tree->point, SLOT(checkForModelData())); //Update csv list when file changes in point -//Signal To Point Input what Diurnal Input is doing - connect(this, SIGNAL(mainDiurnalChanged(bool)),tree->point,SLOT(setDiurnalParam(bool))); - - //connect other writeToConsoles to the main writeToConsole - connect( tree->point, SIGNAL( writeToConsole( QString ) ), - this, SLOT( writeToConsole( QString ) ) ); - - //connect timezone combo to weather model tz string - connect( tree->surface->timeZone, SIGNAL( tzChanged( QString ) ), - tree->weather, SLOT( updateTz( QString ) ) ); - //connect time zone for station fetch - connect( tree->surface->timeZone, SIGNAL( tzChanged( QString ) ), - tree->point, SLOT( updateTz( QString ) ) ); - - //connect the initialization check boxes to the others for mutex - connect( tree->wind->windGroupBox, SIGNAL( toggled( bool ) ), - this, SLOT( selectWindInitialization( bool ) ) ); - connect( tree->point->pointGroupBox, SIGNAL( toggled( bool ) ), - this, SLOT( selectPointInitialization( bool ) ) ); - connect( tree->weather->weatherGroupBox, SIGNAL( toggled( bool ) ), - this, SLOT( selectWeatherInitialization( bool ) ) ); - - //connect change in station file to checkAllItems - connect( tree->point, SIGNAL( stationFileChanged() ), - this, SLOT( checkAllItems() ) ); - - //connect the solve open out path button - connect( tree->solve->openOutputPathButton, SIGNAL( clicked() ), - this, SLOT( openOutputPath() ) ); - - connect( progressDialog, SIGNAL( canceled() ), - this, SLOT( updateTimer() ) ); -} -/** - * Slot to catch a change in initialization method - * - * @param pick I am picked - */ -void mainWindow::selectWindInitialization( bool pick ) -{ - if( pick ) { - tree->point->pointGroupBox->setChecked( false ); - tree->weather->weatherGroupBox->setChecked( false ); - tree->output->wxModelOutputCheckBox->setDisabled( true ); - checkAllItems(); - } -} - -void mainWindow::selectPointInitialization( bool pick ) -{ - if( pick ) { - tree->wind->windGroupBox->setChecked( false ); - tree->weather->weatherGroupBox->setChecked( false ); - tree->output->wxModelOutputCheckBox->setDisabled( true ); - checkAllItems(); - } -} - -void mainWindow::selectWeatherInitialization( bool pick ) -{ - if( pick ) { - tree->wind->windGroupBox->setChecked( false ); - tree->point->pointGroupBox->setChecked( false ); - checkAllItems(); - } - tree->output->wxModelOutputCheckBox->setEnabled( pick ); -} - -#ifdef NINJAFOAM -void mainWindow::selectNativeSolver( bool pick ) -{ - if( pick ) { - tree->ninjafoam->ninjafoamGroupBox->setChecked( false ); - checkAllItems(); - } -} - -void mainWindow::selectNinjafoamSolver( bool pick ) -{ - if( pick ) { - tree->nativesolver->nativeSolverGroupBox->setChecked( false ); - checkAllItems(); - } -} - -void mainWindow::openExistingCase() -{ - QString dir = QFileDialog::getExistingDirectory(this, - tr("Open Existing Case"), - inputFileDir.absolutePath(), - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); - - QString shortName = QFileInfo(dir).fileName(); - tree->surface->foamCaseLineEdit->setText(shortName); - - tree->surface->downloadDEMButton->setEnabled(false); - tree->surface->meshResComboBox->setEnabled(false); - -// if(existingCaseDir != QFileInfo(dir).canonicalFilePath()) -// { -// emit(inputFileChanged(QFileInfo(dir).fileName())); -// } - - existingCaseDir = QFileInfo(dir).canonicalFilePath(); - - if(dir.isEmpty()){ - existingCaseDir = ""; - tree->surface->downloadDEMButton->setEnabled(true); - tree->surface->meshResComboBox->setEnabled(true); - } - - if(!dir.isEmpty()){ - //look for DEM that matches the STL basename in the NINJAFOAM_ paraent directory - char **papszFileList; - const char *pszFilename; - const char *pszBasename; - papszFileList = VSIReadDir( CPLSPrintf("%s/constant/triSurface", existingCaseDir.toStdString().c_str()) ); - //get the basename of the STL - for(int i=0; isurface->foamCaseLineEdit->setText(""); - tree->surface->downloadDEMButton->setEnabled(true); - tree->surface->meshResComboBox->setEnabled(true); - return; - } - else{//Else set pszName to the demfilename - pszFname = CPLStrdup(CPLFormFilename(pszDemPath, pszInputFilename, "")); - } - -//// const char* pszFname = CPLStrdup(CPLFormFilename(pszDemPath, pszInputFilename, ""));pszFname = CPLStrdup(CPLFormFilename(pszDemPath, pszInputFilename, "")); - - updateFileInputForCase(pszFname); //Update the dem file name - - CSLDestroy( papszFileList ); - CPLFree( (void*)pszBasename ); - CPLFree( (void*)pszDemPath ); - CPLFree( (void*)pszInputFilename ); - CPLFree( (void*)pszFname ); - - tree->surface->downloadDEMButton->setEnabled(false); - tree->surface->meshResComboBox->setEnabled(false); - } -} -#endif //NINJAFOAM - -//function for finding and opening an input file. -void mainWindow::openInputFile() -{ - //writeToConsole(inputFileDir.absolutePath()); - //setCursor(Qt::WaitCursor); - QString fileName = QFileDialog::getOpenFileName(this, - tr("Open Elevation Input File"), - inputFileDir.absolutePath(), - - tr("Elevation Input Files (*.asc *.lcp *.tif *.img)")); - - if(!fileName.isEmpty()) - { - cwd = QFileInfo(fileName).dir(); - //use GDAL to check the file - QString newFile = checkForNoData(fileName); - if(!newFile.isEmpty()) - { - fileName = newFile; - } - - if(checkInputFile(fileName) < 0) - { - tree->surface->inputFileLineEdit->clear(); - fileName = ""; - return; - } - - QString shortName = QFileInfo(fileName).fileName(); - if(inputFileType == LCP) - { - tree->surface->roughnessComboBox->setDisabled(true); - tree->surface->roughnessComboBox->hide(); - tree->surface->roughnessLabel->show(); - } - else - { - tree->surface->roughnessComboBox->setDisabled(false); - tree->surface->roughnessComboBox->show(); - tree->surface->roughnessLabel->hide(); - } - - tree->surface->inputFileLineEdit->setText(shortName); - - tree->surface->meshResComboBox->setEnabled(true); - - if(inputFileName != fileName) - emit(inputFileChanged(fileName)); - - inputFileName = fileName; - inputFileDir = QFileInfo(fileName).absolutePath(); - tree->solve->setOutputDir( inputFileDir.absolutePath() ); - shortInputFileName = shortName; - checkMeshCombo(); - checkInputItem(); - } - -#ifdef NINJAFOAM - if(tree->surface->foamCaseLineEdit->text() != ""){ - tree->surface->meshResComboBox->setEnabled(false); - } -#endif - -} -#ifdef NINJAFOAM -/** - * Slot to update elevation file input for existing case - * - * @param file File associated with existing case - */ -void mainWindow::updateFileInputForCase(const char* file) -{ - QString fileName(file); - fileWatcher.addPath(fileName); - - if(!fileName.isEmpty()) - { - cwd = QFileInfo(fileName).dir(); - //use GDAL to check the file - if(checkInputFile(fileName) < 0) - { - tree->surface->inputFileLineEdit->clear(); - fileName = ""; - return; - } - - QString shortName = QFileInfo(fileName).fileName(); - if(inputFileType == LCP) - { - tree->surface->roughnessComboBox->setDisabled(true); - tree->surface->roughnessComboBox->hide(); - tree->surface->roughnessLabel->show(); - } - else - { - tree->surface->roughnessComboBox->setDisabled(false); - tree->surface->roughnessComboBox->show(); - tree->surface->roughnessLabel->hide(); - } - - tree->surface->inputFileLineEdit->setText(shortName); - - tree->surface->meshResComboBox->setEnabled(true); - - if(inputFileName != fileName) - emit(inputFileChanged(fileName)); - - inputFileName = fileName; - inputFileDir = QFileInfo(fileName).absolutePath(); - tree->solve->setOutputDir( inputFileDir.absolutePath() ); - shortInputFileName = shortName; - checkMeshCombo(); - checkInputItem(); - } -} -#endif //NINJAFOAM - -/** - * Slot to update elevation file input with downloaded DEM file - * - * @param file File created from downloaded DEM - */ -void mainWindow::updateFileInput(const char* file) -{ - QString fileName(file); - - fileWatcher.addPath(fileName); - - if(!fileName.isEmpty()) - { - cwd = QFileInfo(fileName).dir(); - //use GDAL to check the file - if(checkInputFile(fileName) < 0) - { - tree->surface->inputFileLineEdit->clear(); - fileName = ""; - return; - } - - QString shortName = QFileInfo(fileName).fileName(); - if(inputFileType == LCP) - { - tree->surface->roughnessComboBox->setDisabled(true); - tree->surface->roughnessComboBox->hide(); - tree->surface->roughnessLabel->show(); - } - else - { - tree->surface->roughnessComboBox->setDisabled(false); - tree->surface->roughnessComboBox->show(); - tree->surface->roughnessLabel->hide(); - } - - tree->surface->inputFileLineEdit->setText(shortName); - - tree->surface->meshResComboBox->setEnabled(true); - - if(inputFileName != fileName) - emit(inputFileChanged(fileName)); - - inputFileName = fileName; - inputFileDir = QFileInfo(fileName).absolutePath(); - shortInputFileName = shortName; - checkMeshCombo(); - checkInputItem(); - tree->solve->setOutputDir(QFileInfo(fileName).absolutePath()); - } -} - -void mainWindow::inputFileDeleted() -{ - tree->surface->inputFileLineEdit->clear(); - //emit(inputFileChanged()); - checkMeshCombo(); - checkInputItem(); -} - -void mainWindow::openMainWindow() -{ - this->setEnabled(true); -} - -void mainWindow::createConsole() -{ - console = new ConsoleDockWidget; - addDockWidget(Qt::BottomDockWidgetArea, console); - console->consoleTextEdit->setReadOnly(true); - - //set prompt - prompt = "~>"; - //orange - orange.setRgb(255, 165, 0); - -} -void mainWindow::setPrompt(QString p) -{ - prompt = p; -} -void mainWindow::writeConsoleOutput() -{ - QDateTime date(QDateTime::currentDateTime()); - QString fileName = QFileDialog::getSaveFileName - (this, tr("Save console output as..."), "console-output.txt", - tr("Text Files (*.txt)")); - writeToConsole("WindNinja console output from:"); - writeToConsole(date.toString("MM/dd/yyyy hh:mm:ss")); - if(!fileName.isEmpty()) - { - std::ofstream fout(fileName.toStdString().c_str(), std::ios::out); - QString text = console->consoleTextEdit->toPlainText(); - fout << text.toStdString(); - writeToConsole(QString("Console data written to " + fileName + "."), Qt::darkGreen); - fout.close(); - } - else - writeToConsole(QString("Cannot open " + fileName + " for writing."), Qt::red); -} - -//create timers for status bar -void mainWindow::createTimers() -{ - runTime = new QTime(0, 0, 0, 0); - runTime->start(); -} - -void mainWindow::updateTimer() -{ - //elapsedRunTime = runTime->elapsed() / 1000.0; - writeToConsole("Total Simulation time: " + QString::number(elapsedRunTime, 'f', 2) + " seconds"); -} - -void mainWindow::openDEMDownloader() -{ - demWidget = new WidgetDownloadDEM(); - //demWidget->setAttribute(Qt::WA_DeleteOnClose, true); - demWidget->settingsDir.setPath(inputFileDir.absolutePath()); - connect(demWidget, SIGNAL(doneDownloading(const char*)), this, SLOT(updateFileInput(const char*))); - connect(demWidget, SIGNAL(exitDEM()), this, SLOT(openMainWindow())); - connect(demWidget, SIGNAL(destroyed()), this, SLOT(openMainWindow())); - this->setEnabled(false); -} - -void mainWindow::test() -{ -} - -void mainWindow::resampleData() -{} - -void mainWindow::writeBlankStationFile() -{ - QString fileName = QFileDialog::getSaveFileName - (this, tr("Save station file as..."), "stations.csv", - tr("Text Files (*.csv)")); - if(!fileName.isEmpty()) - wxStation::writeBlankStationFile( fileName.toStdString() ); - else - return; -} - -void mainWindow::windNinjaHelp() -{} - -void mainWindow::tutorial1() -{ - pwd.cd("share/windninja/doc/tutorials"); - writeToConsole("Opening " + pwd.absoluteFilePath("WindNinja_tutorial1.pdf")); - - if(!QDesktopServices::openUrl(QUrl(pwd.absoluteFilePath("WindNinja_tutorial1.pdf")))) - { - - QMessageBox::warning(this, tr("Broken Link."), - tr("The link to the tutorial is broken, you can get to it through the Start Menu."), - QMessageBox::Ok | QMessageBox::Default); - } - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - pwd.cdUp(); -} - -void mainWindow::tutorial2() -{ - pwd.cd("share/windninja/doc/tutorials"); - writeToConsole("Opening " + pwd.absoluteFilePath("WindNinja_tutorial2.pdf")); - - if(!QDesktopServices::openUrl(QUrl(pwd.absoluteFilePath("WindNinja_tutorial2.pdf")))) - { - - QMessageBox::warning(this, tr("Broken Link."), - tr("The link to the tutorial is broken, you can get to it through the Start Menu."), - QMessageBox::Ok | QMessageBox::Default); - } - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - pwd.cdUp(); -} - -void mainWindow::tutorial3() -{ - pwd.cd("share/windninja/doc/tutorials"); - writeToConsole("Opening " + pwd.absoluteFilePath("WindNinja_tutorial3.pdf")); - - if(!QDesktopServices::openUrl(QUrl(pwd.absoluteFilePath("WindNinja_tutorial3.pdf")))) - { - - QMessageBox::warning(this, tr("Broken Link."), - tr("The link to the tutorial is broken, you can get to it through the Start Menu."), - QMessageBox::Ok | QMessageBox::Default); - } - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - pwd.cdUp(); -} - -void mainWindow::tutorial4() -{ - pwd.cd("share/windninja/doc/tutorials"); - writeToConsole("Opening " + pwd.absoluteFilePath("WindNinja_tutorial4.pdf")); - - if(!QDesktopServices::openUrl(QUrl(pwd.absoluteFilePath("WindNinja_tutorial4.pdf")))) - { - - QMessageBox::warning(this, tr("Broken Link."), - tr("The link to the tutorial is broken, you can get to it through the Start Menu."), - QMessageBox::Ok | QMessageBox::Default); - } - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - pwd.cdUp(); -} - -void mainWindow::demDownload() -{ - pwd.cd("share/windninja/doc"); - writeToConsole("Opening " + pwd.absoluteFilePath("download_elevation_file.pdf")); - - if(!QDesktopServices::openUrl(QUrl(pwd.absoluteFilePath("download_elevation_file.pdf")))) - { - - QMessageBox::warning(this, tr("Broken Link."), - tr("The link to the tutorial is broken, you can get to it through the Start Menu."), - QMessageBox::Ok | QMessageBox::Default); - } - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - pwd.cdUp(); -} - -void mainWindow::fetchDem() -{ - pwd.cd("share/windninja/doc"); - writeToConsole("Opening " + pwd.absoluteFilePath("fetch_dem_instructions.pdf")); - - if(!QDesktopServices::openUrl(QUrl(pwd.absoluteFilePath("fetch_dem_instructions.pdf")))) - { - - QMessageBox::warning(this, tr("Broken Link."), - tr("The link to the tutorial is broken, you can get to it through the Start Menu."), - QMessageBox::Ok | QMessageBox::Default); - } - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - pwd.cdUp(); -} - - -void mainWindow::displayArcGISPro() -{ - pwd.cd("share/windninja/doc"); - writeToConsole("Opening " + pwd.absoluteFilePath("displaying_wind_vectors_in_ArcGIS_Pro.pdf")); - if(!QDesktopServices::openUrl(QUrl(pwd.absoluteFilePath("displaying_wind_vectors_in_ArcGIS_Pro.pdf")))) - { - - QMessageBox::warning(this, tr("Broken Link."), - tr("The link to the tutorial is broken, you can get to it through the Start Menu."), - QMessageBox::Ok | QMessageBox::Default); - } - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - pwd.cdUp(); -} - -void mainWindow::cliInstructions() -{ - pwd.cd("share/windninja/doc"); - writeToConsole("Opening " + pwd.absoluteFilePath("CLI_instructions.pdf")); - - if(!QDesktopServices::openUrl(QUrl(pwd.absoluteFilePath("CLI_instructions.pdf")))) - { - - QMessageBox::warning(this, tr("Broken Link."), - tr("The link to the tutorial is broken, you can get to it through the Start Menu."), - QMessageBox::Ok | QMessageBox::Default); - } - pwd.setPath(QString::fromStdString(FindNinjaBinDir())); - pwd.cdUp(); -} - -void mainWindow::supportEmail() -{ - QDesktopServices::openUrl(QUrl("mailto:wind.ninja.support@gmail.com?subject=[windninja-support]")); -} - -void mainWindow::bugReport() -{ - QDesktopServices::openUrl(QUrl("https://github.com/firelab/windninja/issues/new")); -} - -void mainWindow::aboutWindNinja() -{ - QString aboutText = "

WindNinja

\n"; - aboutText.append("

Version:

" + QString(NINJA_VERSION_STRING) + "

"); - - aboutText.append("

Git Commit:

" + QString(NINJA_SCM_VERSION) + "

"); - - aboutText.append("

Release Date:

" + QString(NINJA_RELEASE_DATE) + "

"); - aboutText.append("

Developed by:

Jason Forthofer
" \ - "Natalie Wagenbrenner
" \ - "Kyle Shannon
" \ - "Loren Atwood
" \ - "Mason Willman"); \ - aboutText.append("

Missoula Fire Sciences Laboratory
"); - aboutText.append("Rocky Mountain Research Station
"); - aboutText.append("USDA Forest Service
"); - aboutText.append("5775 Highway 10 W.
"); - aboutText.append("Missoula, MT 59808

"); - aboutText.append("

Contributors

"); - aboutText.append("

Sponsored By:

"); - aboutText.append("USDA Forest Service
"); - aboutText.append("Center for Environmental Management of Military Lands at Colorado State University
"); - aboutText.append("Joint Fire Sciences Program
"); - aboutText.append("Washington State University

"); - aboutText.append("

Special Thanks

"); - aboutText.append("
"); - - QMessageBox::about(this, tr("About WindNinja"), - aboutText); -} - -void mainWindow::citeWindNinja() -{ - QString citeText = "

To cite WindNinja in a publication use:

"; - - citeText.append("Forthofer, J.M., Butler, B.W., Wagenbrenner, N.S., 2014. A comparison "); - citeText.append("of three approaches for simulating fine-scale surface winds in "); - citeText.append("support of wildland fire management. Part I. Model formulation and "); - citeText.append("comparison against measurements. International Journal of Wildland "); - citeText.append("Fire, 23:969-931. doi: 10.1071/WF12089."); - - citeText.append("

For additional WindNinja publications visit:

"); - citeText.append("

https://ninjastorm.firelab.org/windninja/publications

"); - - QMessageBox::about(this, tr("Cite WindNinja"), - citeText); -} - -void mainWindow::writeToConsole(QString message, QColor color) -{ - console->consoleTextEdit->setTextColor(color); - console->consoleTextEdit->append(QString::number(lineNumber) + ": " + message); - lineNumber++; - //console->consoleTextEdit->append(prompt + message); -} - -void mainWindow::updateOutRes() -{ - //get res from surface page and store as an int - double resolution = tree->surface->meshResDoubleSpinBox->value(); - bool useMeters = tree->surface->meshMetersRadioButton->isChecked(); - if (tree->google->useMeshResCheckBox->isChecked() == true) { - tree->google->googleResSpinBox->setValue(resolution); - if (useMeters) { - tree->google->googleMetersRadioButton->setChecked(true); - } else { - tree->google->googleFeetRadioButton->setChecked(true); - } - } - if (tree->fb->useMeshResCheckBox->isChecked() == true) { - tree->fb->fbResSpinBox->setValue(resolution); - if (useMeters) { - tree->fb->fbMetersRadioButton->setChecked(true); - } else { - tree->fb->fbFeetRadioButton->setChecked(true); - } - } - if (tree->shape->useMeshResCheckBox->isChecked() == true) { - tree->shape->shapeResSpinBox->setValue(resolution); - if (useMeters) { - tree->shape->shapeMetersRadioButton->setChecked(true); - } else { - tree->shape->shapeFeetRadioButton->setChecked(true); - } - } - if (tree->pdf->useMeshResCheckBox->isChecked() == true) { - tree->pdf->pdfResSpinBox->setValue(resolution); - if (useMeters) { - tree->pdf->pdfMetersRadioButton->setChecked(true); - } else { - tree->pdf->pdfFeetRadioButton->setChecked(true); - } - } -} - -//empty fx, need to write it when help is done. -int mainWindow::openHelp(int target) -{ - return target; -} - -void mainWindow::checkMeshCombo() -{ - int choice = tree->surface->meshResComboBox->currentIndex(); - double res = 200; - //there is a splitter at 3 - if(choice == 4) - { - tree->surface->meshResDoubleSpinBox->setEnabled(true); - } - else - { - if(tree->surface->inputFileLineEdit->text() == "") - { - tree->surface->meshResDoubleSpinBox->setValue(res); - meshCellSize = res; - tree->surface->meshResDoubleSpinBox->setEnabled(false); - } - else - { - tree->surface->meshResDoubleSpinBox->setEnabled(false); - res = computeCellSize(choice); - if(tree->surface->meshFeetRadioButton->isChecked()) - res *= 3.28083989502; - tree->surface->meshResDoubleSpinBox->setValue(res); - meshCellSize = res; - writeToConsole("Mesh Resolution set to " + - QString::number(res)); //Note that this is very annoying for pointInitilaization/Station-fetch - } - } -} - -void mainWindow::checkMeshUnits(bool checked) -{ - if(checked) - checkMeshCombo(); - else - checkMeshCombo(); -} - -double mainWindow::computeCellSize(int index) -{ - int coarse, medium, fine; - double meshResolution; - - meshResolution = 200.0; - -#ifdef NINJAFOAM - if( tree->ninjafoam->ninjafoamGroupBox->isChecked() ){ - /*ninjafoam: calculate mesh resolution of lower volume in block mesh*/ - coarse = 25000; - medium = 50000; - fine = 100000; - } - else{ - /* use native mesh */ - coarse = 4000; - medium = 10000; - fine = 20000; - } -#else - coarse = 4000; - medium = 10000; - fine = 20000; -#endif //NINJAFOAM - - int targetNumHorizCells = fine; - switch(index) - { - case 0: - targetNumHorizCells = coarse; - break; - case 1: - targetNumHorizCells = medium; - break; - case 2: - targetNumHorizCells = fine; - break; - case 3: - return meshResolution; - break; - case 4: - return meshResolution; - break; - default: - return meshResolution; - } - -#ifdef NINJAFOAM - if( tree->ninjafoam->ninjafoamGroupBox->isChecked() ){ - /* ninjafoam mesh */ - - double XLength = GDALXSize * GDALCellSize; - double YLength = GDALYSize * GDALCellSize; - - double dz = GDALMaxValue - GDALMinValue; - double ZLength = max((0.1 * max(XLength, YLength)), (dz + 0.1 * dz)); - double zmin, zmax; - zmin = GDALMaxValue + 0.05 * ZLength; //zmin (above highest point in DEM for MDM) - zmax = GDALMaxValue + ZLength; //zmax - - double volume; - double cellCount; - double cellVolume; - - volume = XLength * YLength * (zmax-zmin); //volume of blockMesh - cellCount = targetNumHorizCells * 0.5; // cell count in volume 1 - cellVolume = volume/cellCount; // volume of 1 cell in blockMesh - double side = std::pow(cellVolume, (1.0/3.0)); // length of side of cell in blockMesh - - //determine number of rounds of refinement - int nCellsToAdd = 0; - int refinedCellCount = 0; - int nCellsInLowestLayer = int(XLength/side) * int(YLength/side); - int nRoundsRefinement = 0; - while(refinedCellCount < (0.5 * targetNumHorizCells)){ - nCellsToAdd = nCellsInLowestLayer * 8; //each cell is divided into 8 cells - refinedCellCount += nCellsToAdd - nCellsInLowestLayer; //subtract the parent cells - nCellsInLowestLayer = nCellsToAdd/2; //only half of the added cells are in the lowest layer - nRoundsRefinement += 1; - } - meshResolution = side/(nRoundsRefinement*2.0); - } - else{ - /* native windninja mesh */ - double XLength = GDALXSize * GDALCellSize; - double YLength = GDALYSize * GDALCellSize; - double nXCells = 2 * std::sqrt((double)targetNumHorizCells) * (XLength / (XLength + YLength)); - double nYCells = 2 * std::sqrt((double)targetNumHorizCells) * (YLength / (XLength + YLength)); - - double XCellSize = XLength / nXCells; - double YCellSize = YLength / nYCells; - - meshResolution = (XCellSize + YCellSize) / 2; - - } -#else - double XLength = GDALXSize * GDALCellSize; - double YLength = GDALYSize * GDALCellSize; - double nXCells = 2 * std::sqrt((double)targetNumHorizCells) * (XLength / (XLength + YLength)); - double nYCells = 2 * std::sqrt((double)targetNumHorizCells) * (YLength / (XLength + YLength)); - - double XCellSize = XLength / nXCells; - double YCellSize = YLength / nYCells; - - meshResolution = (XCellSize + YCellSize) / 2; - - //noGoogleCellSize = std::sqrt((XLength * YLength) / noGoogleNumCells); -#endif //NINJAFOAM - - return meshResolution; -} - -//open input file as a GDALDataset, return file type enum; -int mainWindow::checkInputFile(QString fileName) -{ - GDALProjRef = ""; - hasPrj = false; - GDALDataset *poInputDS; - double adfGeoTransform[6]; - writeToConsole("Opening dataset..."); - poInputDS = (GDALDataset*)GDALOpen(fileName.toStdString().c_str(), - GA_ReadOnly); - if(poInputDS == NULL) - { - writeToConsole("Cannot open the input file.", Qt::red); - return -1; - } - - GDALDriverName = poInputDS->GetDriver()->GetDescription(); - GDALDriverLongName = poInputDS->GetDriver()->GetMetadataItem(GDAL_DMD_LONGNAME); - writeToConsole("Reading " + GDALDriverLongName + "..."); - - //set the file type here - if(GDALDriverName == "AAIGrid") - inputFileType = ASC; - if(GDALDriverName == "LCP") - inputFileType = LCP; - if(GDALDriverName == "GTiff") - { - int nBandCount = GDALGetRasterCount( poInputDS ); - //if it's a multi-band GeoTIFF, it's an lcp - if(nBandCount > 1) - { - inputFileType = LCP; - } - else - inputFileType = GTIFF; - } - if(GDALDriverName == "IMG") - inputFileType = IMG; - - if(inputFileType == LCP) - { - tree->surface->roughnessComboBox->setEnabled(false); - tree->surface->roughnessComboBox->hide(); - tree->surface->roughnessLabel->show(); - } - else - { - tree->surface->roughnessComboBox->setEnabled(true); - tree->surface->roughnessComboBox->show(); - tree->surface->roughnessLabel->hide(); - } - - //get x and y dimension - GDALXSize = poInputDS->GetRasterXSize(); - GDALYSize = poInputDS->GetRasterYSize(); - - if(!GDALTestSRS(poInputDS)) - { - hasPrj = false; - writeToConsole("Invalid Spatial Reference (prj), " - "cannot do a simulation with the supplied DEM", red); - QMessageBox::warning(this, tr("WindNinja"), - "The DEM does not contain a proper spatial reference " - "system. WindNinja only supports DEM files " - "with projected coordinate systems (e.g., UTM)", - QMessageBox::Ok | QMessageBox::Default); - GDALClose((GDALDatasetH)poInputDS); - return -1; - } - else - { - GDALProjRef = poInputDS->GetProjectionRef(); - const char *pszProjRef; - OGRSpatialReference oSRS; - pszProjRef = GDALProjRef.c_str(); - oSRS.importFromWkt((char**)&pszProjRef); - if(GDALProjRef == "") - { - hasPrj = false; - QMessageBox::warning(this, tr("WindNinja"), - "The DEM does not contain a proper spatial reference " - "system. WindNinja only supports DEM files " - "with projected coordinate systems (e.g., UTM)", - QMessageBox::Ok | QMessageBox::Default); - GDALClose((GDALDatasetH)poInputDS); - return -1; - } - /* Check for geographic. Separate case as we may allow support later - * on. - */ - else if(oSRS.IsGeographic()) - { - hasPrj = false; - QMessageBox::warning(this, tr("WindNinja"), - "The DEM coordinated system is in a " - "geographic projection (latitude/longitude). " - "WindNinja only supports projected " - "coordinate systems (e.g., UTM)", - QMessageBox::Ok | QMessageBox::Default); - writeToConsole("Invalid Spatial Reference (prj), " - "cannot do a simulation with the supplied DEM", red); - //tree->surface->inputFileLineEdit->setText(""); - GDALClose((GDALDatasetH)poInputDS); - return -1; - } - else - { - hasPrj = true; - double longitude, latitude; - if(GDALGetCenter(poInputDS, &longitude, &latitude)) - { - //set diurnal location, also set DD.DDDDD - QString oTimeZone = FetchTimeZone(longitude, latitude, NULL).c_str(); - if(oTimeZone != "") - { - /* Show all time zones, so we can search all time zones */ - tree->surface->timeZone->tzCheckBox->setChecked(true); - int nIndex = tree->surface->timeZone->tzComboBox->findText(oTimeZone); - tree->surface->timeZone->tzComboBox->setCurrentIndex(nIndex); - } - - //emit latLonChanged( ll[0], ll[1], false ); - } - } - } - - //check for ndv - //if(!checkForNoData(poInputDS)) - if(GDALHasNoData(poInputDS, 1)) - { - writeToConsole("The input file contains no data values, cannot use", - Qt::red); - GDALClose((GDALDatasetH)poInputDS); - return -1; - } - - //get the geo-transform - if(poInputDS->GetGeoTransform(adfGeoTransform) == CE_None) - { - double c1, c2; - c1 = adfGeoTransform[1]; - c2 = adfGeoTransform[5]; - if(abs(c1) == abs(c2)) - GDALCellSize = abs(c1); - else - { - writeToConsole("Invalid cell size, invalid file"); - GDALClose((GDALDatasetH)poInputDS); - return -1; - } - } - - //get min/max values - GDALMaxValue = GDALGetMax(poInputDS); - GDALMinValue = GDALGetMin(poInputDS); - - GDALClose( (GDALDatasetH)poInputDS ); - - double XLength = GDALXSize * GDALCellSize; - double YLength = GDALYSize * GDALCellSize; - - noGoogleCellSize = std::sqrt((XLength * YLength) / noGoogleNumCells); - - writeToConsole("File Opened.", Qt::darkGreen); - return 0; -} - -bool mainWindow::getLatLon() -{ - if(hasGDALCenter) - { - writeToConsole("The center of your DEM has been found.", Qt::darkGreen); - writeToConsole("The lat/lon has been set in the diurnal inputs section", Qt::darkGreen); - checkAllItems(); - return true; - } - else - { - writeToConsole("Cannot get Lat/Lon center", Qt::red); - return false; - } -} - -void mainWindow::openOutputPath() -{ - if( outputPath.isEmpty() || outputPath == "!set" ) - { - return; - } - else - { - QDesktopServices::openUrl( QUrl ( "file:///" + outputPath, - QUrl::TolerantMode ) ); - } -} - -int mainWindow::solve() -{ -#ifdef NINJAFOAM - bool useNinjaFoam = tree->ninjafoam->ninjafoamGroupBox->isChecked(); -#endif - //disable the open output path button - tree->solve->openOutputPathButton->setDisabled( true ); - - //dem file - std::string demFile = inputFileName.toStdString(); - -#ifdef NINJAFOAM - std::string caseFile = existingCaseDir.toStdString(); -#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; - } - - //mesh - int meshIndex = tree->surface->meshResComboBox->currentIndex(); - Mesh::eMeshChoice meshChoice; - double meshRes; - 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; - if( tree->surface->meshFeetRadioButton->isChecked() ) - meshUnits = lengthUnits::feet; - else - meshUnits = lengthUnits::meters; - } -#ifdef NINJAFOAM - WindNinjaInputs::eNinjafoamMeshChoice ninjafoamMeshChoice; - if(useNinjaFoam){ - if( meshIndex == 0 ) - ninjafoamMeshChoice = WindNinjaInputs::coarse; - else if( meshIndex == 1 ) - ninjafoamMeshChoice = WindNinjaInputs::medium; - else if (meshIndex == 2) - ninjafoamMeshChoice = WindNinjaInputs::fine; - else { - meshRes = tree->surface->meshResDoubleSpinBox->value(); - customMesh = true; - if( tree->surface->meshFeetRadioButton->isChecked() ) - meshUnits = lengthUnits::feet; - else - meshUnits = lengthUnits::meters; - } - } -#endif - - //location - int tzIndex = tree->surface->timeZone->tzComboBox->currentIndex(); - if(tzIndex == -1 && (tree->diurnal->diurnalGroupBox->isChecked() || - tree->weather->weatherGroupBox->isChecked() - || tree->stability->stabilityGroupBox->isChecked() - )) - { - QMessageBox::warning(this, tr("WindNinja"), tr("Could not auto-identify " - "time zone, please " - "specify one in Surface"), - QMessageBox::Ok | QMessageBox::Default); - progressDialog->setValue( 0 ); - progressDialog->cancel(); - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - return false; - } - - QVariant temp = tree->surface->timeZone->tzComboBox->itemData( tzIndex ); - std::string timeZone = temp.toString().toStdString(); - - //diurnal - bool useDiurnal = tree->diurnal->diurnalGroupBox->isChecked(); - - //stability - bool useStability = tree->stability->stabilityGroupBox->isChecked(); - - //initialization method - WindNinjaInputs::eInitializationMethod initMethod; - if( tree->wind->windGroupBox->isChecked() ) - initMethod = WindNinjaInputs::domainAverageInitializationFlag; - else if( tree->point->pointGroupBox->isChecked() ) - initMethod = WindNinjaInputs::pointInitializationFlag; - else if( tree->weather->weatherGroupBox->isChecked() ) - initMethod = WindNinjaInputs::wxModelInitializationFlag; - - //input wind height - double inHeight = tree->wind->metaWind->inputHeightDoubleSpinBox->value(); - lengthUnits::eLengthUnits inHeightUnits; - if(tree->wind->metaWind->feetRadioButton->isChecked()) - inHeightUnits = lengthUnits::feet; - else - inHeightUnits = lengthUnits::meters; - - //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; - - temperatureUnits::eTempUnits tempUnits; - if(tree->wind->windTable->airTempUnits->currentIndex() == 0) - tempUnits = temperatureUnits::F; - else if(tree->wind->windTable->airTempUnits->currentIndex() == 1) - tempUnits = temperatureUnits::C; - - //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(); - } - else - weatherFile = ""; - - //output height - double outHeight = tree->output->outputHeight->outputHeightDoubleSpinBox->value(); - lengthUnits::eLengthUnits outHeightUnits; - if(tree->output->outputHeight->feetRadioButton->isChecked()) - outHeightUnits = lengthUnits::feet; - else - outHeightUnits = lengthUnits::meters; - - velocityUnits::eVelocityUnits outputSpeedUnits; - if(tree->output->outputSpeedUnitsCombo->currentIndex() == 0) - outputSpeedUnits = velocityUnits::milesPerHour; - else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 1) - outputSpeedUnits = velocityUnits::metersPerSecond; - else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 2) - outputSpeedUnits = velocityUnits::kilometersPerHour; - else if(tree->output->outputSpeedUnitsCombo->currentIndex() == 3) - outputSpeedUnits = velocityUnits::knots; - - //clip buffer? - int clip = tree->output->bufferSpinBox->value(); - - bool writeWxOutput; - if( tree->output->wxModelOutputCheckBox->isEnabled() ) - writeWxOutput = tree->output->wxModelOutputCheckBox->isChecked(); - - //google - bool writeGoogle = tree->google->googleGroupBox->isChecked(); - double googleRes = tree->google->googleResSpinBox->value(); - 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; - - if(tree->google->uniformRangeRadioButton->isChecked()) - googleScale = KmlVector::equal_interval; - else - googleScale = KmlVector::equal_color; - - std::string googleScheme; - bool googVectorScaling = tree->google->applyVectorScaling->isChecked(); - bool googConsistentColorScale = tree->google->applyConsistentColorScale->isChecked(); - if(tree->google->colorblindBox->isChecked()) - { - std::string googCheckScheme; - QString QgoogleScheme=tree->google->inputColorblindComboBox->currentText(); - googCheckScheme=QgoogleScheme.toStdString(); - - if (googCheckScheme=="Default") - { - googleScheme="default"; - } - if (googCheckScheme=="ROPGW (Red Orange Pink Green White)") - { - googleScheme="ROPGW"; - } - if (googCheckScheme=="Oranges") - { - googleScheme="oranges"; - } - if (googCheckScheme=="Blues") - { - googleScheme="blues"; - } - if (googCheckScheme=="Pinks") - { - googleScheme="pinks"; - } - if (googCheckScheme=="Greens") - { - googleScheme="greens"; - } - if (googCheckScheme=="Magic Beans") - { - googleScheme="magic_beans"; - } - if (googCheckScheme=="Pink to Green") - { - googleScheme="pink_to_green"; - } - } - else - { - googleScheme="default"; - } - //ascii raster fb files - bool writeFb = tree->fb->fbGroupBox->isChecked(); - double fbRes = tree->fb->fbResSpinBox->value(); - lengthUnits::eLengthUnits fbUnits; - if(tree->fb->fbMetersRadioButton->isChecked()) - fbUnits = lengthUnits::meters; - else - fbUnits = lengthUnits::feet; - //write atmosphere file? - bool writeAtm = tree->fb->atmFileCheckBox->isChecked(); - if(writeAtm && writeFb) - { - if((outHeight == 20 && outHeightUnits == lengthUnits::feet && - outputSpeedUnits == velocityUnits::milesPerHour) || - (outHeight == 10 && outHeightUnits == lengthUnits::meters && - outputSpeedUnits == velocityUnits::kilometersPerHour)) - {} - else - { - QMessageBox::critical(this, tr("The solver cannot be run"), - tr("The output wind settings for atm files must " - "either be 10m for output height and " - "output speed units in kph, or " - "20ft for output height and " - "output speed units in mph."), - QMessageBox::Ok | QMessageBox::Default); - progressDialog->setValue( 0 ); - progressDialog->cancel(); - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - return false; - } - } - - //shape - bool writeShape = tree->shape->shapeGroupBox->isChecked(); - double shapeRes = tree->shape->shapeResSpinBox->value(); - lengthUnits::eLengthUnits shapeUnits; - if(tree->shape->shapeMetersRadioButton->isChecked()) - shapeUnits = lengthUnits::meters; - else - shapeUnits = lengthUnits::feet; - //pdf - bool writePdf = tree->pdf->pdfGroupBox->isChecked(); - double pdfRes = tree->pdf->pdfResSpinBox->value(); - double pdfLineWidth = tree->pdf->vectorWidthDoubleSpinBox->value(); - lengthUnits::eLengthUnits pdfUnits; - if(tree->pdf->pdfMetersRadioButton->isChecked()) - pdfUnits = lengthUnits::meters; - else - pdfUnits = lengthUnits::feet; - int pdfBase = tree->pdf->backgroundComboBox->currentIndex(); - - double pdfHeight, pdfWidth; - int pdfSize = tree->pdf->sizeComboBox->currentIndex(); - // Letter - if( pdfSize == 0 ) - { - pdfHeight = 11.0; - pdfWidth = 8.5; - } - // Legal - else if( pdfSize == 1 ) - { - pdfHeight = 14.0; - pdfWidth = 8.5; - } - // Tabloid - else if( pdfSize == 2 ) - { - pdfHeight = 17.0; - pdfWidth = 11.0; - } - if( tree->pdf->landscapeRadioButton->isChecked() ) - { - double tmp; - tmp = pdfWidth; - pdfWidth = pdfHeight; - pdfHeight = tmp; - } - - bool writeVTK = tree->vtk->vtkGroupBox->isChecked(); - - //number of processors - int nThreads = tree->solve->numProcSpinBox->value(); - - army = new ninjaArmy(); - - //count the runs in the wind table - if( initMethod == WindNinjaInputs::pointInitializationFlag ) - { - int pointFormat = tree->point->simType; - std::vector pointFileList = tree->point->stationFileList; //This is for the new way - std::string pointFile = tree->point->stationFileList[0]; //For Old Format, only can accept 1 file - std::vector xStartTime = tree->point->startSeries; //Get the start time from pointInput - std::vector xEndTime = tree->point->endSeries; //Get the Stop time from pointInput - int numTimeSteps = tree->point->numSteps->value(); //Get the number of steps from pointInput - bool useTimeList = tree->point->enableTimeseries; //Find out if its a timeseries run or not - bool writeStationKML = tree->point->writeStationKmlButton->isChecked(); //Write a kml file - bool writeStationCSV = tree->point->writeStationFileButton->isChecked(); //hidden for now - - /* - * Note that pointFormat is not the same as stationFormat! - * - * point Format is based on pointInput::directStationTraffic - * 0 == old format - * 1 == new format with time series - * 2 == new format no time series - * - * this is only used in the GUI - * - * stationFormat is based on wxStation::GetHeaderVersion - * 1 == old format - * 2 == new format, both timeseries and timeseries - * 3 == csv list that points to list of new format files with time series - * NOT used in GUI - * 4 == csv list that points to list of new format files with no time series - * NOT USED IN GUI - * based on header only, not actual data! - * this is used inside the actual simulation - * - * A modified version of directstationtraffic has been adapted for CLI use - * in wxStation::getFirstStationLine - * - */ - - if (pointFormat==0) - { - CPLDebug("STATION_FETCH","USING OLD FORMAT..."); - pointInitialization::SetRawStationFilename(pointFile); //Note: When testing this, - //Only the old format works, so downloaded data, with the date-time column don't yet work! - /* right now the only option is the old format */ - wxStation::SetStationFormat(wxStation::oldFormat); - std::vector timeList; - if(useDiurnal==true || useStability==true) //means that the user is specifying time - { //Get that time and assign it to the simulation - std::vector xSingleTime = tree->point->diurnalTimeVec; - boost::posix_time::ptime singleTime = pointInitialization::generateSingleTimeObject(xSingleTime[0],xSingleTime[1],xSingleTime[2],xSingleTime[3],xSingleTime[4],timeZone); - timeList.push_back(singleTime); - } - else//The user is not giving us time, do what we normally do - { - boost::posix_time::ptime noTime; - timeList.push_back(noTime); - } - try{ //Try to run windninja - army->makePointArmy(timeList,timeZone, pointFile, demFile, true,false); - }catch (exception& e) - { - QMessageBox::critical(this,tr("Failure."), - "An error occured in makePointArmy() - OldFormat! This is " - "usually due to a failure in reading a " - "weather station file. Check your files and " - "try again - Error Info: "+QString(e.what()), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); //Restart everything - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - }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 " - "weather station file. Check your files and " - "try again - Error Info: "+QString(pointInitialization::error_msg.c_str()), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); //Restart everything - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - } - nRuns = army->getSize(); - - } - if (pointFormat==1 || pointFormat==2) //New Format - { - wxStation::SetStationFormat(wxStation::newFormat); - std::vector formatVec; - std::vector timeList; - CPLDebug("STATION_FETCH","NEW FORMAT..."); - for (int i=0;imakePointArmy(timeList,timeZone,pointFileList[0],demFile,true,false); //setting pointFileList[0] is just for header checks etc - }catch (exception& e) - { - QMessageBox::critical(this,tr("Failure."), - "An error occured in makePointArmy() - timeSeries! This is " - "usually due to a failure in reading a " - "weather station file. Check your files and " - "try again - Error Info: "+QString(e.what()), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - }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 " - "weather station file. Check your files and " - "try again - Error Info: "+QString(pointInitialization::error_msg.c_str()), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - } - nRuns = army->getSize(); - } - if (useTimeList == false)//Current Data/Single Step - { - //Get time from file attributes, if its diurnal this matters - //if not, then it really doesn't matter and who cares - boost::posix_time::ptime noTime; - CPLDebug("STATION_FETCH","USING CURRENT WEATHER DATA..."); - std::vector xSingleTime = tree->point->diurnalTimeVec; - boost::posix_time::ptime singleTime = pointInitialization::generateSingleTimeObject(xSingleTime[0], - xSingleTime[1],xSingleTime[2], - xSingleTime[3],xSingleTime[4],timeZone); - timeList.push_back(singleTime); - pointInitialization::storeFileNames(pointFileList); - try{ //try making the army with current data - army->makePointArmy(timeList,timeZone,pointFileList[0],demFile,true,false); - }catch (exception& e) - { - QMessageBox::critical(this,tr("Failure."), - "An error occured in makePointArmy() - currentwxdata! This is " - "usually due to a failure in reading a " - "weather station file. Check your files and " - "try again - Error Info: "+QString(e.what()), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - }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 " - "weather station file. Check your files and " - "try again - Error Info: "+QString(pointInitialization::error_msg.c_str()), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - } - nRuns = army->getSize(); - } - } - else - { - //Note that This error is not normally reachable if all other error - //handling works correctly - CPLDebug("STATION_FETCH","WARNING NOT ALL CSVS ARE OF THE SAME TYPE, CANNOT CONTINUE"); - QMessageBox::critical(this,tr("Failure."), - "An error occured in deteriming data types This is " - "usually due to a failure in reading a " - "weather station file. Check your files and " - "try again", - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - } - - - } - if (writeStationKML==true) //Write KMLS for each time step - { - std::string outputDir = tree->solve->outputDirectory().toStdString(); - writeToConsole("Writing Weather Station .kml"); - nRuns = army->getSize(); - for (int i_=0;i_getWxStations(i_), - demFile, (outputDir + "/").c_str(), outputSpeedUnits); - } - } -// if (writeStationCSV==true) - const char *csvOpt = CPLGetConfigOption("WRITE_CSV","FALSE"); - if(csvOpt!="FALSE") //The only way to write an interpolated CSV is to set a config option - { - writeToConsole("Writing Weather Station .csv"); - nRuns = army->getSize(); - QString demBase = QFileInfo(QString(demFile.c_str())).baseName(); - QString demPath = QFileInfo(demFile.c_str()).absoluteDir().absolutePath()+"/"; - std::string csvPath = demPath.toStdString()+demBase.toStdString(); - pointInitialization::writeStationOutFile(army->getWxStations(0),csvPath,"",true); - - } - const char *metaOpt = CPLGetConfigOption("FETCH_METADATA","FALSE"); - if(metaOpt!="FALSE") //set a config option to get the metadata from the DEM - { //There is also a button for this, that is hidden (see stationFetchWidget) - writeToConsole("Fetching station metadata for DEM..."); - std::string pathDem = std::string(CPLGetDirname(demFile.c_str())); - std::string baseDem = std::string(CPLGetBasename(demFile.c_str())); - 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()); - pointInitialization::fetchMetaData(metaPath,demFile,true); - } - } - else if( initMethod == WindNinjaInputs::domainAverageInitializationFlag ) - { - nRuns = countRuns(); - //do one run. - if(nRuns == 0) - { - nRuns++; - } -#ifdef NINJAFOAM - army->makeDomainAverageArmy( nRuns, useNinjaFoam); -#else - army->makeDomainAverageArmy( nRuns, false); -#endif - } - else if( initMethod == WindNinjaInputs::wxModelInitializationFlag ) - { - if( !CPLCheckForFile( (char*)weatherFile.c_str(), NULL ) ) - { - QMessageBox::critical( this, tr( "Invalid forecast file." ), - tr( "The forecast file does not exist, " \ - "or it cannot be read." ), - QMessageBox::Ok | QMessageBox::Default ); - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - tree->weather->checkForModelData(); - progressDialog->cancel(); - return false; - } - - std::vector times = tree->weather->timeList(); - /* This can throw a badForecastFile */ - try - { -#ifdef NINJAFOAM - army->makeWeatherModelArmy( weatherFile, timeZone, times, useNinjaFoam ); -#else - army->makeWeatherModelArmy( weatherFile, timeZone, times, false ); -#endif - } - catch( badForecastFile &e ) - { - QMessageBox::critical( this, tr( "Invalid forecast file." ), - tr( "The forecast cannot be read." ), - QMessageBox::Ok | QMessageBox::Default ); - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - tree->weather->checkForModelData(); - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - } catch (...) { - QMessageBox::critical( - this, tr("Failure."), - tr("An unknown error occurred in makeWeatherModelArmy(). This is usually " - "due to a failure in reading the weather model file"), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, - SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - tree->weather->checkForModelData(); - progressDialog->cancel(); - progressDialog->hide(); - delete army; - return false; - } - nRuns = army->getSize(); - } - - 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++) - { - - army->setDEM( i, demFile ); -#ifdef NINJAFOAM - if(caseFile != ""){ - army->setExistingCaseDirectory( i, caseFile ); - } -#endif - //set initialization - if( initMethod != WindNinjaInputs::wxModelInitializationFlag ) - { - army->setInitializationMethod( i, initMethod, true ); - } - //set the ninjaCom - army->setNinjaCommunication( i, i, ninjaComClass::ninjaGUICom ); - - //set the input file - //army.readInputFile( i, demFile ); - - if( inputFileType != LCP ) - { - army->setUniVegetation( i, vegetation ); - } - if( initMethod == WindNinjaInputs::pointInitializationFlag ) //Moved to makePointArmy - { - - } - else if( initMethod == WindNinjaInputs::domainAverageInitializationFlag ) - { - //get speed - army->setInputSpeed( i, - tree->wind->windTable->speed[i]->value(), - inputSpeedUnits); - //get direction - army->setInputDirection( i, tree->wind->windTable->dir[i]->value() ); - - army->setInputWindHeight ( i, inHeight, inHeightUnits ); - } - - //set input output height - army->setOutputWindHeight( i, outHeight, outHeightUnits ); - - //set output speed units - army->setOutputSpeedUnits( i, outputSpeedUnits ); - - //set clipping - army->setOutputBufferClipping( i, (double) clip ); - - army->setOutputPath( i, outputDir.c_str() ); - - //diurnal, if needed - army->setDiurnalWinds( i, useDiurnal ); - if( useDiurnal == true ) - { - if( initMethod == WindNinjaInputs::domainAverageInitializationFlag ) - { - army->setDateTime( i, tree->wind->windTable->date[i]->date().year(), - tree->wind->windTable->date[i]->date().month(), - tree->wind->windTable->date[i]->date().day(), - tree->wind->windTable->time[i]->time().hour(), - tree->wind->windTable->time[i]->time().minute(), - 0, timeZone ); - army->setUniAirTemp( i, - tree->wind->windTable->airTemp[i]->value(), - tempUnits ); - army->setUniCloudCover( i, - tree->wind->windTable->cloudCover[i]->value(), - coverUnits::percent ); - army->setPosition( i, GDALCenterLat, GDALCenterLon ); - } - else if( initMethod == WindNinjaInputs::pointInitializationFlag ) - { - army->setPosition( i, GDALCenterLat, GDALCenterLon ); - } - else if( initMethod == WindNinjaInputs::wxModelInitializationFlag ) - { - army->setPosition( i ); - } - } - else // initMethod is wxModelInitialization or useDiurnal is false - { - army->setPosition( i ); - } - - //stability, if needed, check for diurnal also so we don't repeat setters - if( useStability == true && useDiurnal == false ) - { - if( initMethod == WindNinjaInputs::domainAverageInitializationFlag ) - { - army->setDateTime( i, tree->wind->windTable->date[i]->date().year(), - tree->wind->windTable->date[i]->date().month(), - tree->wind->windTable->date[i]->date().day(), - tree->wind->windTable->time[i]->time().hour(), - tree->wind->windTable->time[i]->time().minute(), - 0, timeZone ); - army->setUniAirTemp( i, - tree->wind->windTable->airTemp[i]->value(), - tempUnits ); - army->setUniCloudCover( i, - tree->wind->windTable->cloudCover[i]->value(), - coverUnits::percent ); - army->setPosition( i, GDALCenterLat, GDALCenterLon ); - } - else if( initMethod == WindNinjaInputs::pointInitializationFlag ) //Moved to makePointArmy - { - army->setPosition( i, GDALCenterLat, GDALCenterLon ); - } - } - army->setStabilityFlag( i, useStability ); - //set mesh stuff - if( customMesh ) - { - army->setMeshResolution( i, meshRes, meshUnits ); - } - else - { -#ifdef NINJAFOAM - if(useNinjaFoam){ - army->setMeshCount( i, ninjafoamMeshChoice ); - army->setNumberOfIterations( i, 300); - } - else - army->setMeshResolutionChoice( i, meshChoice ); -#else - army->setMeshResolutionChoice( i, meshChoice ); -#endif - } - - army->setNumVertLayers( i, 20 ); - - //set the input file - //army.ninjas[i].readInputFile( demFile ); - //army->setDEM( i, demFile ); - // this is commented out? - //army.ninjas[i].mesh.compute_domain_height(); - - //set number of cpus... - //army.setnumberCPUs(1); - - army->setGoogOutFlag (i,writeGoogle); - army->setGoogLineWidth (i,vectorWidth); - army->setGoogResolution (i,googleRes,googleUnits); - army->setGoogSpeedScaling(i,googleScale); - army->setGoogColor (i,googleScheme,googVectorScaling); //FIX ME - army->setGoogConsistentColorScale(i,googConsistentColorScale,nRuns); - army->setShpOutFlag (i,writeShape); - army->setShpResolution (i,shapeRes,shapeUnits); - army->setPDFOutFlag (i,writePdf); - army->setPDFResolution (i,pdfRes,pdfUnits); - army->setPDFLineWidth (i,pdfLineWidth); - army->setPDFBaseMap (i,pdfBase); - army->setPDFSize (i,pdfHeight,pdfWidth,150); - army->setAsciiOutFlag (i,writeFb); - army->setAsciiAaigridOutFlag(i,writeFb); - army->setAsciiProjOutFlag(i,writeFb); - army->setAsciiResolution (i,fbRes,fbUnits); - //army->setWriteAtmFile (i,writeAtm ); - army->setVtkOutFlag (i,writeVTK); - - if( initMethod == WindNinjaInputs::wxModelInitializationFlag && - writeWxOutput == true ) - { - army->setWxModelGoogOutFlag( i, writeGoogle ); - army->setWxModelShpOutFlag( i, writeShape ); - army->setWxModelAsciiOutFlag( i, writeFb ); - } - - //army.setOutputFilenames(); - army->setNinjaComNumRuns( i, nRuns ); - } - - army->set_writeFarsiteAtmFile( writeAtm && writeFb ); - - for( unsigned int i = 0; i < army->getSize(); i++ ) - { - progressLog.push_back(0); //Initialize the progressLog, which stores the progress of each ninja with a zero for each ninja in the army - } - - totalProgress = 0; - - progressDialog->setLabelText("Solving..."); - - /* - connect(army.Com, SIGNAL(sendMessage(QString, QColor)), - this, SLOT(writeToConsole(QString, QColor)), - Qt::AutoConnection); - */ - for( unsigned int i = 0; i < army->getSize(); i++ ) - { - connect( army->getNinjaCom( i ), - SIGNAL( sendMessage( QString, QColor ) ), this, - SLOT( updateProgress( QString ) ), Qt::AutoConnection ); - - connect( army->getNinjaCom( i ), - SIGNAL( sendProgress( int, int ) ), this, - SLOT( updateProgress( int, int ) ), Qt::AutoConnection ); - - connect( army->getNinjaCom( i ), - SIGNAL( sendMessage(QString, QColor)), - this, SLOT(writeToConsole(QString, QColor ) ), - Qt::AutoConnection ); - } - writeToConsole(QString::number( army->getSize() ) + " runs initialized. Starting solver..."); - //sThread->start(); - - progressDialog->setValue( 0 ); - runTime->restart(); - connect( progressDialog, SIGNAL(canceled() ), - this, SLOT( cancelSolve() ) ); - - progressDialog->setCancelButtonText( "Cancel" ); - - setCursor( Qt::WaitCursor ); - - progressDialog->setLabelText( "Running..." ); - - writeToConsole( "Initializing runs..." ); - - bool ninjaSuccess = false; - //ninjaSuccess = sThread->run( nThreads, army ); - //start the army - try { - ninjaSuccess = army->startRuns( nThreads ); - } - catch (bad_alloc& e) - { - progressDialog->cancel(); - QMessageBox::warning(this, tr("Exception Caught"), - tr("WindNinja may have run out of memory. This may be caused by too fine of a mesh resolution."), - QMessageBox::Ok | QMessageBox::Default); - - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - delete army; - return false; - } - catch (cancelledByUser& e) - { - progressDialog->cancel(); - QMessageBox::warning(this, tr("Exception Caught"), - tr(e.what()), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - delete army; - return false; - } - catch (exception& e) - { - progressDialog->cancel(); - QMessageBox::warning(this, tr("Exception Caught"), - tr(e.what()), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - delete army; - return false; - } - catch (...) - { - progressDialog->cancel(); - QMessageBox::warning(this, tr("Exception Caught"), - tr("Unknown Exception"), - QMessageBox::Ok | QMessageBox::Default); - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - setCursor(Qt::ArrowCursor); - delete army; - return false; - } - - disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); - - writeToConsole("Finished with simulations", Qt::darkGreen); - //updateTimer(); - - elapsedRunTime = runTime->elapsed() / 1000.0; - int maxProg = nRuns*100; - progressDialog->setValue(maxProg); - progressDialog->setLabelText("Simulations finished"); - progressDialog->setCancelButtonText("Close"); - progressLog.clear(); //Clear the progress bar so that we can do another run later without - //killing the program - - //Everything went okay? enable output path button - tree->solve->openOutputPathButton->setEnabled( true ); - outputPath = QString::fromStdString( outputDir ); - - //clear the army - army->reset(); - - setCursor(Qt::ArrowCursor); - - delete army; - return ninjaSuccess; -} - -void mainWindow::updateProgress(const QString message) -{ - progressDialog->setLabelText(message); -} - -void mainWindow::updateProgress(int run, int progress) -{ - totalProgress = 0; //Initialize the progress bar each time -// runProgress[run]=progress; - - if(progressLog[run]>progress) - { -/* - * If the stored progress is bigger than what we are seeing in the currently emitted progress - * ignore it - * this happens for pointInitialization, when the match points is iterating - * sometimes its next solution is worse and then it would make the progress bar go backwards - * by ignoring it, the progress bar just stays where it is.... - */ - progressLog[run]=progressLog[run]; - } - else //Otherwise, store the progress in the progressLog - { - progressLog[run]=progress; - } - for(int i = 0;i < nRuns;i++) //Iterate over the number of runs and sum up the progress from the Log - { - totalProgress+=progressLog[i]; - - } - progressDialog->setValue(totalProgress); //Set the progress to what we have summed -} - -int mainWindow::countRuns() -{ - int runs = 0; - - for(int i=0; i < tree->wind->windTable->nRuns; i++) - { - if(tree->wind->windTable->speed[i]->value() != 0 || tree->wind->windTable->dir[i]->value() != 0) - { - runs = i+1; // i goes from 0 to N-1, runs goes from 1 to N - } - } - - return runs; -} - -int mainWindow::checkAllItems() -{ - //check and see if the objects have been visited before changing - eInputStatus status = green; -#ifdef NINJAFOAM - checkSolverMethodItem(); - checkMeshCombo(); -#endif - checkInputItem(); - checkOutputItem(); - checkSolveItem(); - - return status; -} - -#ifdef NINJAFOAM -int mainWindow::checkSolverMethodItem() -{ - eInputStatus status = blue; - - checkNativeSolverItem(); - checkNinjafoamItem(); - - if(checkNativeSolverItem() == green) - { - tree->solverMethodItem->setIcon(0, tree->check); - tree->solverMethodItem->setToolTip(0, "Using conservation of mass solver"); - checkNinjafoamItem(); - status = green; - } - else if(checkNinjafoamItem() == green) - { - tree->solverMethodItem->setIcon(0, tree->check); - tree->solverMethodItem->setToolTip(0, "Using conservation of mass and momentum solver"); - checkNativeSolverItem(); - status = green; - } - else - { - tree->solverMethodItem->setIcon(0, tree->cross); - tree->solverMethodItem->setToolTip(0, "Select a solver"); - status = red; - } - return status; -} - -int mainWindow::checkNativeSolverItem() -{ - eInputStatus status = green; - if(!tree->nativesolver->nativeSolverGroupBox->isChecked()) - { - tree->nativeSolverItem->setIcon(0, tree->radio); - tree->nativeSolverItem->setToolTip(0, "Conservation of Mass not selected"); - status = blue; - } - else - { - tree->nativeSolverItem->setIcon(0, tree->check); - tree->nativeSolverItem->setToolTip(0, "Conservation of Mass selected"); - status = green; - } - - return status; -} -int mainWindow::checkNinjafoamItem() -{ - eInputStatus status = green; - if(!tree->ninjafoam->ninjafoamGroupBox->isChecked()) - { - tree->ninjafoamItem->setIcon(0, tree->radio); - tree->ninjafoamItem->setToolTip(0, "Conservation of Mass and Momentum not selected"); - status = blue; - } - else - { - tree->ninjafoamItem->setIcon(0, tree->check); - tree->ninjafoamItem->setToolTip(0, "Conservation of Mass and Momentum selected"); - status = green; - } - - return status; -} -#endif //NINJAFOAM - -int mainWindow::checkInputItem() -{ - eInputStatus status = red; - - checkWindItem(); - - if(checkSurfaceItem() == red && checkWindItem() == red && checkDiurnalItem() == red || checkStabilityItem() == red) - { - tree->inputItem->setIcon(0, tree->cross); - tree->inputItem->setToolTip(0, "Check surface input, wind input, stability input, and diurnal input"); - status = red; - } - if(checkSurfaceItem() == red && checkWindItem() == red && checkDiurnalItem() == red ) - { - tree->inputItem->setIcon(0, tree->cross); - tree->inputItem->setToolTip(0, "Check surface input, wind input and diurnal input"); - status = red; - } - else if(checkSurfaceItem() == red) - { - tree->inputItem->setIcon(0, tree->cross); - tree->inputItem->setToolTip(0, "Check surface input"); - status = red; - } - else if(checkDiurnalItem() == red) - { - tree->inputItem->setIcon(0, tree->caution); - tree->inputItem->setToolTip(0, "Check diurnal input"); - status = red; - } - else if(checkStabilityItem() == red) - { - tree->inputItem->setIcon(0, tree->caution); - tree->inputItem->setToolTip(0, "Check stability input"); - status = red; - } - else if(checkWindItem() == red) - { - tree->inputItem->setIcon(0, tree->cross); - tree->inputItem->setToolTip(0, "Check wind input"); - status = red; - } - else - { - tree->inputItem->setIcon(0, tree->check); - tree->inputItem->setToolTip(0, "Valid"); - status = green; - } - return status; -} - -int mainWindow::checkSurfaceItem() -{ - eInputStatus status = red; - if(inputFileName == "" || !QFile::exists(inputFileName)) - { - tree->surfaceItem->setIcon(0, tree->cross); - tree->surfaceItem->setToolTip(0, "The input file cannot be opened"); - status = red; - } - else if(!hasPrj) - { - tree->surfaceItem->setIcon(0, tree->caution); - tree->surfaceItem->setToolTip(0, "No projection information"); - status = red; - } - else - { - tree->surfaceItem->setIcon(0, tree->check); - tree->surfaceItem->setToolTip(0, "Valid"); - status = green; - } - return status; -} - -int mainWindow::checkDiurnalItem() -{ - eInputStatus status = green; - if(!tree->diurnal->diurnalGroupBox->isChecked()) { - tree->diurnalItem->setIcon(0, tree->blue); - tree->diurnalItem->setToolTip(0, "No Diurnal Input"); - status = blue; - } - else - { - tree->diurnalItem->setIcon(0, tree->check); - status = green; - } - return status; -} - -int mainWindow::checkStabilityItem() -{ - eInputStatus status = green; - if(!tree->stability->stabilityGroupBox->isChecked()) - { - tree->stabilityItem->setIcon(0, tree->blue); - tree->stabilityItem->setToolTip(0, "No Stability Input"); - status = blue; - } - else - { - tree->stabilityItem->setIcon(0, tree->check); - status = green; - } - return status; -} - -int mainWindow::checkWindItem() -{ - eInputStatus status = blue; - - //check all once, just to update icons - checkSpdDirItem(); - checkPointItem(); - checkWeatherItem(); - - if( checkSpdDirItem() == blue && checkPointItem() == blue - && checkWeatherItem() == blue ) { - tree->windItem->setIcon(0, tree->cross); - tree->windItem->setToolTip(0, "No initialization selected"); - status = red; - } - else if( checkSpdDirItem() == red ) { - tree->windItem->setIcon(0, tree->cross); - tree->windItem->setToolTip(0, "Check speed and direction"); - status = red; - } - else if( checkSpdDirItem() == amber ) { - tree->windItem->setIcon(0, tree->caution); - tree->windItem->setToolTip(0, "No runs have been added, one run will be done at speed = 0, dir = 0"); - status = amber; - } - else if( checkPointItem() == red ) { - tree->windItem->setIcon(0, tree->cross); - tree->windItem->setToolTip(0, "Check point initialization"); - status = red; - } - else if( checkWeatherItem() == red ) { - tree->windItem->setIcon(0, tree->cross); - tree->windItem->setToolTip(0, "Check weather model initialization"); - status = red; - } - else { - tree->windItem->setIcon(0, tree->check); - tree->windItem->setToolTip(0, "Valid"); - status = green; - } - return status; -} - -int mainWindow::checkSpdDirItem() -{ - int runs = countRuns(); - eInputStatus status = blue; - if( tree->wind->windGroupBox->isChecked() ) { - if(checkSurfaceItem() != red) { - if(runs == 0 && tree->diurnal->diurnalGroupBox->isChecked() == false) { - tree->spdDirItem->setIcon(0, tree->cross); - tree->spdDirItem->setToolTip(0, "No runs have been added, diurnal is not active"); - status = red; - } - else if(runs == 0 && tree->diurnal->diurnalGroupBox->isChecked() == true) { - tree->spdDirItem->setIcon(0, tree->caution); - tree->spdDirItem->setToolTip(0, "No runs have been added, one run will be done at speed = 0, dir = 0 while using diurnal"); - status = amber; - } - else { - tree->spdDirItem->setIcon(0, tree->check); - tree->spdDirItem->setToolTip(0, QString::number(runs) + " runs"); - status = green; - // override if any 0.0 wind speed runs are detected, warn and run if diurnal, stop if not diurnal - for(int i=0;iwind->windTable->speed[i]->value() == 0.0) - { - if(tree->diurnal->diurnalGroupBox->isChecked() == false) { - tree->spdDirItem->setIcon(0, tree->cross); - tree->spdDirItem->setToolTip(0, QString::number(runs) + " runs have been added, but detecting at least one 0.0 wind speed run without diurnal being active"); - status = red; - } else { - tree->spdDirItem->setIcon(0, tree->caution); - tree->spdDirItem->setToolTip(0, QString::number(runs) + " runs have been added, detecting at least one 0.0 wind speed run, diurnal is active so will continue the runs"); - status = amber; - } - break; - } - } - } - } - else { - tree->spdDirItem->setIcon(0, tree->cross); - tree->spdDirItem->setToolTip(0, "Cannot read input file"); - status = red; - } - } - else { - tree->spdDirItem->setIcon(0, tree->radio); - tree->spdDirItem->setToolTip(0, "Not used"); - status = blue; - } - return status; -} -int mainWindow::checkPointItem() -{ - eInputStatus status = blue; - if( tree->point->pointGroupBox->isChecked() ) { - bool shortGo=tree->point->pointGo; - if (shortGo==false) - { - if (tree->point->stationFileList.size()<1) - { - status = red; - tree->pointItem->setIcon(0,tree->cross); - tree->pointItem->setToolTip(0,"No Stations Selected"); - } - if (tree->point->stationFileList.size()==1) - { - status = red; - tree->pointItem->setIcon(0,tree->cross); - tree->pointItem->setToolTip(0,"No Valid Data detected..."); - } - if (tree->point->stationFileList.size()>=2) - { - status = red; - tree->pointItem->setIcon(0,tree->cross); - tree->pointItem->setToolTip(0,"Mismatched Data Type selected"); - } -// if else -// { -// status = red; -// tree->pointItem->setIcon(0,tree->cross); -// tree->pointItem->setToolTip(0,"Selected options are invald"); -// } - } - - if (shortGo==true) - { - //Check to make sure times are reasonable - QDateTime pStartTime = tree->point->startTime->dateTime(); - QDateTime pStopTime = tree->point->stopTime->dateTime(); - if(pStartTime>pStopTime) - { - status = red; - tree->pointItem->setIcon(0,tree->cross); - tree->pointItem->setToolTip(0,"Start Time is Greater than Stop Time!"); - } - else - { - status = green; - tree->pointItem->setIcon(0,tree->check); - tree->pointItem->setToolTip(0,"Good To Go!"); - } - } - std::vector pfL = tree->point->stationFileList; - for(int i=0;ipointItem->setIcon(0,tree->cross); - tree->pointItem->setToolTip(0,"File Extension Must be .csv!"); - } - } - if (tree->point->simType==0 && pfL.size()>1) - { - status = red; - tree->pointItem->setIcon(0,tree->cross); - tree->pointItem->setToolTip(0,"Too many Stations Selected for Old Format!"); - - } - -// else { -// status = green; -// tree->pointItem->setIcon( 0, tree->check ); -// tree->pointItem->setToolTip( 0, "Valid" ); -// } - } - else { - status = blue; - tree->pointItem->setIcon( 0, tree->radio ); - tree->pointItem->setToolTip( 0, "Not used" ); - } - return status; -} - -int mainWindow::checkWeatherItem() -{ - eInputStatus status = blue; - wxModelInitialization* model = NULL; - if( tree->weather->weatherGroupBox->isChecked() ) { - QFileInfo fi; - QModelIndex mi = tree->weather->treeView->selectionModel()->currentIndex(); - if( mi.isValid() ) { - fi = tree->weather->model->fileInfo( mi ); - std::string filename = fi.absoluteFilePath().toStdString(); - char *p, *q; //code gore - p = strdup( filename.c_str() ); - q = strrchr( p, '/' ); - int n = 0; - if( !q ) - q = strrchr( p, '\\' ); - if( q ) - { - if( strlen( q ) > 1 ) - q++; - if( strlen( q ) > 5 ) - *(q + 4) = '\0'; - n = atoi( q ); - } - else - n = atoi( p ); - free( p ); - if( fi.isDir() && n < 2000 ) - { - status = red; - tree->modelItem->setIcon( 0, tree->cross ); - tree->modelItem->setToolTip( 0, "Forecast is invalid" ); - return status; - } - try { - model = wxModelInitializationFactory::makeWxInitialization(filename); - } - catch( ... ) { - status = red; - tree->modelItem->setIcon( 0, tree->cross ); - tree->modelItem->setToolTip( 0, "Forecast is invalid" ); - delete model; - return status; - } - - if( !fi.exists() ) { - status = red; - tree->modelItem->setIcon( 0, tree->cross ); - tree->modelItem->setToolTip( 0, "Forecast does not exist" ); - } - else { - status = green; - tree->modelItem->setIcon( 0, tree->check ); - tree->modelItem->setToolTip( 0, "Valid" ); - } - } - else { - status = red; - tree->modelItem->setIcon( 0, tree->cross ); - tree->modelItem->setToolTip( 0, "You must select a valid forecast file or path" ); - } - } - else { - tree->modelItem->setIcon( 0, tree->radio ); - tree->modelItem->setToolTip( 0, "Not used" ); - } - delete model; - return status; -} - -int mainWindow::checkOutputItem() -{ - eInputStatus status = green; - if(checkSurfaceItem() == red) - { - tree->outputItem->setIcon(0, tree->cross); - tree->outputItem->setToolTip(0, "Cannot read input file"); - status = red; - } - if(checkGoogleItem() == blue && checkFbItem() == blue && checkShapeItem() == blue && checkVtkItem() == blue && - checkPdfItem() == blue) - { - tree->outputItem->setIcon(0, tree->cross); - tree->outputItem->setToolTip(0, "No outputs selected"); - status = red; - } - if(checkGoogleItem() == amber || checkFbItem() == amber || checkShapeItem() == amber || checkVtkItem() == amber || - checkPdfItem() == amber) - { - if(checkGoogleItem() == amber) - { - tree->outputItem->setIcon(0, tree->caution); - tree->outputItem->setToolTip(0, "Check Google ouput"); - status = amber; - } - if(checkFbItem() == amber) - { - tree->outputItem->setIcon(0, tree->check); - tree->outputItem->setToolTip(0, "Check fire behavior ouput"); - status = amber; - } - if(checkShapeItem() == amber) - { - tree->outputItem->setIcon(0, tree->check); - tree->outputItem->setToolTip(0, "Check shape file ouput"); - status = amber; - } - if(checkPdfItem() == amber) - { - tree->outputItem->setIcon(0, tree->check); - tree->outputItem->setToolTip(0, "Check pdf file ouput"); - status = amber; - } - if(checkVtkItem() == amber) - { - tree->outputItem->setIcon(0, tree->check); - tree->outputItem->setToolTip(0, "Check vtk file ouput"); - status = amber; - } - } - if(status == green) - { - tree->outputItem->setIcon(0, tree->check); - tree->outputItem->setToolTip(0, "Valid"); - status = green; - } - return status; -} - -int mainWindow::checkGoogleItem() -{ - eInputStatus status = red; - if(!tree->google->googleGroupBox->isChecked()) - { - tree->googleItem->setIcon(0, tree->blue); - tree->googleItem->setToolTip(0, "No output"); - status = blue; - } - else - { - if(checkSurfaceItem() != red) - { - 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(GDALCellSize > tree->google->googleResSpinBox->value()) - { - tree->googleItem->setIcon(0, tree->caution); - tree->googleItem->setToolTip(0, "The output resolution is finer than the DEM resolution"); - status = amber; - } - /* - else if((int)meshCellSize > tree->google->googleResSpinBox->value()) - { - tree->googleItem->setIcon(0, tree->check); - tree->googleItem->setToolTip(0, "The output resolutions is finer than the computational mesh"); - status = amber; - } - */ - else - { - tree->googleItem->setIcon(0, tree->check); - tree->googleItem->setToolTip(0, "Valid"); - status = green; - } - } - else - { - tree->googleItem->setIcon(0, tree->cross); - tree->googleItem->setToolTip(0, "Cannot read input file"); - status = red; - } - } - return status; -} - -int mainWindow::checkFbItem() -{ - eInputStatus status = red; - if(!tree->fb->fbGroupBox->isChecked()) - { - tree->fbItem->setIcon(0, tree->blue); - tree->fbItem->setToolTip(0, "No output"); - status = blue; - } - else - { - if(checkSurfaceItem() == green || checkSurfaceItem() == amber) - { - 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"); - status = amber; - } - - else - { - tree->fbItem->setIcon(0, tree->check); - tree->fbItem->setToolTip(0, "Valid"); - status = green; - } - } - else - { - tree->fbItem->setIcon(0, tree->cross); - tree->fbItem->setToolTip(0, "Cannot read input file"); - status = red; - } - } - return status; -} - -int mainWindow::checkShapeItem() -{ - eInputStatus status = red; - if(!tree->shape->shapeGroupBox->isChecked()) - { - tree->shapeItem->setIcon(0, tree->blue); - tree->shapeItem->setToolTip(0, "No output"); - status = blue; - } - else - { - if(checkSurfaceItem() == green || checkSurfaceItem() == amber) - { - /* - if((int)meshCellSize > tree->shape->shapeResSpinBox->value()) - { - tree->shapeItem->setIcon(0, tree->check); - tree->shapeItem->setToolTip(0, "The output resolutions is finer than the computational mesh"); - status = amber; - } - */ - 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"); - status = amber; - } - else - { - tree->shapeItem->setIcon(0, tree->check); - tree->shapeItem->setToolTip(0, "Valid"); - status = green; - } - } - else - { - tree->shapeItem->setIcon(0, tree->cross); - tree->shapeItem->setToolTip(0, "Cannot read input file") ; - status = red; - } - } - return status; -} - -int mainWindow::checkPdfItem() -{ - eInputStatus status = red; - if(!tree->pdf->pdfGroupBox->isChecked()) - { - tree->pdfItem->setIcon(0, tree->blue); - tree->pdfItem->setToolTip(0, "No output"); - status = blue; - } - else - { - if(checkSurfaceItem() == green || checkSurfaceItem() == amber) - { - /* - if((int)meshCellSize > tree->pdf->pdfResSpinBox->value()) - { - tree->pdfItem->setIcon(0, tree->check); - tree->pdfItem->setToolTip(0, "The output resolutions is finer than the computational mesh"); - status = amber; - } - */ - 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"); - status = amber; - } - else - { - tree->pdfItem->setIcon(0, tree->check); - tree->pdfItem->setToolTip(0, "Valid"); - status = green; - } - } - else - { - tree->pdfItem->setIcon(0, tree->cross); - tree->pdfItem->setToolTip(0, "Cannot read input file") ; - status = red; - } - } - return status; -} - -int mainWindow::checkVtkItem() -{ - eInputStatus status = red; - if(!tree->vtk->vtkGroupBox->isChecked()) - { - tree->vtkItem->setIcon(0, tree->blue); - tree->vtkItem->setToolTip(0, "No output"); - status = blue; - } - else - { - if(checkSurfaceItem() == green || checkSurfaceItem() == amber) - { - tree->vtkItem->setIcon(0, tree->check); - tree->vtkItem->setToolTip(0, "Valid"); - status = green; - } - else - { - tree->vtkItem->setIcon(0, tree->cross); - tree->vtkItem->setToolTip(0, "Cannot read input file") ; - status = red; - } - } - return status; -} - -int mainWindow::checkSolveItem() -{ - eInputStatus status = red; - if(checkInputItem() != red && checkOutputItem() != red) - { - tree->solveItem->setIcon(0, tree->check); - tree->solveItem->setToolTip(0, "You may start the solver"); - tree->solve->solveToolButton->setEnabled(true); - status = green; - } - //handle if nothing is checked for initialization - else if( !tree->wind->windGroupBox->isChecked() && - !tree->point->pointGroupBox->isChecked() && - !tree->weather->weatherGroupBox->isChecked () ) { - tree->solveItem->setIcon(0, tree->cross); - tree->solveItem->setToolTip(0, "You must select and initialization method"); - tree->solve->solveToolButton->setEnabled(false); - status = red; - } - else - { - tree->solveItem->setIcon(0, tree->cross); - tree->solveItem->setToolTip(0, "There are errors in the inputs or outputs"); - tree->solve->solveToolButton->setEnabled(false); - status = red; - } - return status; -} - -int mainWindow::checkKmlLimit(double xx) -{ - eInputStatus status = amber; - if((int)noGoogleCellSize > tree->google->googleResSpinBox->value()) - { - writeToConsole("The resolution of the google file may be too fine.", orange); - status = amber; - } - return amber; -} - -void mainWindow::cancelSolve() -{ - progressDialog->setAutoClose(true); - progressDialog->setLabelText("Canceling..."); - army->cancel(); -} - -void mainWindow::treeDoubleClick(QTreeWidgetItem *item, int column) -{ - if(item == tree->surfaceItem) - openInputFile(); - else if(item == tree->diurnalItem) - { - if(tree->diurnal->diurnalGroupBox->isChecked()) - tree->diurnal->diurnalGroupBox->setChecked(false); - else - tree->diurnal->diurnalGroupBox->setChecked(true); - } -#ifdef NINJAFOAM - else if(item == tree->nativeSolverItem) - { - if(tree->nativesolver->nativeSolverGroupBox->isChecked()) - tree->nativesolver->nativeSolverGroupBox->setChecked(false); - else{ - tree->nativesolver->nativeSolverGroupBox->setChecked(true); - } - } - else if(item == tree->ninjafoamItem) - { - if(tree->ninjafoam->ninjafoamGroupBox->isChecked()) - tree->ninjafoam->ninjafoamGroupBox->setChecked(false); - else{ - tree->ninjafoam->ninjafoamGroupBox->setChecked(true); - } - } -#endif - else if(item == tree->stabilityItem) - { - if(tree->stability->stabilityGroupBox->isChecked()) - tree->stability->stabilityGroupBox->setChecked(false); - else - tree->stability->stabilityGroupBox->setChecked(true); - } - else if( item == tree->spdDirItem ) { - if( tree->wind->windGroupBox->isChecked() ) - tree->wind->windGroupBox->setChecked( false ); - else - tree->wind->windGroupBox->setChecked( true ); - } - else if( item == tree->pointItem ) { - if( tree->point->pointGroupBox->isChecked() ) - tree->point->pointGroupBox->setChecked( false ); - else - tree->point->pointGroupBox->setChecked( true ); - } - else if( item == tree->modelItem ) { - if( tree->weather->weatherGroupBox->isChecked() ) - tree->weather->weatherGroupBox->setChecked( false ); - else - tree->weather->weatherGroupBox->setChecked( true ); - } - else if(item == tree->googleItem) - { - if(tree->google->googleGroupBox->isChecked()) - tree->google->googleGroupBox->setChecked(false); - else - tree->google->googleGroupBox->setChecked(true); - } - else if(item == tree->fbItem) - { - if(tree->fb->fbGroupBox->isChecked()) - tree->fb->fbGroupBox->setChecked(false); - else - tree->fb->fbGroupBox->setChecked(true); - } - else if(item == tree->shapeItem) - { - if(tree->shape->shapeGroupBox->isChecked()) - tree->shape->shapeGroupBox->setChecked(false); - else - tree->shape->shapeGroupBox->setChecked(true); - } - else if(item == tree->pdfItem) - { - if(tree->pdf->pdfGroupBox->isChecked()) - tree->pdf->pdfGroupBox->setChecked(false); - else - tree->pdf->pdfGroupBox->setChecked(true); - } - else if(item == tree->vtkItem) - { - if(tree->vtk->vtkGroupBox->isChecked()) - tree->vtk->vtkGroupBox->setChecked(false); - else - tree->vtk->vtkGroupBox->setChecked(true); - } - checkAllItems(); - return; -} - -/** - * \brief Check the input dem for no data and optionally fill - * - * Note that if we fail to fix things, we have to close the ds. - * - * \return - * - */ -QString mainWindow::checkForNoData( QString inputFile ) -{ - QString fileName; - QString newFile = inputFile; - GDALDataset *poDS = (GDALDataset*)GDALOpen( inputFile.toStdString().c_str(), - GA_ReadOnly ); - if( GDALHasNoData( poDS, 1 ) ) - { - int r = QMessageBox::warning( this, 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( r == QMessageBox::Yes ) - { - fileName = QFileDialog::getSaveFileName( this, - tr( "Open Elevation Input File" ), - inputFileDir.absolutePath(), - tr( "GeoTiff (*.tif)" ) ); - if( fileName.isEmpty() ) - return QString(""); - - if( !fileName.endsWith( ".tif", Qt::CaseInsensitive ) ) - fileName += ".tif"; - GDALDriverH hDriver = GDALGetDriverByName( "GTiff" ); - GDALDatasetH hNewDS; - hNewDS = GDALCreateCopy( hDriver, fileName.toStdString().c_str(), - (GDALDriverH)poDS, FALSE, NULL, NULL, - NULL ); - GDALRasterBandH hSrcBand, hDstBand; - hSrcBand = GDALGetRasterBand( (GDALDatasetH)poDS, 1 ); - hDstBand = GDALGetRasterBand( hNewDS, 1 ); - int nSuccess; - GDALSetRasterNoDataValue( hDstBand, - GDALGetRasterNoDataValue( hSrcBand, &nSuccess ) ); - int nNoData = GDALFillBandNoData( (GDALDataset*)hNewDS, 1, 100 ); - if( nNoData ) - { - QMessageBox::warning( this, tr ("WindNinja" ), - tr( "Could not fill no data pixels, too many pixels " - "were invalid." ), - QMessageBox::Ok ); - GDALClose( hNewDS ); - GDALClose( (GDALDatasetH)poDS ); - VSIUnlink( fileName.toStdString().c_str() ); - newFile = ""; - } - else - { - GDALFlushCache( hNewDS ); - GDALClose( hNewDS ); - GDALClose( (GDALDatasetH)poDS ); - newFile = fileName; - } - } - } - else - GDALClose( (GDALDatasetH)poDS ); - return newFile; -} - -void mainWindow::enablePointDate(bool enable) -{ - (void)enable; - if( tree->point->pointGroupBox->isChecked() ) - { - if( tree->diurnal->diurnalGroupBox->isChecked() - || tree->stability->stabilityGroupBox->isChecked() - ) - { - //Allows for on the fly changes in diurnal parameters as users select/deselect stations of - //various types - emit mainDiurnalChanged(true); //Sets to true so that stability options are also set - if(tree->point->simType==0) //Only turn on datetimeEdit for single step runs old format - { - tree->point->dateTimeEdit->setEnabled( true ); - } - else - { - tree->point->dateTimeEdit->setEnabled(false); - } - } - else - { - emit mainDiurnalChanged(false); //Tells pointInput we don't want stability/Diurnal - tree->point->dateTimeEdit->setEnabled( false ); - } - } -} - -#ifdef NINJAFOAM -void mainWindow::enableNinjafoamOptions(bool enable) -{ - (void)enable; - if( tree->ninjafoam->ninjafoamGroupBox->isChecked() ) - { - tree->stability->stabilityGroupBox->setCheckable( false ); - tree->stability->stabilityGroupBox->setChecked( false ); - tree->stability->stabilityGroupBox->setHidden( true ); - tree->stability->ninjafoamConflictLabel->setHidden( false ); - - tree->wind->windTable->enableDiurnalCells( false ); - - tree->point->pointGroupBox->setCheckable( false ); - tree->point->pointGroupBox->setChecked( false ); - tree->point->pointGroupBox->setHidden( true ); - tree->point->ninjafoamConflictLabel->setHidden( false ); - - tree->surface->foamCaseGroupBox->setHidden( false ); - tree->surface->timeZoneGroupBox->setHidden( false ); - - tree->vtk->vtkLabel->setHidden( false ); - tree->vtk->vtkGroupBox->setHidden( false ); - tree->vtk->vtkGroupBox->setCheckable(true); - tree->vtk->vtkGroupBox->setChecked( false ); - } - else{ - tree->diurnal->diurnalGroupBox->setCheckable( true ); - tree->diurnal->diurnalGroupBox->setChecked( false ); - tree->diurnal->diurnalGroupBox->setHidden( false ); - - tree->stability->stabilityGroupBox->setCheckable( true ); - tree->stability->stabilityGroupBox->setChecked( false ); - tree->stability->stabilityGroupBox->setHidden( false ); - tree->stability->ninjafoamConflictLabel->setHidden( true ); - - tree->point->pointGroupBox->setCheckable( true ); - tree->point->pointGroupBox->setChecked( false ); - tree->point->pointGroupBox->setHidden( false ); - tree->point->ninjafoamConflictLabel->setHidden( true ); - - tree->surface->foamCaseGroupBox->setHidden( true ); - tree->surface->timeZoneGroupBox->setHidden( false ); - tree->surface->meshResComboBox->addItem("Custom", 4); - - tree->vtk->vtkLabel->setHidden( false ); - tree->vtk->vtkGroupBox->setHidden( false ); - tree->vtk->vtkGroupBox->setCheckable( true ); - tree->vtk->vtkGroupBox->setChecked( false ); - } -} -#endif - -void mainWindow::SetConfigOption() -{ - QString key, val; - int rc; - SetConfigDialog dialog; - rc = dialog.exec(); - if( rc == QDialog::Rejected ) - return; - const char *pszKey, *pszVal; - key = dialog.GetKey(); - val = dialog.GetVal(); - if( key == "" ) - return; - if( val == "" ) - pszVal = NULL; - else - pszVal = CPLSPrintf( "%s", (char*)val.toLocal8Bit().data() ); - qDebug() << "Setting config option " << key << "to" << val; - pszKey = CPLSPrintf( "%s", (char*)key.toLocal8Bit().data() ); - CPLSetConfigOption( pszKey, pszVal ); -} diff --git a/src/gui/mainWindow.h b/src/gui/mainWindow.h deleted file mode 100644 index c59230f69..000000000 --- a/src/gui/mainWindow.h +++ /dev/null @@ -1,301 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Main window and parent to all other widgets - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gdal_priv.h" -#include "ogr_srs_api.h" -#include "cpl_string.h" -#include "gdal_util.h" -#include "startRuns.h" -#include "ninja.h" -#include "ninja_version.h" -#include "WindNinjaTree.h" -#include "consoleDockWidget.h" -#include "solveThread.h" -#include "ninjaException.h" -#include "ninjaUnits.h" -#include "wxStation.h" -#include "ninjaUnits.h" -#include "ninjaArmy.h" -#include "ninja_conv.h" - -#include "setconfigdialog.h" - -class mainWindow : public QMainWindow -{ - Q_OBJECT - - public: - mainWindow(QWidget *parent = 0); - - //ninja *inputNinja; - - ninjaArmy *army; - - QProgressDialog *progressDialog; - int nRuns; - - QDir cwd; - QDir pwd; - - QTimer *timer; - - int *runProgress; - int totalProgress; - std::vector progressLog; - - bool okToContinueCheck; - - public slots: - void updateProgress(int run, int progress); - void updateProgress(const QString message); - void updateTimer(); - void openDEMDownloader(); - - public: - WindNinjaTree *tree; - ConsoleDockWidget *console; - QString prompt; - QColor orange; - - enum eInputFileType{ - ASC, LCP, GTIFF, IMG}; -#ifdef NINJAFOAM - QString existingCaseDir; -#endif - QString inputFileName; - QDir inputFileDir; - QString shortInputFileName; - QString outputPath; - int inputFileType; - bool hasPrj; - QString prjFileName; - double meshCellSize; - - enum eInputStatus{ - blue, green, amber, red}; - - int lineNumber; - - //GDAL values - QString GDALDriverName, GDALDriverLongName; - std::string GDALProjRef; - bool hasGDALCenter; - double GDALCenterLat; - double GDALCenterLon; - int GDALXSize, GDALYSize; - double GDALCellSize, GDALNoData; - double GDALMaxValue, GDALMinValue; - - //threshold for no-googling = 400000 - - static const int noGoogleNumCells = 400000; - double noGoogleCellSize; - //threads - solveThread *sThread; - - signals: - void inputFileChanged(QString newFile); - void mainDiurnalChanged(bool dC); - - public slots: - -#ifdef PHONE_HOME_QUERIES_ENABLED - void checkMessages(); -#endif -#ifdef NINJAFOAM - void openExistingCase(); - void updateFileInputForCase(const char* file); -#endif - void openInputFile(); - void updateFileInput(const char* file); - void inputFileDeleted(); - void openMainWindow(); - double computeCellSize(int index); - int checkInputFile(QString fileName); - void checkMeshCombo(); - void checkMeshUnits(bool checked); - void updateOutRes(); - void setPrompt(QString p); - void writeToConsole(QString message, QColor color = Qt::black); - - void writeConsoleOutput(); - void resampleData(); - void writeBlankStationFile(); - void windNinjaHelp(); - void displayArcGISPro(); - void tutorial1(); - void tutorial2(); - void tutorial3(); - void tutorial4(); - void demDownload(); - void fetchDem(); - void cliInstructions(); - void aboutWindNinja(); - void citeWindNinja(); - void supportEmail(); - void bugReport(); - int openHelp(int target = 0); - - void treeDoubleClick(QTreeWidgetItem *item, int column); - - bool getLatLon(); - - void test(); - - int solve(); - void cancelSolve(); - int countRuns(); - - void openOutputPath(); - - //functions for checking inputItems - int checkInputItem(); - int checkSurfaceItem(); - int checkDiurnalItem(); - int checkStabilityItem(); -#ifdef NINJAFOAM - int checkNativeSolverItem(); - int checkNinjafoamItem(); - int checkSolverMethodItem(); - void selectNativeSolver( bool pick ); - void selectNinjafoamSolver( bool pick ); -#endif - int checkWindItem(); - int checkSpdDirItem(); - int checkPointItem(); - int checkWeatherItem(); - int checkOutputItem(); - int checkGoogleItem(); - int checkFbItem(); - int checkShapeItem(); - int checkPdfItem(); - int checkVtkItem(); - int checkSolveItem(); - int checkAllItems(); - - int checkKmlLimit(double); - - bool okToContinue(); - - //initialization mutual exclusion - void selectWindInitialization( bool pick ); - void selectPointInitialization( bool pick ); - void selectWeatherInitialization( bool pick ); - void enablePointDate(bool enable); -#ifdef NINJAFOAM - void enableNinjafoamOptions(bool enable); -#endif - - void SetConfigOption(); - - protected: - void closeEvent(QCloseEvent *event); - - private: - QAction *openInputFileAction; - QAction *exitAction; - QAction *editPromptAction; - QAction *writeConsoleOutputAction; - QAction *rddsAction; - QAction *writeBlankStationFileAction; - QAction *setConfigAction; - QAction *rddsInstructAction; - QAction *resampleAction; - QAction *windNinjaHelpAction; - QAction *tutorial1Action, *tutorial2Action; - QAction *tutorial3Action, *tutorial4Action; - QAction *downloadDemAction; - QAction *fetchDemAction; - QAction *displayShapeFileViewAction; - QAction *displayShapeFileProAction; - QAction *cliInstructionsAction; - QAction *aboutWindNinjaAction; - QAction *aboutQtAction; - QAction *citeWindNinjaAction; - QAction *supportEmailAction; - QAction *submitBugReportAction; - - //test action connected to test slot - QAction *testAction; - - //Menus for the interface main window - QMenu *fileMenu; - QMenu *optionsMenu; - QMenu *toolsMenu; - QMenu *helpMenu; - QMenu *tutorialSubMenu; - QMenu *shapeSubMenu; - - QLabel *statusLabel; - - QTime *runTime; - - double elapsedRunTime; - - //create various entities in there own functions. - - void createMenus(); - void createActions(); - void createTimers(); - void createConnections(); - void createConsole(); - void createTreeView(); - void readSettings(); - void writeSettings(); - QString checkForNoData( QString fileName ); - - QVBoxLayout *mainLayout; - - QFileSystemWatcher fileWatcher; - WidgetDownloadDEM *demWidget; -}; - -#endif /* MAINWINDOW_H */ - diff --git a/src/gui/mainwindow.ui b/src/gui/mainwindow.ui deleted file mode 100644 index e623b385a..000000000 --- a/src/gui/mainwindow.ui +++ /dev/null @@ -1,583 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 530 - 846 - - - - MainWindow - - - - - - - - 0 - 0 - - - - - WindNinja - - - - - Input - - - - Surface - - - - - Location - - - - - Diurnal - - - - - Wind Input - - - - Domain Average Initialization - - - - - Point Initialization - - - - - Weather Model Initialization - - - - - - - Output - - - - Google Earth - - - - - Fire Behavior - - - - - Shape Files - - - - - VTK - - - - - - Solve - - - - - - - - Input - - - - - - - - 0 - 0 - - - - 5 - - - - - - - - - - 0 - 0 - - - - Longitude - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - 0 - - - - Latitude - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Use Diurnal Model - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - - - - - - - Read Station File - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - Download Forecast - - - - - - - - - - 1 - - - - - - - - - - - - - - - Elevation File - - - - - - - - 0 - 0 - - - - - - - - Open Elevation File - - - Qt::ToolButtonTextBesideIcon - - - - - - - - - - - Vegetation - - - - - - - - Grass - - - - - Brush - - - - - Trees - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Mesh Resolution - - - - - - - - Coarse - - - - - Medium - - - - - Fine - - - - - Custom - - - - - - - - true - - - 5000.000000000000000 - - - 100.000000000000000 - - - - - - - Meters - - - true - - - - - - - Feet - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - 0 - 0 - 530 - 25 - - - - - File - - - - - - - - - Options - - - - - - Tools - - - - - - - Help - - - - - - - - - - 8 - - - - - - - - 0 - 0 - - - - - - - - - - toolBar - - - TopToolBarArea - - - false - - - - - - Open &Elevation File - - - - - Write &Console Output to File - - - - - Exit - - - - - Console Output - - - - - Open RDDS Website - - - - - Write Blank Station File - - - - - - Marble::LatLonEdit - QWidget -
LatLonEdit.h
-
-
- - -
diff --git a/src/gui/metaWindWidget.cpp b/src/gui/metaWindWidget.cpp deleted file mode 100644 index 015bbf8f5..000000000 --- a/src/gui/metaWindWidget.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Input for global wind input variables - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "metaWindWidget.h" - -metaWindWidget::metaWindWidget(QWidget *parent) : QWidget(parent) -{ - metaWindGroupBox = new QGroupBox(tr("Options")); - - //input Height widgets - inputHeightGroupBox = new QGroupBox(tr("Input Wind Height")); - - inputHeightComboBox = new QComboBox; - inputHeightComboBox->addItem(tr("20ft-US")); - inputHeightComboBox->addItem(tr("10m-SI")); - inputHeightComboBox->addItem(tr("Custom")); - - inputHeightDoubleSpinBox = new QDoubleSpinBox; - inputHeightDoubleSpinBox->setRange(0, 100000); //Increased from 100 - inputHeightDoubleSpinBox->setValue(20.00); - inputHeightDoubleSpinBox->setAccelerated(true); - inputHeightDoubleSpinBox->setDisabled(true); - - feetRadioButton = new QRadioButton(tr("Feet")); - feetRadioButton->setChecked(true); - feetRadioButton->setDisabled(true); - meterRadioButton = new QRadioButton(tr("Meters")); - meterRadioButton->setDisabled(true); - - inputHeightLayout = new QHBoxLayout; - inputHeightLayout->addWidget(inputHeightComboBox); - inputHeightLayout->addWidget(inputHeightDoubleSpinBox); - inputHeightLayout->addWidget(feetRadioButton); - inputHeightLayout->addWidget(meterRadioButton); - inputHeightLayout->addStretch(); - - inputHeightGroupBox->setLayout(inputHeightLayout); - - connect(inputHeightComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(checkInputHeight(int))); - - layout = new QVBoxLayout; - layout->addWidget(inputHeightGroupBox); - - setLayout(layout); -} - -void metaWindWidget::checkInputHeight(int choice) -{ - if(choice == 0) - { - inputHeightDoubleSpinBox->setValue(20.00); - inputHeightDoubleSpinBox->setDisabled(true); - feetRadioButton->setChecked(true); - feetRadioButton->setDisabled(true); - meterRadioButton->setDisabled(true); - } - else if(choice == 1) - { - inputHeightDoubleSpinBox->setValue(10.00); - inputHeightDoubleSpinBox->setDisabled(true); - meterRadioButton->setChecked(true); - feetRadioButton->setDisabled(true); - meterRadioButton->setDisabled(true); - } - else if(choice == 2) - { - inputHeightDoubleSpinBox->setEnabled(true); - feetRadioButton->setEnabled(true); - feetRadioButton->setChecked(true); - inputHeightDoubleSpinBox->setValue(0.00); - meterRadioButton->setEnabled(true); - } -} diff --git a/src/gui/metaWindWidget.h b/src/gui/metaWindWidget.h deleted file mode 100644 index 755546190..000000000 --- a/src/gui/metaWindWidget.h +++ /dev/null @@ -1,79 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Input for global wind input variables - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 METAWINDWIDGET_H -#define METAWINDWIDGET_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "gdal_priv.h" -#include "ogr_srs_api.h" - -#include "latLonWidget.h" - -#ifndef Q_MOC_RUN -#include "ninja.h" -#endif - -class metaWindWidget : public QWidget -{ - Q_OBJECT - - public: - - metaWindWidget(QWidget *parent = 0); - - QGroupBox *metaWindGroupBox; - //input height - QGroupBox *inputHeightGroupBox; - QComboBox *inputHeightComboBox; - QDoubleSpinBox *inputHeightDoubleSpinBox; - QRadioButton *feetRadioButton, *meterRadioButton; - - //layouts - QHBoxLayout *inputHeightLayout; - - QVBoxLayout *layout; - - public slots: - void checkInputHeight(int choice); - -}; - -#endif /* METAWINDWIDGET_H */ diff --git a/src/gui/move_icon.png b/src/gui/move_icon.png deleted file mode 100644 index bcb510f07..000000000 Binary files a/src/gui/move_icon.png and /dev/null differ diff --git a/src/gui/nativeSolverInput.cpp b/src/gui/nativeSolverInput.cpp deleted file mode 100644 index 3a415b0d6..000000000 --- a/src/gui/nativeSolverInput.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************************** - * - * $Id: stabilityInput.cpp 1304 2012-01-20 21:07:12Z kyle.shannon $ - * - * Project: WindNinja Qt GUI - * Purpose: native solver interface - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "nativeSolverInput.h" - -/** - * Construct and layout the nativeSolverInput widget. This is only a checkable - * option now. - * - * @param parent parent widget - */ -nativeSolverInput::nativeSolverInput(QWidget *parent) : QWidget(parent) -{ - nativeSolverGroupBox = new QGroupBox(tr("Conservation of Mass")); - nativeSolverGroupBox->setCheckable(true); - nativeSolverGroupBox->setChecked(true); - - nativeSolverLabel = new QLabel(tr("This is the native WindNinja solver. It solves a conservation of mass equation,\n" - "but not a conservation of momentum equation. This solver is fast-running, \n" - "but may give less accurate wind predictions in regions where momentum effects are\n" - "important, for example on the lee side of terrain obstacles.\n" - ), this); - - nativeSolverLayout = new QVBoxLayout; - - nativeSolverGroupBox->setLayout(nativeSolverLayout); - - layout = new QVBoxLayout; - layout->addWidget(nativeSolverGroupBox); - layout->addWidget(nativeSolverLabel); - layout->addStretch(); - setLayout(layout); - - -} diff --git a/src/gui/nativeSolverInput.h b/src/gui/nativeSolverInput.h deleted file mode 100644 index 2b117443c..000000000 --- a/src/gui/nativeSolverInput.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef NATIVESOLVERINPUT_H -#define NATIVESOLVERINPUT_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "gdal_priv.h" -#include "ogr_srs_api.h" - -#ifndef Q_MOC_RUN -#include "boost/date_time/local_time/local_time.hpp" -#include "boost/date_time/posix_time/posix_time_types.hpp" -#endif - -#include "latLonWidget.h" -#include "timeZoneWidget.h" - -#include "qdebug.h" - -class nativeSolverInput : public QWidget -{ - Q_OBJECT - - public: - - nativeSolverInput(QWidget *parent = 0); - QGroupBox *nativeSolverGroupBox; - QVBoxLayout *nativeSolverLayout; - QVBoxLayout *layout; - QLabel *nativeSolverLabel; - - -}; - -#endif /* NATIVESOLVERINPUT_H */ diff --git a/src/gui/ninjafoamInput.cpp b/src/gui/ninjafoamInput.cpp deleted file mode 100644 index beae42503..000000000 --- a/src/gui/ninjafoamInput.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/****************************************************************************** - * - * $Id: stabilityInput.cpp 1304 2012-01-20 21:07:12Z kyle.shannon $ - * - * Project: WindNinja Qt GUI - * Purpose: NinjaFOAM interface - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "ninjafoamInput.h" - -/** - * Construct and layout the ninjafoamInput widget. This is only a checkable - * option now. - * - * @param parent parent widget - */ -ninjafoamInput::ninjafoamInput(QWidget *parent) : QWidget(parent) -{ - ninjafoamGroupBox = new QGroupBox(tr("Conservation of Mass and Momentum")); - ninjafoamGroupBox->setCheckable(true); - ninjafoamGroupBox->setChecked(false); - - ninjafoamLabel = new QLabel(tr("This solver conserves both mass and momentum. It is based on the OpenFOAM\n" - "CFD toolbox. This solver should give more accurate wind predictions in regions where\n" - "momentum effects are important, such as on the lee side of terrain obstacles. Because\n" - "this solver is more computationally intensive than the conservation of mass solver,\n" - "simulation times will be longer. Typical simulation times for this solver range from\n" - "10-30 min, but will depend on your domain, resolution, and computational\n" - "resources. Note that some options (e.g., point initialization and non-neutral\n" - "stability) are not available for this solver at this time. We plan to make these options\n" - "available in future releases." - ), this); - - ninjafoamLayout = new QVBoxLayout; - - ninjafoamGroupBox->setLayout(ninjafoamLayout); - - layout = new QVBoxLayout; - layout->addWidget(ninjafoamGroupBox); - layout->addWidget(ninjafoamLabel); - layout->addStretch(); - setLayout(layout); - - -} diff --git a/src/gui/ninjafoamInput.h b/src/gui/ninjafoamInput.h deleted file mode 100644 index 2beee89a9..000000000 --- a/src/gui/ninjafoamInput.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef NINJAFOAMINPUT_H -#define NINJAFOAMINPUT_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "gdal_priv.h" -#include "ogr_srs_api.h" - -#ifndef Q_MOC_RUN -#include "boost/date_time/local_time/local_time.hpp" -#include "boost/date_time/posix_time/posix_time_types.hpp" -#endif - -#include "latLonWidget.h" -#include "timeZoneWidget.h" - -#include "qdebug.h" - -class ninjafoamInput : public QWidget -{ - Q_OBJECT - - public: - - ninjafoamInput(QWidget *parent = 0); - QGroupBox *ninjafoamGroupBox; - QVBoxLayout *ninjafoamLayout; - QVBoxLayout *layout; - QLabel *ninjafoamLabel; - -}; - -#endif /* NINJAFOAMINPUT_H */ diff --git a/src/gui/outputHeightWidget.cpp b/src/gui/outputHeightWidget.cpp deleted file mode 100644 index b8bc9350c..000000000 --- a/src/gui/outputHeightWidget.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Output wind option widget - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "outputHeightWidget.h" - -outputHeightWidget::outputHeightWidget(QWidget *parent) : QWidget(parent) -{ - //create output height sections for tree/gui - outputHeightGroupBox = new QGroupBox(tr("Output Height")); - - outputHeightComboBox = new QComboBox; - outputHeightComboBox->addItem(tr("20ft-US")); - outputHeightComboBox->addItem(tr("10m-SI")); - outputHeightComboBox->addItem(tr("Custom")); - - outputHeightDoubleSpinBox = new QDoubleSpinBox; - outputHeightDoubleSpinBox->setRange(0, 10000); //Increased from 100 - outputHeightDoubleSpinBox->setValue(20.00); - outputHeightDoubleSpinBox->setAccelerated(true); - outputHeightDoubleSpinBox->setDisabled(true); - - feetRadioButton = new QRadioButton(tr("Feet")); - feetRadioButton->setChecked(true); - - meterRadioButton = new QRadioButton(tr("Meters")); - - layout = new QHBoxLayout; - layout->addWidget(outputHeightComboBox); - layout->addWidget(outputHeightDoubleSpinBox); - layout->addWidget(feetRadioButton); - layout->addWidget(meterRadioButton); - layout->addStretch(); - - outputHeightGroupBox->setLayout(layout); - - mainLayout = new QVBoxLayout; - mainLayout->addWidget(outputHeightGroupBox); - - setLayout(mainLayout); - - //signal/slot connection - connect(outputHeightComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(checkOutputHeight(int))); -} - -void outputHeightWidget::checkOutputHeight(int choice) -{ - if(choice == 0) - { - outputHeightDoubleSpinBox->setValue(20.00); - outputHeightDoubleSpinBox->setDisabled(true); - feetRadioButton->setChecked(true); - feetRadioButton->setDisabled(true); - meterRadioButton->setDisabled(true); - } - else if(choice == 1) - { - outputHeightDoubleSpinBox->setValue(10.00); - outputHeightDoubleSpinBox->setDisabled(true); - meterRadioButton->setChecked(true); - feetRadioButton->setDisabled(true); - meterRadioButton->setDisabled(true); - } - else if(choice == 2) - { - outputHeightDoubleSpinBox->setEnabled(true); - feetRadioButton->setEnabled(true); - feetRadioButton->setChecked(true); - outputHeightDoubleSpinBox->setValue(0.00); - meterRadioButton->setEnabled(true); - } -} diff --git a/src/gui/outputMetaData.cpp b/src/gui/outputMetaData.cpp deleted file mode 100644 index b506ddb53..000000000 --- a/src/gui/outputMetaData.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja - * Purpose: Meta output information - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "outputMetaData.h" - -/** - * Constructor for outputMetaData, sets layouts and attributes - * - * @param parent parent widget - */ -outputMetaData::outputMetaData( QWidget *parent ) : QWidget( parent ) -{ - - outputHeight = new outputHeightWidget( this ); - - bufferLabel = new QLabel( tr( "Clip output by: " ), this ); - bufferSpinBox = new QSpinBox( this ); - bufferSpinBox->setRange( 0, 49 ); - bufferSpinBox->setSingleStep( 5 ); - bufferSpinBox->setSuffix( "%" ); - - bufferLayout = new QHBoxLayout(); - bufferLayout->addWidget( bufferLabel ); - bufferLayout->addWidget( bufferSpinBox ); - bufferLayout->addStretch(); - - outputSpeedUnitsLabel = new QLabel( "Output Speed Units:", this ); - - outputSpeedUnitsCombo = new QComboBox( this ); - outputSpeedUnitsCombo->addItem( "mph" ); - outputSpeedUnitsCombo->addItem( "m/s" ); - outputSpeedUnitsCombo->addItem( "kph" ); - outputSpeedUnitsCombo->addItem( "kts" ); - - outputSpeedUnitsLayout = new QHBoxLayout(); - outputSpeedUnitsLayout->addWidget( outputSpeedUnitsLabel ); - outputSpeedUnitsLayout->addWidget( outputSpeedUnitsCombo ); - outputSpeedUnitsLayout->addStretch(); - - wxModelOutputCheckBox = new QCheckBox( tr( "Write Raw Weather Model Output" ), - this ); - wxModelOutputCheckBox->setDisabled( true ); - wxModelOutputCheckBox->setChecked( true ); - - layout = new QVBoxLayout(); - layout->addWidget( outputHeight ); - layout->addLayout( outputSpeedUnitsLayout ); - layout->addLayout( bufferLayout ); - layout->addWidget( wxModelOutputCheckBox ); - layout->addStretch(); - setLayout( layout ); -} - -outputMetaData::~outputMetaData() -{ - -} - - - - diff --git a/src/gui/outputMetaData.h b/src/gui/outputMetaData.h deleted file mode 100644 index a31a260e0..000000000 --- a/src/gui/outputMetaData.h +++ /dev/null @@ -1,64 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja - * Purpose: Meta output information - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 OUTPUT_META_DATA_H -#define OUTPUT_META_DATA_H - -#include -#include -#include -#include -#include - -#include "outputHeightWidget.h" - -class outputMetaData : public QWidget -{ - Q_OBJECT - - public: - outputMetaData( QWidget *parent = 0 ); - ~outputMetaData(); - - outputHeightWidget *outputHeight; /**< Output wind Heigth Widget */ - - QLabel *bufferLabel; /**< Describe the buffer */ - QSpinBox *bufferSpinBox; /**< Set buffer size in % */ - - QLabel *outputSpeedUnitsLabel; - QComboBox *outputSpeedUnitsCombo; - - QCheckBox *wxModelOutputCheckBox; /**< Write out the raw wx model data */ - - QHBoxLayout *outputSpeedUnitsLayout; - QHBoxLayout *bufferLayout; /**< layout for buffer info */ - QVBoxLayout *layout; /**< main layout */ -}; - -#endif /* OUTPUT_META_DATA_H */ diff --git a/src/gui/pdfOutput.cpp b/src/gui/pdfOutput.cpp deleted file mode 100644 index fcbe21140..000000000 --- a/src/gui/pdfOutput.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/****************************************************************************** - * - * Project: WindNinja Qt GUI - * Purpose: PDF output selection widgetx - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "pdfOutput.h" - -pdfOutput::pdfOutput(QWidget *parent) : QWidget(parent) -{ - pdfGroupBox = new QGroupBox(tr("Create Geospatial PDF Files (*.pdf)")); - pdfGroupBox->setCheckable(true); - pdfGroupBox->setChecked(false); - - vectorGroupBox = new QGroupBox(tr("Vectors")); - - vectorWidthLabel = new QLabel(tr("Line Width")); - - vectorWidthDoubleSpinBox = new QDoubleSpinBox; - vectorWidthDoubleSpinBox->setRange(1.0, 10.0); - vectorWidthDoubleSpinBox->setDecimals(1); - vectorWidthDoubleSpinBox->setValue(1.0); - vectorWidthDoubleSpinBox->setSingleStep(0.1); - vectorWidthDoubleSpinBox->setAccelerated(true); - - vectorLayout = new QHBoxLayout; - vectorLayout->addWidget(vectorWidthLabel); - vectorLayout->addWidget(vectorWidthDoubleSpinBox); - vectorLayout->addStretch(); - - vectorGroupBox->setLayout(vectorLayout); - - pdfResGroupBox = new QGroupBox(tr("Resolution")); - - pdfResSpinBox = new QDoubleSpinBox(this); - pdfResSpinBox->setRange(1, 5000); - pdfResSpinBox->setDecimals(2); - pdfResSpinBox->setAccelerated(true); - pdfResSpinBox->setValue(200); - pdfResSpinBox->setDisabled(true); - - pdfMetersRadioButton = new QRadioButton(tr("Meters")); - pdfMetersRadioButton->setChecked(true); - pdfMetersRadioButton->setDisabled(true); - pdfFeetRadioButton = new QRadioButton(tr("Feet")); - pdfFeetRadioButton->setDisabled(true); - - useMeshResCheckBox = new QCheckBox(tr("Use Mesh Resolution")); - useMeshResCheckBox->setChecked(true); - - backgroundLabel = new QLabel(tr("Basemap"), this); - - backgroundComboBox = new QComboBox(this); - backgroundComboBox->addItem(tr("TopoFire topo maps")); - backgroundComboBox->addItem(tr("Hillshade")); - - // Size names dictated by https://en.wikipedia.org/wiki/Paper_size - sizeLabel = new QLabel(tr("Size"), this); - sizeComboBox = new QComboBox(this); - sizeComboBox->addItem(tr("Letter-8 1/2 x 11")); - sizeComboBox->addItem(tr("Legal - 8 1/2 x 14")); - sizeComboBox->addItem(tr("Tabloid - 11 x 17")); - - portraitRadioButton = new QRadioButton(tr("Portrait"), this); - landscapeRadioButton = new QRadioButton(tr("Landscape"), this); - portraitRadioButton->setChecked(true); - - orientLayout = new QVBoxLayout; - orientLayout->addWidget(portraitRadioButton); - orientLayout->addWidget(landscapeRadioButton); - - sizeLayout = new QHBoxLayout; - sizeLayout->addWidget(sizeLabel); - sizeLayout->addWidget(sizeComboBox); - sizeLayout->addLayout(orientLayout); - - //connect checkbox with spin box - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - pdfResSpinBox, SLOT(setDisabled(bool))); - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - pdfFeetRadioButton, SLOT(setDisabled(bool))); - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - pdfMetersRadioButton, SLOT(setDisabled(bool))); - - backgroundLayout = new QHBoxLayout; - backgroundLayout->addWidget(backgroundLabel); - backgroundLayout->addWidget(backgroundComboBox); - backgroundLayout->addStretch(); - - resLayout = new QGridLayout; - resLayout->addWidget(pdfResSpinBox, 0, 0); - resLayout->addWidget(pdfMetersRadioButton, 0, 1); - resLayout->addWidget(pdfFeetRadioButton, 0, 2); - resLayout->addWidget(useMeshResCheckBox, 1, 0); - - pdfResGroupBox->setLayout(resLayout); - - pageLayout = new QVBoxLayout; - pageLayout->addWidget(vectorGroupBox); - pageLayout->addLayout(backgroundLayout); - pageLayout->addLayout(sizeLayout); - pageLayout->addWidget(pdfResGroupBox); - pageLayout->addStretch(); - - pdfGroupBox->setLayout(pageLayout); - - mainLayout = new QVBoxLayout; - mainLayout->addWidget(pdfGroupBox); - mainLayout->addStretch(); - - setLayout(mainLayout); -} diff --git a/src/gui/pdfOutput.h b/src/gui/pdfOutput.h deleted file mode 100644 index 1b72ad4a5..000000000 --- a/src/gui/pdfOutput.h +++ /dev/null @@ -1,74 +0,0 @@ -/****************************************************************************** - * - * Project: WindNinja Qt GUI - * Purpose: PDF output selection widgetx - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 PDFOUTPUT_H -#define PDFOUTPUT_H - -#include -#include -#include -#include -#include -#include - -#include -#include - -class pdfOutput : public QWidget -{ -Q_OBJECT - -public: - pdfOutput(QWidget *parent = 0); - - QGroupBox *pdfGroupBox; - QGroupBox *vectorGroupBox; - QLabel *vectorWidthLabel; - QDoubleSpinBox *vectorWidthDoubleSpinBox; - QGroupBox *pdfResGroupBox; - QDoubleSpinBox *pdfResSpinBox; - QRadioButton *pdfMetersRadioButton, *pdfFeetRadioButton; - QCheckBox *useMeshResCheckBox; - QLabel *backgroundLabel; - QComboBox *backgroundComboBox; - - QLabel *sizeLabel; - QComboBox *sizeComboBox; - QRadioButton *portraitRadioButton; - QRadioButton *landscapeRadioButton; - - QHBoxLayout *vectorLayout; - QVBoxLayout *optionLayout; - QGridLayout *resLayout; - QHBoxLayout *backgroundLayout; - QVBoxLayout *orientLayout; - QHBoxLayout *sizeLayout; - QVBoxLayout *pageLayout; - QVBoxLayout *mainLayout; -}; - -#endif /* PDFOUTPUT_H */ diff --git a/src/gui/pointInput.cpp b/src/gui/pointInput.cpp deleted file mode 100644 index 2e81d5955..000000000 --- a/src/gui/pointInput.cpp +++ /dev/null @@ -1,1051 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Point initialization input. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -/** - * @file pointInput.cpp - * @author Kyle Shannon - */ - -#include "pointInput.h" -static int wxStationFormat; - -pointInput::pointInput( QWidget *parent ) : QWidget( parent ) -{ - pointGroupBox = new QGroupBox( "Point Initialization", this ); - pointGroupBox->setCheckable( true ); - pointGroupBox->setChecked(false); - - pointGo=false; //Very Imptortant! - enableTimeseries=false; //bool for mainwindow - - newForm = new QWidget(); - -//#################################################### -// Some General Buttons # -//#################################################### - - //dateTimeEdit is the way of setting the simulation time for old format runs - dateTimeEdit = new QDateTimeEdit( newForm ); - dateTimeEdit->setDateTime( QDateTime::currentDateTime() ); - dateTimeEdit->setCalendarPopup( true ); - dateTimeEdit->setDisplayFormat( "MM/dd/yyyy HH:mm" ); - dateTimeEdit->setEnabled( false ); //This is for Old Format Diurnal Simulations - dateTimeEdit->setVisible(false); - dateTimeEdit->setToolTip("Set date and time for single time step diurnal/stability simulations"); - - diurnalLabel = new QLabel(this); //A Label for dateTimeEdit - diurnalLabel->setText("Set Simulation Time: "); - diurnalLabel->setVisible(false); - - oneStepTimeLabel = new QLabel(this); //Label for 1 step datetime runs - oneStepTimeLabel->setText("Simulation time set to:"); - oneStepTimeLabel->setVisible(false); - - writeStationFileButton = new QCheckBox( this ); //This writes an interpolated csv of the weather data (we might not want this) - writeStationFileButton->setText( tr( "Write Station File" ) ); - writeStationFileButton->setIcon( QIcon( ":weather_clouds.png" ) ); - writeStationFileButton->setToolTip("Time Series: Writes an Interpolated CSV for each time step\nSingle Step: Writes a CSV of inputted weather data."); - - writeStationKmlButton = new QCheckBox( this ); //This writes a KML of the weather stations (1 per run) - writeStationKmlButton->setText( tr( "Write Station Kml" ) ); - writeStationKmlButton->setIcon( QIcon( ":weather_cloudy.png" ) ); - writeStationKmlButton->setToolTip("Time Series: Writes a KML for each time step with time interpolated station data.\nSingle Step: Writes a KML of weather station data."); - - widgetButton = new QToolButton( this ); //This opens the station fetch downloader Widget (formerly doTest) - widgetButton->setText( tr( "Download data" )); - widgetButton->setIcon(QIcon(":server_go.png")); - widgetButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - widgetButton->setToolTip("Download weather station data from the Mesonet API."); - -//#################################################### -// The Main Box and directory stuff # -//#################################################### - - sfModel = new QDirModel(this); //Creates the directory model - sfModel->setReadOnly(true); //probably can be true, but i don't know - sfModel->setSorting(QDir::Time); //Sort by time created - - treeView = new QTreeView(this); //Creates the box where the sfModel goes - treeView->setVisible(true); - treeView->setModel(sfModel); //Sets the model to the thing above - treeView->header()->setStretchLastSection(true); //Makes it look nice - treeView->setAnimated(true); //Fancy stuff - treeView->setColumnHidden(1, true); - treeView->setColumnHidden(2, true); - treeView->setAlternatingRowColors( false ); - treeView->setSelectionMode(QAbstractItemView::MultiSelection); //Allows multiple files to be selected - treeView->setSelectionBehavior(QAbstractItemView::SelectRows); //Select entire row when we do select something - - treeLabel = new QLabel(tr("Select Weather Stations")); //Label for Tree and sfModel - treeLabel->setToolTip("Select Weather Stations from available files.\n" - "Click a file to add it to the list of included stations\n" - "Click it again to remove it from the included stations.\n" - "Available formats are time series, one step runs with time data,\n" - "and one step runs without time data (old format)"); - -//#################################################### -// Tool buttons and other things # -//#################################################### - ClippyToolLayout = new QHBoxLayout; //Layout for info and toolbox - - refreshToolButton = new QToolButton(this); //This refreshes the tree so that new files will populate - refreshToolButton->setText(tr("Refresh Weather Stations")); - refreshToolButton->setIcon(QIcon(":arrow_rotate_clockwise.png")); - refreshToolButton->setToolTip(tr("Refresh Stations stored on disk in the DEM directory.")); - refreshToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - refreshToolButton->setVisible(true); - - clippit = new QLabel(tr("")); //This gets updated with important information about the simulation as station files are chosen - clippit->setVisible(false); - - timeLine = new QFrame(this); //Adds a horizontal line - timeLine->setFrameShape(QFrame::HLine); - timeLine->setFrameShadow(QFrame::Sunken); - - timeLine2 = new QFrame(this); //Adds a horizontal line - timeLine2->setFrameShape(QFrame::HLine); - timeLine2->setFrameShadow(QFrame::Sunken); - - ClippyToolLayout->addStretch(); //Moves it over to the other side - ClippyToolLayout->addWidget(refreshToolButton); -// ClippyToolLayout->addWidget(clippit); - -//#################################################### -//Stuff for timeseries runs... # -//#################################################### - startTime = new QDateTimeEdit(QDateTime::currentDateTime()); //Initializes the start and stop time boxes for timeseries - stopTime = new QDateTimeEdit(QDateTime::currentDateTime()); - - startTime->setDateTime(QDateTime::currentDateTime().addDays(-1)); //Set default to be yesterday - startTime->setMaximumDateTime(QDateTime::currentDateTime().addSecs(-3600)); //Prevent time from overlapping with right now - stopTime->setMaximumDateTime(QDateTime::currentDateTime()); //No Future simulations - - startTime->setDisplayFormat( "MM/dd/yyyy HH:mm" ); - stopTime->setDisplayFormat( "MM/dd/yyyy HH:mm" ); - - startTime->setToolTip("Enter the simulation start time"); - stopTime->setToolTip("Enter the simulation stop time"); - - startTime->setCalendarPopup(true); - stopTime->setCalendarPopup(true); - - numSteps = new QSpinBox; //Number of timesteps box - numSteps->setValue(24); - numSteps->setMinimum(1); - numSteps->setMaximum(99999);//Hopefully big enough - numSteps->setToolTip("Enter the number of time steps"); - - startTime->setVisible(true); //Some visibility settings so that the default thing the user sees is a timeseries - stopTime->setVisible(true); - - startTime->setEnabled(false); //Disables timeseries options until the user does something - stopTime->setEnabled(false); - - numSteps->setVisible(true); //Same as above for time step options - numSteps->setEnabled(false); - - timeBoxLayout = new QHBoxLayout; //Layout setup - startLayout = new QVBoxLayout; - stopLayout = new QVBoxLayout; - stepLayout = new QVBoxLayout; - - startLabel = new QLabel(tr("Start Time")); //Labels for timeseries - stopLabel = new QLabel(tr("Stop Time")); - stepLabel = new QLabel(tr("Number of Time Steps")); - startLabel->setVisible(true); - stopLabel->setVisible(true); - stepLabel->setVisible(true); - - startLayout->addWidget(startLabel); //Labels are vertically stacked onto timeboxes and then horizontally set up - startLayout->addWidget(startTime); - - stopLayout->addWidget(stopLabel); - stopLayout->addWidget(stopTime); - - stepLayout->addWidget(stepLabel); - stepLayout->addWidget(numSteps); - - timeBoxLayout->addLayout(startLayout); //Add all the vertial labels to a horizontal grouping - timeBoxLayout->addLayout(stopLayout); - timeBoxLayout->addLayout(stepLayout); - -//------------------------------------------------- -// Add some new layouts -//------------------------------------------------- - vTreeLayout = new QVBoxLayout; //Adds everything to a vertical layout - - hDownloaderLayout = new QHBoxLayout; //Holds the label for the tree & download button - hDownloaderLayout->addWidget(treeLabel); //Adds label - hDownloaderLayout->addWidget(widgetButton); //adds Download Button to top of page - - vTreeLayout->addLayout(hDownloaderLayout); //adds hDownloaderLayout sublayout to the overal system - vTreeLayout->addWidget(treeView); //Add the big box - vTreeLayout->addLayout(ClippyToolLayout); //Add the refresh tool button - vTreeLayout->addWidget(timeLine); //Add a line to space it out - vTreeLayout->addLayout(timeBoxLayout); //Add the timeseries stuff -//#################################################### -//add in old format and other layout options # -//#################################################### - - buttonLayout = new QHBoxLayout; - buttonLayout->addWidget( writeStationFileButton ); - writeStationFileButton->setVisible( false ); //This was disabled in the original PI, leave it in for now, but the is now controlled by a config_option - buttonLayout->addWidget( writeStationKmlButton ); - buttonLayout->addStretch(); - - diurnalTimeLayout = new QHBoxLayout; //Create a layout for the old format diurnal options - diurnalTimeLayout->addWidget(diurnalLabel); //Add all the parts - diurnalTimeLayout->addWidget(dateTimeEdit,1); - diurnalTimeLayout->addWidget(oneStepTimeLabel); -// diurnalTimeLayout->addStretch(-1); - - pointLayout = new QVBoxLayout; - - pointLayout->addLayout(vTreeLayout); //add the above layout to the main window layouyt - - pointLayout->addLayout(diurnalTimeLayout); - - pointLayout->addStretch(); - pointLayout->addWidget(timeLine2); //Add another line - pointLayout->addLayout( buttonLayout ); //Add the kml button at the bottom - - pointGroupBox->setLayout( pointLayout ); //Set the layout to all of this - - ninjafoamConflictLabel = new QLabel(tr("The point initialization option is not currently available for the momentum solver.\n"), - this); - ninjafoamConflictLabel->setHidden(true); - - layout = new QVBoxLayout; - layout->addWidget( pointGroupBox ); - layout->addWidget(ninjafoamConflictLabel); - setLayout( layout ); //done - - checkForModelData();//On load check for model data to filter out directories and stuff we don't want - -//#################################################### -// Connect Signls to Slots # -//#################################################### - connect( widgetButton ,SIGNAL( clicked () ), this, - SLOT(openStationFetchWidget())); //Opens the Downloader Connector - connect(refreshToolButton, SIGNAL(clicked()), //Refreshes new Format, deselects files - this, SLOT(checkForModelData())); - connect(startTime,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(updateStartTime(QDateTime))); //update time into a vector from the timeboxes - connect(stopTime,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(updateStopTime(QDateTime))); - connect(stopTime,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(watchStopTime()));//Watch the times to make sure the user can't do crazy things - connect(startTime,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(watchStartTime())); - connect(treeView->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection &,const QItemSelection &)), - this, SLOT(readStationFiles(const QItemSelection &,const QItemSelection &))); //Update the selected stations when a user clicks something - connect(numSteps,SIGNAL(valueChanged(int)),this,SLOT(setOneStepTimeseries())); //watch the number of steps incase it goes to 1 - stationFileName = ""; //Sets a default -} - -pointInput::~pointInput() -{ - -} - -/** - * @brief pointInput::readStationFiles - * Reads the files on disk that the user selects - * - * x is the previously selected data - * y is the new selected data - * - * These two are not used explicitly but are necessary to link up the - * model with this function, as they trigger the checking to occur - * - * How this function works: - * 1. get a list of the stations the user selects from the UI - * 2. get what file type it is from directStationTraffic - * a. this will also update the user on what the station type is - * 3. if all selected files are the same type, set the simulation type and tell mainwindow that they are good - * if they are not all the same type, warn the user and mainwindow and wait for the user to change something - * - * @param x - * @param y - */ -void pointInput::readStationFiles(const QItemSelection &x ,const QItemSelection &y) -{ - QModelIndexList idx = x.indexes(); //Don't need these, probably delete later - QModelIndexList idy = y.indexes(); - QModelIndexList idx0 = treeView->selectionModel()->selectedRows(); //Get the number of files selected - - std::vector selectedStations; //The good stations - std::vector finalTypes; //What type they are - CPLDebug("STATION_FETCH","========================================"); - CPLDebug("STATION_FETCH","NUMBER OF SELECTED STATIONS: %i",idx0.count()); - - for(int i=0;ifileInfo(idx0[i]).isDir()==true) //If its a directory, make it so that it can't be selected - { - CPLDebug("STATION_FETCH","IGNORING SELECTED DIRECTORY!"); - treeView->selectionModel()->select(idx0[i],QItemSelectionModel::Deselect | QItemSelectionModel::Rows);//Deselct entire row by calling ::Rows otherwise it looks messy - } - else //if its not a directory, add it to the list of available files - { - selectedStations.push_back(sfModel->fileInfo(idx0[i]).absoluteFilePath().toStdString()); - } - } - - for (int i=0;i setInt(finalTypes.begin(),finalTypes.end()); //This is a sanity check to see if the stations that we are reading are all the same type - std::vector vecInt(setInt.begin(),setInt.end()); - - CPLDebug("STATION_FETCH","Unique Data Types (bad if >1) %lu",vecInt.size()); - for(int j=0;j1) //Set the simulation type to -1 -> user selects different file types - { - simType = -1; - displayInformation(-1); //Display some information to the console - } - if (vecInt.size()==1) //Set the sim type to whatever it is - { - simType = vecInt[0]; - displayInformation(vecInt[0]); - } - else //we probably failed to open it because its a bad file - { - simType = -1; - displayInformation(-1); - } -} - -/** - * @brief Figures out what format belongs to what file based on the presence of date time - * data and header information - * Turns options on and off for the gui based on what the file formats are, such as - * enables timeseries when there is a timeseries. Suggests time series data ranges. - * prevents timeseries when there is no way it is a timeseries - * * * - * Possible options to be returned from directStationTraffic: - * a. 0 : OLD FORMAT <-1 Step - * b. 1 : NEW FORMAT, Time Series <- Multiple Steps - * c. 2 : NEW FORMAT, NO Time Series <- 1 Step - * d. -1 : Something bad happened, not sure what at this point - * @param xFileName <- the file name of the wxStation to be read from disk. - * @return see above - */ -int pointInput::directStationTraffic(const char* xFileName) -{ - //Get the header version from a wxStation function that does this for us - int stationHeader = wxStation::GetHeaderVersion(xFileName); - CPLDebug("STATION_FETCH","HEADER TYPE: %i",stationHeader); - int instant = 0; - std::string idx3; - stringstream ssidx; - - if(stationHeader!=1) //Station header is different than what we return - { - //Determine whether it is a timeseries or not... - OGRDataSourceH hDS; - OGRLayer *poLayer; - OGRFeature *poFeature; - OGRFeatureDefn *poFeatureDefn; - OGRFieldDefn *poFieldDefn; - OGRLayerH hLayer; - - hDS = OGROpen( xFileName, FALSE, NULL ); - - if(hDS == NULL) - { - writeToConsole("Cannot open station file!"); - return -1; //very bad! - } - - poLayer = (OGRLayer*)OGR_DS_GetLayer( hDS, 0 ); - hLayer=OGR_DS_GetLayer(hDS,0); - OGR_L_ResetReading(hLayer); - poLayer->ResetReading(); - - GIntBig iBig = 1; - GIntBig idx0 = poLayer->GetFeatureCount(); - GIntBig idx1 = idx0-iBig; - GIntBig idx2; - - idx2 = poLayer->GetFeatureCount(); - - CPLDebug("STATION_FETCH","Number of Time Entries: %llu",idx2); //How many lines are on disk - QString qFileName = QFileInfo(xFileName).fileName(); - writeToConsole(QString(qFileName+" has: "+QString::number(idx2)+" time entries")); //tell the user in the console - const char* emptyChair; //Muy Importante! - - poFeature = poLayer->GetFeature(iBig); - if (poFeature==NULL) - { - writeToConsole("No Stations Found in file!"); - return -1; //If there are no stations in the csv! - } - // startTime = poFeature->GetFieldAsString(15); - std::string start_datetime(poFeature->GetFieldAsString(15)); - poFeature = poLayer->GetFeature(idx2); - std::string stop_datetime(poFeature->GetFieldAsString(15)); - - CPLDebug("STATION_FETCH","STATION START TIME: %s",start_datetime.c_str()); - CPLDebug("STATION_FETCH","STATION END TIME: %s",stop_datetime.c_str()); - - if (start_datetime.empty()==true && stop_datetime.empty()==true) - { - //Means that there is not a time series - CPLDebug("STATION_FETCH", "File cannot be used for Time Series"); - instant = 1; - } - if (start_datetime.empty()==false && stop_datetime.empty()==false) //Definately some sort of time series - { - CPLDebug("STATION_FETCH","File can be used for Times Series"); - CPLDebug("STATION_FETCH","Suggesting Potentially Reasonable Time Series Parameters..."); - - ///// check whether the times are valid HERE, while we still have access to file index, last selected file information, and error type information - QString q_time_format = "yyyy-MM-ddTHH:mm:ssZ"; - if( start_datetime.length() < q_time_format.toStdString().length() ) - { - QMessageBox::critical(this, tr("Failure."), "station start_time "+QString(start_datetime.c_str())+" is invalid length!", QMessageBox::Ok | QMessageBox::Default); - // need to deselect the last selected station file as well - deselectStationFile(xFileName); - return -1; - } - if( stop_datetime.length() < q_time_format.toStdString().length() ) - { - QMessageBox::critical(this, tr("Failure."), "station stop_time "+QString(stop_datetime.c_str())+" is invalid length!", QMessageBox::Ok | QMessageBox::Default); - // need to deselect the last selected station file as well - deselectStationFile(xFileName); - return -1; - } - if( start_datetime[4] != '-' || start_datetime[7] != '-' || start_datetime[10] != 'T' || start_datetime[13] != ':' || start_datetime[16] != ':' || start_datetime[19] != 'Z') - { - QMessageBox::critical(this, tr("Failure."), "station start_time "+QString(start_datetime.c_str())+" has invalid format!", QMessageBox::Ok | QMessageBox::Default); - // need to deselect the last selected station file as well - deselectStationFile(xFileName); - return -1; - } - if( stop_datetime[4] != '-' || stop_datetime[7] != '-' || stop_datetime[10] != 'T' || stop_datetime[13] != ':' || stop_datetime[16] != ':' || stop_datetime[19] != 'Z') - { - QMessageBox::critical(this, tr("Failure."), "station stop_time "+QString(stop_datetime.c_str())+" has invalid format!", QMessageBox::Ok | QMessageBox::Default); - // need to deselect the last selected station file as well - deselectStationFile(xFileName); - return -1; - } - - readStationTime(start_datetime,stop_datetime,idx2); //Turns the Start and Stop times into local timess...... - ssidx<setEnabled(false); //Hide these things because its an old format run - stopTime->setEnabled(false); - numSteps->setEnabled(false); - - //Try flipping the UI - startLabel->setVisible(false); //Hide all the timesries stuff - stopLabel->setVisible(false); - stepLabel->setVisible(false); - - startTime->setVisible(false); - stopTime->setVisible(false); - numSteps->setVisible(false); - - dateTimeEdit->setVisible(true); //show the date time box to set the sim time - diurnalLabel->setVisible(true); - oneStepTimeLabel->setVisible(false); // hide the 1 step time box thing for instant==1 - - //Update Diurnal Time - if (isDiurnalChecked==true) - { - dateTimeEdit->setEnabled(true); //enable the diurnal Box - updateSingleTime(dateTimeEdit->dateTime()); //set the time from the diurnal box - } - - - return 0; - } - if (stationHeader == 2 && instant == 0) - { - //new Foramt w/ Time Series - //Turn on Several options for controlling time series in the GUI - enableTimeseries=true; - startTime->setEnabled(true); - stopTime->setEnabled(true); - numSteps->setEnabled(true); - updateStartTime(startTime->dateTime()); - updateStopTime(stopTime->dateTime()); - - //try flipping the UI - startLabel->setVisible(true); - stopLabel->setVisible(true); - stepLabel->setVisible(true); - - startTime->setVisible(true); - stopTime->setVisible(true); - numSteps->setVisible(true); - - dateTimeEdit->setVisible(false); //hide the single step stuff - diurnalLabel->setVisible(false); - oneStepTimeLabel->setVisible(false); - return 1; - } - if (stationHeader == 2 && instant == 1) - { - //new Format no Time Series - //Prevent users from using time series with 1 step - enableTimeseries=false; - startTime->setEnabled(false); - stopTime->setEnabled(false); - numSteps->setEnabled(false); - - //Try flipping the UI - startLabel->setVisible(false); - stopLabel->setVisible(false); - stepLabel->setVisible(false); - - startTime->setVisible(false); - stopTime->setVisible(false); - numSteps->setVisible(false); - - dateTimeEdit->setVisible(false); - diurnalLabel->setVisible(false); - dateTimeEdit->setEnabled(false); - - const char *optChangeTime = CPLGetConfigOption("CHANGE_DATE_TIME","FALSE"); //Allow the user the change the datetime via config option - if(optChangeTime!="FALSE") - { - QString time_format = "yyyy-MM-ddTHH:mm:ss"; - QString optXTime = QString::fromAscii(optChangeTime); - QDateTime opt_time_obj = QDateTime::fromString(optXTime,time_format); - updateSingleTime(opt_time_obj); - QString oneStepText = "Simulation time set to: "+optXTime; - oneStepTimeLabel->setText(oneStepText); - oneStepTimeLabel->setVisible(true); // to this label in the GUI - } - else - { - //Reads the sim time from the file the user provides; - QDateTime singleRunTime = readNinjaNowName(xFileName); //Read in the date time - updateSingleTime(singleRunTime); //update it globally - QString runTimeText = singleRunTime.toString(); //Print it out for the user - QString oneStepText = "Simulation time set to: "+runTimeText; - oneStepTimeLabel->setText(oneStepText); - oneStepTimeLabel->setVisible(true); // to this label in the GUI - } - return 2; - - } - if (stationHeader == 3) - { - //Invalid header type for GUI run... - return -1; - } - if (stationHeader == 4) - { - //Invalid header type for GUI run... - return -1; - } - else - { - //Something wrong with the station... - return -1; - } -} -/** - * @brief pointInput::readStationTime: Takes in times read from disk, turns them into - * Qt datetime objects, then updates the gui with suggested ranges of times based on available disk data - * tries to stop users from picking frivolous time ranges (maybe). Also suggests a number of timesteps based on - * available data. - * @param start_time - * @param stop_time - * @param xSteps - */ -void pointInput::readStationTime(string start_time, string stop_time, int xSteps) -{ - QString q_time_format = "yyyy-MM-ddTHH:mm:ssZ"; - - //// convert the input start and stop times into boost local date time objects, so convert from UTC time to local time - - blt::time_zone_ptr tz; // Initialize time zone - tz = globalTimeZoneDB.time_zone_from_region(tzString.toStdString()); // Get time zone from database - - // parse the start time into date and time parts - int start_year = atoi(start_time.substr(0, 4).c_str()); - int start_month = atoi(start_time.substr(5, 2).c_str()); - int start_day = atoi(start_time.substr(8, 2).c_str()); - int start_hour = atoi(start_time.substr(11, 2).c_str()); - int start_minute = atoi(start_time.substr(14, 2).c_str()); - int start_sec = atoi(start_time.substr(17, 2).c_str()); - - // parse the start time into date and time parts - int stop_year = atoi(stop_time.substr(0, 4).c_str()); - int stop_month = atoi(stop_time.substr(5, 2).c_str()); - int stop_day = atoi(stop_time.substr(8, 2).c_str()); - int stop_hour = atoi(stop_time.substr(11, 2).c_str()); - int stop_minute = atoi(stop_time.substr(14, 2).c_str()); - int stop_sec = atoi(stop_time.substr(17, 2).c_str()); - - // make intermediate start and stop dates for generating ptime objects - bg::date startTime_date(start_year,start_month,start_day); - bg::date stopTime_date(stop_year,stop_month,stop_day); - bpt::time_duration startTime_duration(start_hour,start_minute,start_sec,0); - bpt::time_duration stopTime_duration(stop_hour,stop_minute,stop_sec,0); - - // these are UTC times - bpt::ptime startTime_ptime(startTime_date,startTime_duration); - bpt::ptime stopTime_ptime(stopTime_date,stopTime_duration); - - // this constructor generates local times from UTC times - blt::local_date_time boost_local_startTime( startTime_ptime, tz ); - blt::local_date_time boost_local_stopTime( stopTime_ptime, tz ); - - - //// convert the boost local date time objects into QDateTime objects - - std::string os_time_format = "%Y-%m-%dT%H:%M:%SZ"; // this is the ostringstream format string that replicates the above QDateTime format string - - bpt::time_facet* facet; - facet = new bpt::time_facet(); - - facet->format(os_time_format.c_str()); - - - // calculate the start_time QDate - std::ostringstream os; - os.imbue(std::locale(std::locale::classic(), facet)); - //os << boost_local_startTime.utc_time(); - os << boost_local_startTime.local_time(); - QString loc_start_time_qStr = QString::fromStdString( os.str() ); - os.str(""); // reset for parsing the next time - os.clear(); - QDateTime loc_start_time = QDateTime::fromString(loc_start_time_qStr,q_time_format); - - // calculate the end_time QDate - os.imbue(std::locale(std::locale::classic(), facet)); - //os << boost_local_stopTime.utc_time(); - os << boost_local_stopTime.local_time(); - QString loc_end_time_qStr = QString::fromStdString( os.str() ); - os.str(""); // reset for parsing the next time - os.clear(); - QDateTime loc_end_time = QDateTime::fromString(loc_end_time_qStr,q_time_format); - - CPLDebug("STATION_FETCH","qdate start_local = %s",loc_start_time.toString(q_time_format).toStdString().c_str()); - CPLDebug("STATION_FETCH","qdate end_local = %s",loc_end_time.toString(q_time_format).toStdString().c_str()); - - - //// now use the final local time QDate start and stop times - writeToConsole("Start Time (local): "+loc_start_time.toString()); //Tell console what the - writeToConsole("Stop Time (local): "+loc_end_time.toString()); //Local time is - - startTime->setDateTime(loc_start_time); - stopTime->setDateTime(loc_end_time); //Updates date time based on disk information - - updateTimeSteps(); //Calculate how many steps we can do between the start and stop time -} -/** - * @brief pointInput::displayInformation - * Displays important stuff to the user based on what they select - * Warns them if they select 2 types of files that are incompatible. - * @param dataType - */ -void pointInput::displayInformation(int dataType) -{ - CPLDebug("STATION_FETCH","Data Format: %i",dataType); - if(dataType == 0) - { - clippit->setText("Run Type: Old Format"); - pointGo=true; - if (isDiurnalChecked==true) - { - dateTimeEdit->setEnabled(true); - } - if (stationFileList.size()>1) - { - clippit->setText("Too many stations selected for data type"); - } - } - if(dataType == 1) - { - clippit->setText("Run Type: Time Series"); - pointGo=true; - if (dateTimeEdit->isEnabled()) - { - dateTimeEdit->setEnabled(false); - } - } - if(dataType == 2) - { - clippit->setText("Run Type: Single Step"); - pointGo=true; - } - if (dataType == -1 && stationFileList.size()==0) //Special Case - { - clippit->setText("No Stations Selected..."); - pointGo=false; - } - if (dataType == -1 && stationFileList.size()==1) //Case of 1 file that is crap - { - clippit->setText("No Valid Data detected in file..."); - - pointGo=false; - } - if (dataType==-1 && stationFileList.size()>=2) - { - clippit->setText("MULTIPLE TYPES SELECTED, CANNOT PROCEED!"); - - pointGo=false; - } -// else if(dataType == -1) -// { -// clippit->setText("MULTIPLE TYPES SELECTED, CANNOT PROCEED!"); -// pointGo=false; -// } -} -/** - * @brief pointInput::readNinjaNowName - * @param fileName - * This is for current step new format runs - * we need time if the user turnsl on diurnal/stability input - * Read the Time from the date created attribute attached to the file - * - * @return - */ -QDateTime pointInput::readNinjaNowName(const char *fileName) -{ - CPLDebug("STATION_FETCH","Reading 1 step Station start Time"); - QDateTime qxDate = QFileInfo(fileName).created(); //Get when the file was created because that is when the simulation time is - return qxDate; -} -/** - * @brief pointInput::setOneStepTimeseries - * If one step is set, diable stop time - * and only use start time - */ -void pointInput::setOneStepTimeseries() -{ - if(numSteps->value()==1) - { - CPLDebug("STATION_FETCH","One Step Set for Timeseries, greying out stop time!"); - stopTime->setEnabled(false); - stopTime->setToolTip("Stop time is disabled for 1 time step simulations"); - } - else - { - stopTime->setEnabled(true); - stopTime->setToolTip("Enter the simulation stop time"); - } -} - - -/** - * @brief pointInput::setWxStationFormat - * sets the format of the wxStation - * not used right now - * @param format - */ -void pointInput::setWxStationFormat(int format) -{ - wxStationFormat = format; -} - -void pointInput::selChanged(const QItemSelection &x, const QItemSelection &y) //Generic test function, delete once everything is good -{ - CPLDebug("STATION_FETCH","TEST"); -} - -void pointInput::openMainWindow() //This is for opening and closing the station-fetch widget -{ - this->setEnabled(true); -} - -/** Allows mainwindow to update the timezone from the DEM or as provided by the user - * @brief pointInput::updateTz - * @param tz - */ - -void pointInput::updateTz(QString tz) -{ - tzString = tz; -} - -/** Pairs start and stop times to what the stationFetchWidget does. - * @brief pointInput::pairStartTime - * @param xDate - */ -void pointInput::pairStartTime(QDateTime xDate) //These functions take the start and stop time from the widget -//and populate the timeseries objects in the gUI -{ - startTime->setDateTime(xDate); -} -void pointInput::pairStopTime(QDateTime xDate) -{ - stopTime->setDateTime(xDate); -} -/** Groups all the different timseries parts to be enabled/disabled at the same time based on - * user options - * @brief pointInput::pairTimeSeries - * @param curIndex - */ -void pointInput::pairTimeSeries(int curIndex) -{ - if(curIndex==0) - { - enableTimeseries=true; - startTime->setEnabled(false); - stopTime->setEnabled(false); - numSteps->setEnabled(false); - } - if(curIndex==1) - { - enableTimeseries=true; - startTime->setEnabled(true); - stopTime->setEnabled(true); - numSteps->setEnabled(true); - } -} -/** - * @brief pointInput::watchStopTime - * corrects user selecting stop time behind start time! - */ -void pointInput::watchStopTime() //Stop time cannot be farther in the future than the stop time -{ - if(stopTime->dateTime()dateTime()) - { - writeToConsole("Start Time is greater than Stop Time!"); - CPLDebug("STATION_FETCH","START TIME > END TIME, FIXING START TIME!"); - startTime->setDateTime(stopTime->dateTime().addSecs(-3600)); - } -} -/** - * @brief pointInput::watchStartTime - * corrects the stop time if the user picks a start time farther in the future than the stop - * time - */ -void pointInput::watchStartTime() //Stop time cannot be farther in the future than the stop time -{ - if(stopTime->dateTime()dateTime()) - { - writeToConsole("Start Time is greater than Stop Time!"); - CPLDebug("STATION_FETCH","START TIME > END TIME, FIXING STOP TIME!"); - stopTime->setDateTime(startTime->dateTime().addSecs(3600)); - } -} -/** Updates the timeseries start time based on user requests - * @brief pointInput::updateStartTime - * @param xDate - */ -void pointInput::updateStartTime(QDateTime xDate) -{ - startSeries.clear(); //delete whatever is stored in the vector - int year,month,day,hour,minute; - year = xDate.date().year(); - month = xDate.date().month(); - day = xDate.date().day(); - hour = xDate.time().hour(); - minute = xDate.time().minute(); - - updateTimeSteps(); //when we change the time, update the math on the time steps - CPLDebug("STATION_FETCH","UPDATED START TIME: %i %i %i %i %i",year,month,day,hour,minute); - - startSeries.push_back(year); - startSeries.push_back(month); - startSeries.push_back(day); - startSeries.push_back(hour); - startSeries.push_back(minute); -} -/** Updates the time for stopping the simulation based on user requests - * @brief pointInput::updateStopTime - * @param xDate - */ -void pointInput::updateStopTime(QDateTime xDate) -{ - endSeries.clear(); //delete whatever is stored in the vector - int year,month,day,hour,minute; - year = xDate.date().year(); - month = xDate.date().month(); - day = xDate.date().day(); - hour = xDate.time().hour(); - minute = xDate.time().minute(); - - updateTimeSteps(); //when we change the time, update the math on the time steps - - CPLDebug("STATION_FETCH","UPDATED STOP TIME: %i %i %i %i %i",year,month,day,hour,minute); - - endSeries.push_back(year); - endSeries.push_back(month); - endSeries.push_back(day); - endSeries.push_back(hour); - endSeries.push_back(minute); -} -/** - * @brief pointInput::updateSingleTime - * @param xDate - * - * For single step runs, set the simulation time based on the read station file time, - * see also: - * readNinjaNowName() - * - */ -void pointInput::updateSingleTime(QDateTime xDate) -{ - int year,month,day,hour,minute; - year = xDate.date().year(); - month = xDate.date().month(); - day = xDate.date().day(); - hour = xDate.time().hour(); - minute = xDate.time().minute(); - - CPLDebug("STATION_FETCH","UPDATED SINGLE STEP TIME: %i %i %i %i %i",year,month,day,hour,minute); - - diurnalTimeVec.push_back(year); - diurnalTimeVec.push_back(month); - diurnalTimeVec.push_back(day); - diurnalTimeVec.push_back(hour); - diurnalTimeVec.push_back(minute); - -} -/** - * @brief pointInput::updateTimeSteps - * Suggest a number of time steps based on the - * start and stop time - * - * this function sets the number of time steps to the number of - * hours between the start and stop time - * - * if its less than 2 hours, - * suggest two steps - * - * the user can then change this if they really want - * - */ -void pointInput::updateTimeSteps() -{ - CPLDebug("STATION_FETCH","Updating Suggested Time steps...."); - int u_start = startTime->dateTime().toTime_t(); - int u_stop = stopTime->dateTime().toTime_t(); - - if ( u_start == u_stop ) { - - numSteps->setValue(1); - - } else { - - int u_diff = (u_stop - u_start)/(3600); //calculate the number of hours between the start and stop times - - if(u_diff<=2) //if its less than 2 hours, make it 2 - { - u_diff = 2; - } - - numSteps->setValue(u_diff); - - } -} -/** - * @brief pointInput::openStationFetchWidget - * Opens the downloader widget to download station files - */ -void pointInput::openStationFetchWidget() -{ - xWidget = new stationFetchWidget(); - QSignalMapper *signalMapper; - connect(xWidget, SIGNAL(exitWidget()),this, SLOT(openMainWindow())); //Launches Widget Connector - connect(xWidget, SIGNAL(destroyed()),this,SLOT(openMainWindow())); //Some sort of deconstructor thing - this->setEnabled(false); //disable the main window - xWidget->setInputFile(demFileName); //give the widget the dem file - xWidget->updatetz(tzString); //give the widget the time zone as a string - connect(xWidget, SIGNAL(exitWidget()),this, SLOT(checkForModelData())); //Launches Widget Connector - connect(xWidget->startEdit,SIGNAL(dateTimeChanged(const QDateTime)),this,SLOT(pairStartTime(const QDateTime))); //connect the various time boxes to eachother - connect(xWidget->endEdit,SIGNAL(dateTimeChanged(const QDateTime)),this,SLOT(pairStopTime(const QDateTime))); - connect(xWidget->timeLoc,SIGNAL(currentIndexChanged(int)),this,SLOT(pairTimeSeries(int))); //Connects What the user does in the widget - //to what the timeseries checkbox does -} -/** Allows mainwindow to update pointInput with changes to the DEM - * @brief pointInput::setInputFile - * @param file - */ -void pointInput::setInputFile( QString file ) -{ - demFileName = file; - cwd = QFileInfo(file).absolutePath(); -} -/** Allows mainWindow to update pointInput with changes in diurnal/stability options - * @brief pointInput::setDiurnalParam - * @param diurnalCheck - */ -void pointInput::setDiurnalParam(bool diurnalCheck) -{ - isDiurnalChecked = diurnalCheck;//Note that this works for stability too - CPLDebug("STATION_FETCH","DIURNAL/STABILITY STATUS: %i",isDiurnalChecked); -} - -void pointInput::deselectStationFile(const char* stationFileName) -{ - QModelIndex QFileIdx = sfModel->index(stationFileName); - if( QFileIdx.isValid() ) - { - treeView->selectionModel()->select(QFileIdx, QItemSelectionModel::Deselect | QItemSelectionModel::Rows); - } -} - -/** - * *@brief pointInput::checkForModelData - * Applies filters to the tree - * Also clears selection when the user clicks it - */ -void pointInput::checkForModelData() -{ - stationFileList.clear(); //Clear the list - treeView->selectionModel()->clear(); //Clear the models selections - pointGo = false; //Set the pointInput bool to false just to be extra explicit - QDir wd(cwd); - QStringList filters; - filters<<"*.csv"; //Only show CSV - filters<<"WXSTATIONS-*"; //Add downloadable directories to filters - sfModel->setNameFilters(filters); - sfModel->setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); //QDir::Dir specifies to add filters to directories - treeView->setRootIndex(sfModel->index(wd.absolutePath())); - treeView->resizeColumnToContents(0); -} diff --git a/src/gui/pointInput.h b/src/gui/pointInput.h deleted file mode 100644 index 7225b3e83..000000000 --- a/src/gui/pointInput.h +++ /dev/null @@ -1,189 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Point initialization input. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 POINT_INPUT_H -#define POINT_INPUT_H - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "latLonWidget.h" -#include "wxStation.h" -#include "ninjaUnits.h" -#include "stationFetchWidget.h" -#include - -#include -#include - -#include - -#include - -class pointInput : public QWidget -{ - Q_OBJECT - - public: - - pointInput( QWidget *parent = 0 ); - ~pointInput(); - - QString demFileName; - QString stationFileName; - std::vector stationFileList; - std::vector stationFileTypes; - int simType; - bool pointGo; - int isDiurnalChecked; - bool enableTimeseries; - - QHBoxLayout *diurnalTimeLayout; - QDateTimeEdit *dateTimeEdit; - QLabel *diurnalLabel; - QLabel *oneStepTimeLabel; - - QCheckBox *writeStationFileButton; - QCheckBox *writeStationKmlButton; - QToolButton *widgetButton; - - QGroupBox *pointGroupBox; - - QHBoxLayout *buttonLayout; - QVBoxLayout *pointLayout; - QVBoxLayout *layout; - QLabel *ninjafoamConflictLabel; - - //TreeBox Stuff - - QLabel *treeLabel; - - QTreeView *treeView; - QVBoxLayout *vTreeLayout; - QHBoxLayout *hDownloaderLayout; //Put the downloader up near the top of the page - QWidget *newForm; - - //Station Fetch Directory Stuff - - QDir cwd; - QDirModel *sfModel; - //Handlers - QHBoxLayout *ClippyToolLayout; - QToolButton *refreshToolButton; - QLabel *clippit; //displays vital information - - QFrame *timeLine; //Creates a fancy line to seprate things out - QFrame *timeLine2; //Creates another fancy line... - - std::vector vx; //For file names - - //endDirectoryChecking - //Time series stuff - QDateTimeEdit *startTime; - QDateTimeEdit *stopTime; - QHBoxLayout *timeBoxLayout; - QSpinBox *numSteps; - - QVBoxLayout *startLayout; - QVBoxLayout *stopLayout; - QVBoxLayout *stepLayout; - - QLabel *startLabel; - QLabel *stopLabel; - QLabel *stepLabel; - - std::vector startSeries; - std::vector endSeries; //Global Storage for start and stop times - - std::vector diurnalTimeVec; - - //End Timeseries stuff - - stationFetchWidget *xWidget; - QString tzString; - - void deselectStationFile(const char* stationFileName); - - public slots: - void updateTz(QString tz); - void checkForModelData(); - int directStationTraffic(const char* xFileName); - void readStationTime(std::string start_time, std::string stop_time, int xSteps); - static void setWxStationFormat(int format); //I don't Think I need this anymore (delete later) - void displayInformation(int dataType); - QDateTime readNinjaNowName(const char* fileName); - void setOneStepTimeseries(); - - private slots: - void readStationFiles(const QItemSelection &x ,const QItemSelection &y); - void selChanged(const QItemSelection &x ,const QItemSelection &y); //Test Function - void setInputFile( QString file ); - void setDiurnalParam(bool diurnalCheck); - void openMainWindow(); - void openStationFetchWidget(); - - void pairStartTime(QDateTime xDate); - void pairStopTime(QDateTime xDate); - void pairTimeSeries(int curIndex); - void updateTimeSteps(); - - void updateSingleTime(QDateTime xDate); - void updateStartTime(QDateTime xDate); - void updateStopTime(QDateTime xDate); - void watchStopTime(); - void watchStartTime(); - - signals: - void writeToConsole( QString message ); - void stationFileChanged(); - -friend class stationFetchWidget; -}; - -#endif /* POINT_INPUT_H */ diff --git a/src/gui/qt6/CMakeLists.txt b/src/gui/qt6/CMakeLists.txt new file mode 100755 index 000000000..0331b5f01 --- /dev/null +++ b/src/gui/qt6/CMakeLists.txt @@ -0,0 +1,81 @@ +cmake_minimum_required(VERSION 3.16) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets) +find_package(Qt6 REQUIRED COMPONENTS + Core + Widgets + WebEngineWidgets + WebChannel +) + +set(PROJECT_SOURCES + main.cpp + mainWindow.cpp + mainWindow.h + mainWindow.ui +) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_executable(WindNinja + MANUAL_FINALIZATION + ${PROJECT_SOURCES} + ${CMAKE_SOURCE_DIR}/wn-resources.qrc + appState.h + appState.cpp + splashScreen.h + splashScreen.cpp + surfaceInput.h + surfaceInput.cpp + setConfigurationDialogOption.h + setConfigurationDialogOption.cpp + setConfigurationDialogOption.ui + menuBar.h + menuBar.cpp + mapBridge.h + mapBridge.cpp + surfaceInput.h + surfaceInput.cpp + serverBridge.h + serverBridge.cpp + domainAverageInput.h + domainAverageInput.cpp + pointInitializationInput.h + pointInitializationInput.cpp + weatherModelInput.h + weatherModelInput.cpp + outputs.h + outputs.cpp + ) +else() + add_executable(WindNinja + ${PROJECT_SOURCES} + ) +endif() + +if(WIN32) + target_link_libraries(WindNinja PRIVATE shapelib::shp GDAL::GDAL) +endif() + +target_link_libraries(WindNinja PRIVATE Qt${QT_VERSION_MAJOR}::Widgets PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets PRIVATE ninja) +target_link_libraries(WindNinja PRIVATE Qt6::Core) + +target_link_libraries(WindNinja + PRIVATE Qt${QT_VERSION_MAJOR}::Widgets + PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets + PRIVATE Qt${QT_VERSION_MAJOR}::WebChannel + PRIVATE ninja +) + +include(GNUInstallDirs) +install(TARGETS WindNinja + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +if(QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(WindNinja) +endif() diff --git a/src/gui/qt6/appState.cpp b/src/gui/qt6/appState.cpp new file mode 100644 index 000000000..f777cbf78 --- /dev/null +++ b/src/gui/qt6/appState.cpp @@ -0,0 +1,477 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Stores the states for inputs in the GUI + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "appState.h" + +AppState& AppState::instance() +{ + static AppState s; + return s; +} + +AppState::AppState() + : tickIcon(":/tick.png"), + warnIcon(":/jason_caution.png"), + crossIcon(":/cross.png"), + bulletIcon(":/bullet_blue.png") +{} + +void AppState::setUi(Ui::MainWindow* mainWindowUi) +{ + ui = mainWindowUi; +} + +void AppState::setState() +{ + updateSolverMethodologyState(); + updateSurfaceInputState(); + updateDiurnalInputState(); + updateStabilityInputState(); + updateDomainAverageInputState(); + updatePointInitializationInputState(); + updateWeatherModelInputState(); + updateGoogleEarthOutputState(); + updateFireBehaviorOutputState(); + updateShapeFilesOutputState(); + updateGeoSpatialPDFFilesOutputState(); + updateVTKFilesOutputState(); +} + +void AppState::updateSolverMethodologyState() +{ + if (isMassSolverToggled) + { + isSolverMethodologyValid = true; + ui->treeWidget->topLevelItem(0)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(0)->setToolTip(0, "Using Conservation of Mass Selected"); + ui->treeWidget->topLevelItem(0)->child(0)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(0)->child(0)->setToolTip(0, "Conservation of Mass Selected"); + ui->treeWidget->topLevelItem(0)->child(1)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(0)->child(1)->setToolTip(0, "Conservation of Mass and Momentum Not Selected"); + } + else if (isMomentumSolverToggled) + { + isSolverMethodologyValid = true; + ui->treeWidget->topLevelItem(0)->setToolTip(1, "Conservation of Mass and Momentum Selected"); + ui->treeWidget->topLevelItem(0)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(0)->child(1)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(0)->child(1)->setToolTip(0, "Conservation of Mass and Momentum Selected"); + ui->treeWidget->topLevelItem(0)->child(0)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(0)->child(0)->setToolTip(0, "Conservation of Mass Not Selected"); + } + else + { + isSolverMethodologyValid = false; + ui->treeWidget->topLevelItem(0)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(0)->setToolTip(0,"Select a Solver"); + ui->treeWidget->topLevelItem(0)->child(0)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(0)->child(0)->setToolTip(0, "Conservation of Mass Not Selected"); + ui->treeWidget->topLevelItem(0)->child(1)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(0)->child(1)->setToolTip(0, "Conservation of Mass and Momentum Not Selected"); + } + + updateOverallState(); +} + +void AppState::updateSurfaceInputState() +{ + if (ui->elevationInputFileLineEdit->text() != "") + { + isSurfaceInputValid = true; + ui->treeWidget->topLevelItem(1)->child(0)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(1)->child(0)->setToolTip(0, "Valid"); + } + else + { + isSurfaceInputValid = false; + ui->treeWidget->topLevelItem(1)->child(0)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->child(0)->setToolTip(0, "Input File Cannot be Detected"); + } + updateInputState(); + updateGoogleEarthOutputState(); +} + +void AppState::updateDiurnalInputState() +{ + if (isDiurnalInputToggled) + { + ui->treeWidget->topLevelItem(1)->child(1)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(1)->child(1)->setToolTip(0, "Valid"); + } + else + { + ui->treeWidget->topLevelItem(1)->child(1)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(1)->child(1)->setToolTip(0, "No Diurnal Input"); + } +} + +void AppState::updateStabilityInputState() +{ + if (isStabilityInputToggled) + { + ui->treeWidget->topLevelItem(1)->child(2)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(1)->child(2)->setToolTip(0, "Valid"); + } + else + { + ui->treeWidget->topLevelItem(1)->child(2)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(1)->child(2)->setToolTip(0, "No Stability Input"); + } +} + +void AppState::updateDomainAverageInputState() +{ + if(isDomainAverageInitializationToggled) + { + if(DomainAvgTableNumRuns == 0) + { + if(ui->diurnalCheckBox->isChecked() == false) + { + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setToolTip(0, "No runs have been added, diurnal is not active"); + isDomainAverageInitializationValid = false; + } + else // if(ui->diurnalCheckBox->isChecked() == true) + { + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setIcon(0, warnIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setToolTip(0, "No runs have been added, one run will be done at speed = 0, dir = 0 while using diurnal"); + isDomainAverageInitializationValid = true; + } + } + else // if(DomainAvgTableNumRuns != 0) + { + if(DomainAvgTableNumZeroRuns == 0) + { + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setToolTip(0, QString::number(DomainAvgTableNumRuns)+" runs"); + isDomainAverageInitializationValid = true; + } + else // if(DomainAvgTableNumZeroRuns != 0) + { + if(ui->diurnalCheckBox->isChecked() == true) + { + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setIcon(0, warnIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setToolTip(0, QString::number(DomainAvgTableNumRuns)+" runs have been added, detecting "+QString::number(DomainAvgTableNumZeroRuns)+" zero wind speed runs, diurnal is active so will continue the runs"); + isDomainAverageInitializationValid = true; + } + else // if(ui->diurnalCheckBox->isChecked() == false) + { + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setToolTip(0, QString::number(DomainAvgTableNumRuns)+" runs have been added, but detecting "+QString::number(DomainAvgTableNumZeroRuns)+" zero wind speed runs without diurnal being active"); + isDomainAverageInitializationValid = false; + } + } + } + } + else + { + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(0)->setToolTip(0, "Not Selected"); + isDomainAverageInitializationValid = false; + } + + updateInputState(); +} + +void AppState::updatePointInitializationInputState() +{ + if (isPointInitializationToggled && isStationFileSelectionValid && isStationFileSelected) + { + ui->treeWidget->topLevelItem(1)->child(3)->child(1)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(1)->setToolTip(0, "Valid"); + isPointInitializationValid = true; + } + else if(isPointInitializationToggled && !isStationFileSelected) + { + ui->treeWidget->topLevelItem(1)->child(3)->child(1)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(1)->setToolTip(0, "No Station File Selected"); + isPointInitializationValid = false; + } + else if(isPointInitializationToggled && !isStationFileSelectionValid) + { + ui->treeWidget->topLevelItem(1)->child(3)->child(1)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(1)->setToolTip(0, "Conflicting Files Selected"); + isPointInitializationValid = false; + } + else + { + ui->treeWidget->topLevelItem(1)->child(3)->child(1)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(1)->setToolTip(0, "Not Selected"); + isPointInitializationValid = false; + } + + updateInputState(); +} + +void AppState::updateWeatherModelInputState() +{ + if (isWeatherModelInitializationToggled && isWeatherModelForecastValid) + { + isWeatherModelInitializationValid = true; + ui->treeWidget->topLevelItem(1)->child(3)->child(2)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(2)->setToolTip(0, "Valid"); + } + else if (isWeatherModelInitializationToggled && !isWeatherModelForecastValid) + { + isWeatherModelInitializationValid = false; + ui->treeWidget->topLevelItem(1)->child(3)->child(2)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(2)->setToolTip(0, "Forecast is Invalid"); + } + else + { + isWeatherModelInitializationValid = false; + ui->treeWidget->topLevelItem(1)->child(3)->child(2)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(1)->child(3)->child(2)->setToolTip(0, "Not Selected"); + } + + updateInputState(); +} + +void AppState::updateGoogleEarthOutputState() +{ + if(ui->googleEarthCheckBox->isChecked()) + { + if(isSurfaceInputValid) + { + isGoogleEarthValid = true; + ui->treeWidget->topLevelItem(2)->child(0)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, ""); + } + else + { + isGoogleEarthValid = false; + ui->treeWidget->topLevelItem(2)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, "Cannot read DEM File"); + ui->treeWidget->topLevelItem(2)->child(0)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->child(0)->setToolTip(0, "Cannot read DEM File"); + } + } + else + { + isGoogleEarthValid = false; + ui->treeWidget->topLevelItem(2)->child(0)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, "Not Selected"); + } + + updateOutputState(); +} + +void AppState::updateFireBehaviorOutputState() +{ + if(isFireBehaviorToggled) + { + if(isSurfaceInputValid) + { + isFireBehaviorValid = true; + ui->treeWidget->topLevelItem(2)->child(1)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, ""); + } + else + { + isFireBehaviorValid = false; + ui->treeWidget->topLevelItem(2)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, "Cannot read DEM File"); + ui->treeWidget->topLevelItem(2)->child(1)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->child(1)->setToolTip(0, "Cannot read DEM File"); + } + } + else + { + isFireBehaviorValid = false; + ui->treeWidget->topLevelItem(2)->child(1)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(2)->child(1)->setToolTip(0, "Not Selected"); + } + + updateOutputState(); +} + +void AppState::updateShapeFilesOutputState() +{ + if(isShapeFilesToggled) + { + if(isSurfaceInputValid) + { + isShapeFilesValid = true; + ui->treeWidget->topLevelItem(2)->child(2)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, ""); + } + else + { + isShapeFilesValid = false; + ui->treeWidget->topLevelItem(2)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, "Cannot read DEM File"); + ui->treeWidget->topLevelItem(2)->child(2)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->child(2)->setToolTip(0, "Cannot read DEM File"); + } + } + else + { + isShapeFilesValid = false; + ui->treeWidget->topLevelItem(2)->child(2)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(2)->child(2)->setToolTip(0, "Not Selected"); + } + + updateOutputState(); +} + +void AppState::updateGeoSpatialPDFFilesOutputState() +{ + if(isGeoSpatialPDFFilesToggled) + { + if(isSurfaceInputValid) + { + isGeoSpatialPDFFilesValid = true; + ui->treeWidget->topLevelItem(2)->child(3)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, ""); + } + else + { + isGeoSpatialPDFFilesValid = false; + ui->treeWidget->topLevelItem(2)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, "Cannot read DEM File"); + ui->treeWidget->topLevelItem(2)->child(3)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->child(3)->setToolTip(0, "Cannot read DEM File"); + } + } + else + { + isGeoSpatialPDFFilesValid = false; + ui->treeWidget->topLevelItem(2)->child(3)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(2)->child(3)->setToolTip(0, "Not Selected"); + } + + updateOutputState(); +} + +void AppState::updateVTKFilesOutputState() +{ + if(isVTKFilesToggled) + { + if(isSurfaceInputValid) + { + isVTKFilesValid = true; + ui->treeWidget->topLevelItem(2)->child(4)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, ""); + } + else + { + isVTKFilesValid = false; + ui->treeWidget->topLevelItem(2)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, "Cannot read DEM File"); + ui->treeWidget->topLevelItem(2)->child(4)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->child(4)->setToolTip(0, "Cannot read DEM File"); + } + } + else + { + isVTKFilesValid = false; + ui->treeWidget->topLevelItem(2)->child(4)->setIcon(0, bulletIcon); + ui->treeWidget->topLevelItem(2)->child(4)->setToolTip(0, "Not Selected"); + } + + updateOutputState(); +} + +void AppState::updateInputState() +{ + if (isDomainAverageInitializationValid || isPointInitializationValid || isWeatherModelInitializationValid) + { + isWindInputValid = true; + ui->treeWidget->topLevelItem(1)->child(3)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(1)->child(3)->setToolTip(0, "Valid"); + } + else + { + isWindInputValid = false; + ui->treeWidget->topLevelItem(1)->child(3)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->child(3)->setToolTip(0, "No Initialization Method Selected"); + } + + if (isSurfaceInputValid && isWindInputValid) + { + isInputValid = true; + ui->treeWidget->topLevelItem(1)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(1)->setToolTip(0, "Valid"); + } + else if (!isSurfaceInputValid && !isWindInputValid) + { + isInputValid = false; + ui->treeWidget->topLevelItem(1)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->setToolTip(0, "Check Surface Input"); + } + else if (!isSurfaceInputValid) + { + isInputValid = false; + ui->treeWidget->topLevelItem(1)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->setToolTip(0, "Check Surface Input"); + } + else if (!isWindInputValid) + { + isInputValid = false; + ui->treeWidget->topLevelItem(1)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(1)->setToolTip(0, "Check Wind Input"); + } + + updateOverallState(); +} + +void AppState::updateOutputState() +{ + if(isGoogleEarthValid || isFireBehaviorValid || isShapeFilesValid || isGeoSpatialPDFFilesValid || isVTKFilesValid) + { + isOutputValid = true; + ui->treeWidget->topLevelItem(2)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, "Valid"); + } + else + { + ui->treeWidget->topLevelItem(2)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(2)->setToolTip(0, "No Output Selected"); + } + + updateOverallState(); +} + +void AppState::updateOverallState() +{ + if (isSolverMethodologyValid && isInputValid && isOutputValid) + { + ui->numberOfProcessorsSolveButton->setEnabled(true); + ui->numberOfProcessorsSolveButton->setToolTip(""); + ui->treeWidget->topLevelItem(3)->setIcon(0, tickIcon); + ui->treeWidget->topLevelItem(3)->setToolTip(0, ""); + } + else + { + ui->numberOfProcessorsSolveButton->setEnabled(false); + ui->numberOfProcessorsSolveButton->setToolTip("Solver Methodology and Inputs must be passing to solve."); + ui->treeWidget->topLevelItem(3)->setIcon(0, crossIcon); + ui->treeWidget->topLevelItem(3)->setToolTip(0, "There are errors in the inputs or outputs"); + } +} diff --git a/src/gui/qt6/appState.h b/src/gui/qt6/appState.h new file mode 100644 index 000000000..a9adf4272 --- /dev/null +++ b/src/gui/qt6/appState.h @@ -0,0 +1,111 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Stores the states for inputs in the GUI + * Author: Mason Willman + * + ****************************************************************************** + * + * 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 APPSTATE_H +#define APPSTATE_H + +#include "ui_mainWindow.h" +#include + +class AppState : public QObject +{ + Q_OBJECT + +public: + static AppState& instance(); + void setUi(Ui::MainWindow* mainWindowUi); + void setState(); + + bool isSolverMethodologyValid = false; + bool isMassSolverToggled = false; + bool isMomentumSolverToggled = false; + + bool isInputValid = false; + bool isSurfaceInputValid = false; + bool isDiurnalInputToggled = false; + bool isStabilityInputToggled = false; + + bool isWindInputValid = false; + bool isDomainAverageInitializationToggled = false; + int DomainAvgTableNumRuns = 0; + int DomainAvgTableNumZeroRuns = 0; + bool isDomainAverageInitializationValid = false; + bool isPointInitializationToggled = false; + bool isStationFileSelected = false; + bool isStationFileSelectionValid = false; + bool isPointInitializationValid = false; + bool isWeatherModelInitializationToggled = false; + bool isWeatherModelForecastValid = false; + bool isWeatherModelInitializationValid = false; + + bool isOutputValid = false; + bool isGoogleEarthToggled = false; + bool isGoogleEarthValid = false; + bool isFireBehaviorToggled = false; + bool isFireBehaviorValid = false; + bool isShapeFilesToggled = false; + bool isShapeFilesValid = false; + bool isGeoSpatialPDFFilesToggled = false; + bool isGeoSpatialPDFFilesValid = false; + bool isVTKFilesToggled = false; + bool isVTKFilesValid = false; + + bool isSolverReady = false; + +public slots: + void updateSolverMethodologyState(); + void updateSurfaceInputState(); + void updateDiurnalInputState(); + void updateStabilityInputState(); + void updateDomainAverageInputState(); + void updatePointInitializationInputState(); + void updateWeatherModelInputState(); + void updateGoogleEarthOutputState(); + void updateFireBehaviorOutputState(); + void updateShapeFilesOutputState(); + void updateGeoSpatialPDFFilesOutputState(); + void updateVTKFilesOutputState(); + +private: + Ui::MainWindow *ui; + + QIcon tickIcon; + QIcon warnIcon; + QIcon crossIcon; + QIcon bulletIcon; + + AppState(); + AppState(const AppState&) = delete; + AppState& operator=(const AppState&) = delete; + void updateInputState(); + void updateOutputState(); + void updateOverallState(); +}; + +#endif // APPSTATE_H diff --git a/src/gui/qt6/domainAverageInput.cpp b/src/gui/qt6/domainAverageInput.cpp new file mode 100644 index 000000000..a55ca24f2 --- /dev/null +++ b/src/gui/qt6/domainAverageInput.cpp @@ -0,0 +1,204 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Handles GUI related logic for the Domain Average Page + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "domainAverageInput.h" +#include "ui_mainWindow.h" + +DomainAverageInput::DomainAverageInput(Ui::MainWindow* ui, QObject* parent) + : QObject(parent), + ui(ui) +{ + setupDomainAverageTableWidgets(); + ui->domainAverageTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + connect(ui->inputWindHeightComboBox, &QComboBox::currentIndexChanged, this, &DomainAverageInput::windHeightComboBoxCurrentIndexChanged); + connect(ui->clearTableButton, &QPushButton::clicked, this, &DomainAverageInput::clearTableButtonClicked); + connect(ui->domainAverageTable, &QTableWidget::cellChanged, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(ui->diurnalCheckBox, &QCheckBox::clicked, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(ui->stabilityCheckBox, &QCheckBox::clicked, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(ui->domainAverageGroupBox, &QGroupBox::toggled, this, &DomainAverageInput::domainAverageGroupBoxToggled); + connect(ui->domainAverageGroupBox, &QGroupBox::toggled, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(this, &DomainAverageInput::updateState, &AppState::instance(), &AppState::updateDomainAverageInputState); +} + +int DomainAverageInput::countNumRuns() +{ + int numActiveRows = 0; + for(int rowIdx = 0; rowIdx < ui->domainAverageTable->rowCount(); rowIdx++) + { + if(speedSpins[rowIdx]->value() != 0.0 || dirSpins[rowIdx]->value() != 0.0) + { + //numActiveRows = numActiveRows + 1; // this method skips adding up the in between 0.0, 0.0 spd, dir rows + numActiveRows = rowIdx + 1; // last active row, as a 1 to N count, rather than as a 0 to N-1 rowIdx, this method properly grabs the in between 0.0, 0.0 spd, dir rows + } + } + + return numActiveRows; +} + +void DomainAverageInput::domainAverageTableCheckRows() +{ + int numRuns = countNumRuns(); + + int numZeroRuns = 0; + for(int runIdx = 0; runIdx < numRuns; runIdx++) + { + if(speedSpins[runIdx]->value() == 0.0) + { + numZeroRuns = numZeroRuns + 1; + } + } + + AppState::instance().DomainAvgTableNumRuns = numRuns; + AppState::instance().DomainAvgTableNumZeroRuns = numZeroRuns; + + emit updateState(); +} + +void DomainAverageInput::clearTableButtonClicked() +{ + // AppState::instance().DomainAvgTableNumRuns and AppState::instance().DomainAvgTableNumZeroRuns are set + // and updateState() is emitted here, by the call to the domainAverageTableCheckRows() function + + speedSpins.clear(); + dirSpins.clear(); + timeEdits.clear(); + dateEdits.clear(); + cloudSpins.clear(); + airTempSpins.clear(); + + ui->domainAverageTable->clearContents(); + + setupDomainAverageTableWidgets(); + + domainAverageTableCheckRows(); +} + +void DomainAverageInput::windHeightComboBoxCurrentIndexChanged(int index) +{ + switch(index) + { + case 0: + ui->inputWindHeightSpinBox->setValue(20.00); + ui->inputWindHeightSpinBox->setEnabled(false); + ui->inputWindHeightUnitsComboBox->setCurrentIndex(0); + break; + + case 1: + ui->inputWindHeightSpinBox->setValue(10.00); + ui->inputWindHeightSpinBox->setEnabled(false); + ui->inputWindHeightUnitsComboBox->setCurrentIndex(1); + break; + + case 2: + ui->inputWindHeightSpinBox->setValue(0.00); + ui->inputWindHeightSpinBox->setEnabled(true); + ui->inputWindHeightUnitsComboBox->setEnabled(true); + break; + } +} + +void DomainAverageInput::domainAverageGroupBoxToggled() +{ + AppState& state = AppState::instance(); + state.isDomainAverageInitializationToggled = ui->domainAverageGroupBox->isChecked(); + + if (state.isDomainAverageInitializationToggled) + { + ui->pointInitializationGroupBox->setChecked(false); + ui->weatherModelGroupBox->setChecked(false); + state.isPointInitializationToggled = ui->pointInitializationGroupBox->isChecked(); + state.isWeatherModelInitializationToggled = ui->weatherModelGroupBox->isChecked(); + } + + emit updateState(); +} + +void DomainAverageInput::setupDomainAverageTableWidgets() +{ + QTableWidget* table = ui->domainAverageTable; + int rows = table->rowCount(); + + speedSpins.resize(rows); + dirSpins.resize(rows); + timeEdits.resize(rows); + dateEdits.resize(rows); + cloudSpins.resize(rows); + airTempSpins.resize(rows); + + for(int row = 0; row < rows; row++) + { + speedSpins[row] = new QDoubleSpinBox(table); + speedSpins[row]->setRange(0.0, 500.0); + speedSpins[row]->setDecimals(2); + table->setCellWidget(row, 0, speedSpins[row]); + + dirSpins[row] = new QDoubleSpinBox(table); + dirSpins[row]->setRange(0.0, 359.9); + dirSpins[row]->setDecimals(0); + table->setCellWidget(row, 1, dirSpins[row]); + + timeEdits[row] = new QTimeEdit(QTime::currentTime(), table); + timeEdits[row]->setDisplayFormat("HH:mm"); + table->setCellWidget(row, 2, timeEdits[row]); + + dateEdits[row] = new QDateEdit(QDate::currentDate(), table); + dateEdits[row]->setCalendarPopup(true); + dateEdits[row]->setDisplayFormat("MM/dd/yyyy"); + table->setCellWidget(row, 3, dateEdits[row]); + + cloudSpins[row] = new QDoubleSpinBox(table); + cloudSpins[row]->setRange(0.0, 100.0); + cloudSpins[row]->setDecimals(0); + table->setCellWidget(row, 4, cloudSpins[row]); + + airTempSpins[row] = new QDoubleSpinBox(table); + airTempSpins[row]->setRange(-40.0, 200.0); + airTempSpins[row]->setDecimals(0); + airTempSpins[row]->setValue(72.0); + table->setCellWidget(row, 5, airTempSpins[row]); + + connect(speedSpins[row], &QDoubleSpinBox::valueChanged, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(dirSpins[row], &QDoubleSpinBox::valueChanged, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(timeEdits[row], &QTimeEdit::timeChanged, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(dateEdits[row], &QDateEdit::dateChanged, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(cloudSpins[row], &QDoubleSpinBox::valueChanged, this, &DomainAverageInput::domainAverageTableCheckRows); + connect(airTempSpins[row], &QDoubleSpinBox::valueChanged, this, &DomainAverageInput::domainAverageTableCheckRows); + } + + bool enabled = ui->diurnalCheckBox->isChecked() || ui->stabilityCheckBox->isChecked(); + for(int row = 0; row < rows; row++) + { + timeEdits[row]->setEnabled(enabled); + dateEdits[row]->setEnabled(enabled); + cloudSpins[row]->setEnabled(enabled); + airTempSpins[row]->setEnabled(enabled); + } +} + diff --git a/src/gui/GoogleMapsInterface.h b/src/gui/qt6/domainAverageInput.h similarity index 55% rename from src/gui/GoogleMapsInterface.h rename to src/gui/qt6/domainAverageInput.h index f9fb7d08b..4c1f92542 100644 --- a/src/gui/GoogleMapsInterface.h +++ b/src/gui/qt6/domainAverageInput.h @@ -1,10 +1,10 @@ -/****************************************************************************** + /****************************************************************************** * - * $Id: GoogleMapsInterface.h 1757 2012-08-07 18:40:40Z kyle.shannon $ + * $Id$ * - * Project: WindNinja - * Purpose: Class for creating an interface between javascript and Qt - * Author: Cody Posey + * Project: WindNinja Qt GUI + * Purpose: Handles GUI related logic for the Domain Average Page + * Author: Mason Willman * ****************************************************************************** * @@ -26,28 +26,43 @@ * DEALINGS IN THE SOFTWARE. * *****************************************************************************/ -#ifndef GOOGLE_MAPS_INTERFACE_H_ -#define GOOGLE_MAPS_INTERFACE_H_ + +#ifndef DOMAINAVERAGEINPUT_H +#define DOMAINAVERAGEINPUT_H + +#include "appState.h" +#include "ui_mainWindow.h" #include -class GoogleMapsInterface : public QObject +class DomainAverageInput: public QObject { Q_OBJECT +signals: + void updateState(); + public: - GoogleMapsInterface(QObject *parent = 0); + DomainAverageInput(Ui::MainWindow* ui, QObject* parent = nullptr); -signals: - void latlngChanged(double lat, double lng); - void latlngChangedGUI(double lat, double lng); - void plotUserPoint(); - void zoomExtents(); - void boundsChanged(double northBound, double southBound, double eastBound, double westBound); - void boundsChangedGUI(double northBound, double southBound, double eastBound, double westBound); - void bufferChanged(); - void areaSelected(bool selected); - void geocodeError(); -}; + int countNumRuns(); + + QVector speedSpins; + QVector dirSpins; + QVector timeEdits; + QVector dateEdits; + QVector cloudSpins; + QVector airTempSpins; + +private slots: + void setupDomainAverageTableWidgets(); + void domainAverageTableCheckRows(); + void clearTableButtonClicked(); + void domainAverageGroupBoxToggled(); + void windHeightComboBoxCurrentIndexChanged(int index); -#endif /* GOOGLE_MAPS_INTERFACE_H_ */ +private: + Ui::MainWindow *ui; + +}; +#endif // DOMAINAVERAGEINPUT_H diff --git a/src/gui/cmake_gui.cpp b/src/gui/qt6/main.cpp old mode 100644 new mode 100755 similarity index 55% rename from src/gui/cmake_gui.cpp rename to src/gui/qt6/main.cpp index f82f6d4d0..948b97bfb --- a/src/gui/cmake_gui.cpp +++ b/src/gui/qt6/main.cpp @@ -1,10 +1,10 @@ -/****************************************************************************** + /****************************************************************************** * * $Id$ * * Project: WindNinja Qt GUI * Purpose: main() function to initiate Qt GUI - * Author: Kyle Shannon + * Author: Mason Willman * ****************************************************************************** * @@ -27,63 +27,62 @@ * *****************************************************************************/ -#include "cli.h" - +#include "mainWindow.h" +#include "windninja.h" #include +#include #include #include -#include -#include -#include +#include +#include +#include #include -#include "mainWindow.h" -#include "splash.h" +#include +#include +#include "splashScreen.h" -#include "ninjaArmy.h" -#include "ninjaException.h" -#include "ninja_conv.h" -#include "ninja_init.h" -#include "gdal.h" -#include "ogr_api.h" - -#ifdef _OPENMP -omp_lock_t netCDF_lock; -#endif int main(int argc, char *argv[]) { + setbuf(stdout, nullptr); + int result; -#ifdef _OPENMP - omp_init_lock (&netCDF_lock); -#endif - if(argc > 1) + char ** papszOptions = NULL; + const char * runType = "gui"; + NinjaErr ninjaErr = 0; + ninjaErr = NinjaInit(runType, papszOptions); + if(ninjaErr != NINJA_SUCCESS) { - CPLSetConfigOption( "NINJA_DISABLE_CALL_HOME", "ON" ); - result = windNinjaCLI(argc, argv); -#ifdef _OPENMP - omp_destroy_lock (&netCDF_lock); -#endif - return result; + qDebug() << "NinjaInit: ninjaErr =" << ninjaErr; } - NinjaInitialize("gui"); - - QApplication app(argc, argv); - - //set application name/version in User-Agent header + QApplication a(argc, argv); + QIcon icon(":/wn-icon.png"); QString ver = NINJA_VERSION_STRING; - app.setApplicationName(QString("WindNinja")); - app.setApplicationVersion(ver); + a.setWindowIcon(icon); + a.setApplicationName(QString("WindNinja")); + a.setApplicationVersion(ver); + a.setStyle(QStyleFactory::create("Fusion")); - app.setWindowIcon(QIcon(":wn-icon.png")); + MainWindow* w = nullptr; + try + { + w = new MainWindow; + } + catch (...) + { + QMessageBox::critical(nullptr, "Initialization Error", + "WindNinja failed to initialize. Most likely cause is failure to find data " + "dependencies. Try setting the environment variable WINDNINJA_DATA"); + return 1; + } QPixmap bigSplashPixmap(":wn-splash.png"); - //resampled one QSize splashSize(1200, 320); QPixmap smallSplashPixmap; smallSplashPixmap = bigSplashPixmap.scaled(splashSize, - Qt::KeepAspectRatioByExpanding); + Qt::KeepAspectRatioByExpanding); QStringList list; list << "Loading WindNinja " + ver + "..."; list << "Loading mesh generator..."; @@ -91,30 +90,10 @@ int main(int argc, char *argv[]) list << "Loading preconditioner..."; list << "WindNinja " + ver + " loaded."; - mainWindow *mw; - QMessageBox mbox; - try - { - mw = new mainWindow; - } - catch(...) - { - mbox.setText("WindNinja failed to initialize. Most " - "likely cause is failure to find data " - "dependencies. Try setting the environment " - "variable WINDNINJA_DATA"); - mbox.exec(); - return 1; - } - splashScreen *splash = new splashScreen(smallSplashPixmap, list, 1000); + SplashScreen *splash = new SplashScreen(smallSplashPixmap, list, 1000); splash->display(); - //QObject::connect(splash, SIGNAL(done()), mw, SLOT(checkMessages())); - QObject::connect(splash, SIGNAL(done()), mw, SLOT(show())); - result = app.exec(); - -#ifdef _OPENMP - omp_destroy_lock (&netCDF_lock); -#endif + QObject::connect(splash, SIGNAL(done()), w, SLOT(show())); + result = a.exec(); return result; } diff --git a/src/gui/qt6/mainWindow.cpp b/src/gui/qt6/mainWindow.cpp new file mode 100644 index 000000000..cb1c262dc --- /dev/null +++ b/src/gui/qt6/mainWindow.cpp @@ -0,0 +1,1724 @@ +/****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Main Window that handles GUI scraping and state changes + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "mainWindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + resize(1200, 700); + ui->treeWidget->expandAll(); + + AppState& state = AppState::instance(); + state.setUi(ui); + ui->massSolverCheckBox->setChecked(true); + ui->treeWidget->setMouseTracking(true); + state.isMassSolverToggled = true; + + lineNumber = 1; + + serverBridge = new ServerBridge(); + serverBridge->checkMessages(); + + QWebEngineProfile::defaultProfile()->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); + QWebEngineProfile::defaultProfile()->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, true); + QString dataPath = QString::fromUtf8(CPLGetConfigOption("WINDNINJA_DATA", "")); + QString mapPath = QDir(dataPath).filePath("map.html"); + webEngineView = new QWebEngineView(ui->mapPanelWidget); + webChannel = new QWebChannel(webEngineView->page()); + mapBridge = new MapBridge(this); + webChannel->registerObject(QStringLiteral("bridge"), mapBridge); + webEngineView->page()->setWebChannel(webChannel); + + QUrl url = QUrl::fromLocalFile(mapPath); + webEngineView->setUrl(url); + QVBoxLayout *layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(webEngineView); + ui->mapPanelWidget->setLayout(layout); + menuBar = new MenuBar(ui, this); + surfaceInput = new SurfaceInput(ui, webEngineView, this); + domainAverageInput = new DomainAverageInput(ui, this); + pointInitializationInput = new PointInitializationInput(ui, this); + weatherModelInput = new WeatherModelInput(ui, this); + outputs = new Outputs(ui, this); + + ui->treeWidget->topLevelItem(0)->setData(0, Qt::UserRole, 1); + ui->treeWidget->topLevelItem(0)->child(0)->setData(0, Qt::UserRole, 1); + ui->treeWidget->topLevelItem(0)->child(1)->setData(0, Qt::UserRole, 2); + ui->treeWidget->topLevelItem(1)->setData(0, Qt::UserRole, 3); + ui->treeWidget->topLevelItem(1)->child(0)->setData(0, Qt::UserRole, 3); + ui->treeWidget->topLevelItem(1)->child(1)->setData(0, Qt::UserRole, 4); + ui->treeWidget->topLevelItem(1)->child(2)->setData(0, Qt::UserRole, 5); + ui->treeWidget->topLevelItem(1)->child(3)->setData(0, Qt::UserRole, 6); + QTreeWidgetItem *windInputItem = ui->treeWidget->topLevelItem(1)->child(3); + windInputItem->child(0)->setData(0, Qt::UserRole, 6); + windInputItem->child(1)->setData(0, Qt::UserRole, 7); + windInputItem->child(2)->setData(0, Qt::UserRole, 8); + ui->treeWidget->topLevelItem(2)->setData(0, Qt::UserRole, 9); + ui->treeWidget->topLevelItem(2)->child(0)->setData(0, Qt::UserRole, 10); + ui->treeWidget->topLevelItem(2)->child(1)->setData(0, Qt::UserRole, 11); + ui->treeWidget->topLevelItem(2)->child(2)->setData(0, Qt::UserRole, 12); + ui->treeWidget->topLevelItem(2)->child(3)->setData(0, Qt::UserRole, 13); + ui->treeWidget->topLevelItem(2)->child(4)->setData(0, Qt::UserRole, 14); + ui->treeWidget->topLevelItem(3)->setData(0, Qt::UserRole, 15); + + connectSignals(); + + ui->treeWidget->topLevelItem(0)->setSelected(true); + ui->inputsStackedWidget->setCurrentIndex(1); // setSelected shows the blank page, have to have this to show proper page + + int nCPUs = QThread::idealThreadCount(); + ui->availableProcessorsLabel->setText("Available Processors: " + QString::number(nCPUs)); + ui->numberOfProcessorsSpinBox->setMaximum(nCPUs); + ui->numberOfProcessorsSpinBox->setValue(nCPUs); + + QString version(NINJA_VERSION_STRING); + version = "Welcome to WindNinja " + version; + writeToConsole(version, Qt::blue); + writeToConsole("WINDNINJA_DATA=" + dataPath); + + state.setState(); +} + +MainWindow::~MainWindow() +{ + delete serverBridge; + delete ui; +} + +void MainWindow::connectSignals() +{ + connect(ui->massSolverCheckBox, &QCheckBox::clicked, this, &MainWindow::massSolverCheckBoxClicked); + connect(ui->momentumSolverCheckBox, &QCheckBox::clicked, this, &MainWindow::momentumSolverCheckBoxClicked); + connect(ui->diurnalCheckBox, &QCheckBox::clicked, this, &MainWindow::diurnalCheckBoxClicked); + connect(ui->stabilityCheckBox, &QCheckBox::clicked, this, &MainWindow::stabilityCheckBoxClicked); + connect(ui->treeWidget, &QTreeWidget::itemDoubleClicked, this, &MainWindow::treeWidgetItemDoubleClicked); + connect(ui->numberOfProcessorsSolveButton, &QPushButton::clicked, this, &MainWindow::solveButtonClicked); + connect(ui->outputDirectoryButton, &QPushButton::clicked, this, &MainWindow::outputDirectoryButtonClicked); + connect(ui->treeWidget, &QTreeWidget::itemSelectionChanged, this, &MainWindow::treeWidgetItemSelectionChanged); + + connect(menuBar, &MenuBar::writeToConsoleSignal, this, &MainWindow::writeToConsole); + connect(mapBridge, &MapBridge::boundingBoxReceived, surfaceInput, &SurfaceInput::boundingBoxReceived); + connect(surfaceInput, &SurfaceInput::updateTreeView, pointInitializationInput, &PointInitializationInput::updateTreeView); + connect(surfaceInput, &SurfaceInput::updateTreeView, weatherModelInput, &WeatherModelInput::updateTreeView); + connect(weatherModelInput, &WeatherModelInput::updateState, &AppState::instance(), &AppState::updateWeatherModelInputState); + connect(webEngineView, &QWebEngineView::loadFinished, this, &MainWindow::readSettings); + + connect(this, &MainWindow::updateDirunalState, &AppState::instance(), &AppState::updateDiurnalInputState); + connect(this, &MainWindow::updateStabilityState, &AppState::instance(), &AppState::updateStabilityInputState); + connect(this, &MainWindow::updateMetholodyState, &AppState::instance(), &AppState::updateSolverMethodologyState); + connect(this, &MainWindow::updateProgressValueSignal, this, &MainWindow::updateProgressValue, Qt::QueuedConnection); + connect(this, &MainWindow::updateProgressMessageSignal, this, &MainWindow::updateProgressMessage, Qt::QueuedConnection); + connect(this, &MainWindow::writeToConsoleSignal, this, &MainWindow::writeToConsole, Qt::QueuedConnection); + + connect(surfaceInput, &SurfaceInput::writeToConsoleSignal, this, &MainWindow::writeToConsole, Qt::QueuedConnection); + connect(pointInitializationInput, &PointInitializationInput::writeToConsoleSignal, this, &MainWindow::writeToConsole, Qt::QueuedConnection); + connect(weatherModelInput, &WeatherModelInput::writeToConsoleSignal, this, &MainWindow::writeToConsole, Qt::QueuedConnection); +} + +void MainWindow::writeToConsole(QString message, QColor color) +{ + // if( ui->consoleDockWidget->isFloating() && color == Qt::white ) + // { + // color = Qt::black; + // } + + ui->consoleTextEdit->setTextColor(color); + ui->consoleTextEdit->append(QString::number(lineNumber) + ": " + message); + ui->consoleTextEdit->repaint(); + lineNumber++; +} + +void MainWindow::updateProgressMessage(const QString message) +{ + progressDialog->setLabelText(message); +} + +void MainWindow::updateProgressValue(int run, int progress) +{ + // update the stored progress value for the current run + if( runProgress[run] > progress ) + { + // if the stored progress is bigger than what we are seeing in the currently emitted progress + // ignore it. This happens for pointInitialization, when the match points is iterating, + // sometimes its next solution is worse and then it would make the progress bar go backwards + // by ignoring it, the progress bar just stays where it is + runProgress[run] = runProgress[run]; + } + else + { + // otherwise, store the progress for the current run + runProgress[run] = progress; + } + + // update the total progress value + // calculate the total progress from scratch each time, summing up the progress from each run + totalProgress = 0; // Initialize the progress bar each time + for(unsigned int i = 0; i < runProgress.size(); i++) + { + totalProgress = totalProgress + runProgress[i]; + } + + // update the progress bar + progressDialog->setValue(totalProgress); +} + +static void comMessageHandler(const char *pszMessage, void *pUser) +{ + MainWindow *self = static_cast(pUser); + + std::string msg = pszMessage; + if( msg.substr(msg.size()-1, 1) == "\n") + { + msg = msg.substr(0, msg.size()-1); + } + + int runNumber = -1; + sscanf(msg.c_str(), "Run %d", &runNumber); + + size_t pos; + size_t startPos; + size_t endPos; + std::string clipStr; + + int runProgress; + endPos = msg.find("% complete"); + if( endPos != msg.npos ) + { + clipStr = msg.substr(0, endPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + pos = clipStr.rfind(": "); + startPos = pos+2; + clipStr = clipStr.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + runProgress = atoi(clipStr.c_str()); + + emit self->updateProgressValueSignal(runNumber, runProgress); + } + +//"Run 1 ERROR: Multiple runs were requested with the same input parameters." +//"Run 0 ERROR: Exception caught: I WANT CHOCOLATE!!! Yum." +//"Run 0: Exception caught: Simulation was cancelled by the user." +//"Run 1: Exception canceled by user caught: Simulation was cancelled by the user." + + if( msg.find("Exception caught: ") != msg.npos || msg.find("ERROR: ") != msg.npos || msg.find("Exception canceled by user caught: ") != msg.npos ) + { + if( msg.find("Exception caught: ") != msg.npos ) + { + pos = msg.find("Exception caught: "); + startPos = pos+18; + } + else if( msg.find("Exception canceled by user caught: ") != msg.npos ) + { + pos = msg.find("Exception canceled by user caught: "); + startPos = pos+35; + } + else // if( msg.find("ERROR: ") != msg.npos ) + { + pos = msg.find("ERROR: "); + startPos = pos+7; + } + clipStr = msg.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); + //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); + if( clipStr == "Simulation was cancelled by the user." ) + { + emit self->updateProgressMessageSignal(QString::fromStdString("Simulation cancelled")); + emit self->writeToConsoleSignal(QString::fromStdString("Simulation cancelled by user"), QColor(255, 140, 0)); + } + else if( clipStr == "Cannot determine exception type." ) + { + emit self->updateProgressMessageSignal(QString::fromStdString("Simulation ended with unknown error")); + emit self->writeToConsoleSignal(QString::fromStdString("unknown solver error"), Qt::red); + } + else + { + emit self->updateProgressMessageSignal(QString::fromStdString("Simulation ended in error:\n"+clipStr)); + emit self->writeToConsoleSignal(QString::fromStdString("Solver error: "+clipStr), Qt::red); + } + } + else if( msg.find("Warning: ") != msg.npos ) + { + if( msg.find("Warning: ") != msg.npos ) + { + pos = msg.find("Warning: "); + startPos = pos+9; + } + clipStr = msg.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); + //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); + emit self->updateProgressMessageSignal(QString::fromStdString("Solver ended in warning:\n"+clipStr)); + emit self->writeToConsoleSignal(QString::fromStdString("Solver warning: "+clipStr), QColor(255, 140, 0)); + } + else + { + emit self->updateProgressMessageSignal(QString::fromStdString(msg)); + emit self->writeToConsoleSignal(QString::fromStdString(msg)); + } +} + +void MainWindow::cancelSolve() +{ + progressDialog->setLabelText("Canceling..."); + //qDebug() << "Canceling..."; + //writeToConsole( "Canceling...", QColor(255, 140, 0)); + + char **papszOptions = nullptr; + ninjaErr = NinjaCancel(ninjaArmy, papszOptions); + if( ninjaErr != NINJA_SUCCESS ) + { + qDebug() << "NinjaCancel: ninjaErr =" << ninjaErr; + } +} + +void MainWindow::treeWidgetItemSelectionChanged() +{ + int column = ui->treeWidget->currentColumn(); + int pageIndex = ui->treeWidget->selectedItems().first()->data(column, Qt::UserRole).toInt(); // assume 0 since no multi selection + ui->inputsStackedWidget->setCurrentIndex(pageIndex); +} + +void MainWindow::massSolverCheckBoxClicked() +{ + AppState& state = AppState::instance(); + + if (state.isMomentumSolverToggled) + { + ui->momentumSolverCheckBox->setChecked(false); + state.isMomentumSolverToggled = ui->momentumSolverCheckBox->isChecked(); + } + state.isMassSolverToggled = ui->massSolverCheckBox->isChecked(); + + if(!ui->elevationInputFileLineEdit->text().isEmpty()) + { + ui->meshResolutionSpinBox->setValue(surfaceInput->computeMeshResolution(ui->meshResolutionComboBox->currentIndex(), ui->momentumSolverCheckBox->isChecked())); + surfaceInput->updateMeshResolutionByUnits(); + } + emit updateMetholodyState(); +} + +void MainWindow::momentumSolverCheckBoxClicked() +{ + AppState& state = AppState::instance(); + + if (state.isMassSolverToggled) + { + ui->massSolverCheckBox->setChecked(false); + state.isMassSolverToggled = ui->massSolverCheckBox->isChecked(); + } + state.isMomentumSolverToggled = ui->momentumSolverCheckBox->isChecked(); + + if(!ui->elevationInputFileLineEdit->text().isEmpty()) + { + ui->meshResolutionSpinBox->setValue(surfaceInput->computeMeshResolution(ui->meshResolutionComboBox->currentIndex(), ui->momentumSolverCheckBox->isChecked())); + surfaceInput->updateMeshResolutionByUnits(); + } + emit updateMetholodyState(); +} + +void MainWindow::diurnalCheckBoxClicked() +{ + AppState& state = AppState::instance(); + state.isDiurnalInputToggled = ui->diurnalCheckBox->isChecked(); + + bool enabled = ui->diurnalCheckBox->isChecked() || ui->stabilityCheckBox->isChecked(); + for(int row = 0; row < ui->domainAverageTable->rowCount(); row++) + { + domainAverageInput->timeEdits[row]->setEnabled(enabled); + domainAverageInput->dateEdits[row]->setEnabled(enabled); + domainAverageInput->cloudSpins[row]->setEnabled(enabled); + domainAverageInput->airTempSpins[row]->setEnabled(enabled); + } + + emit updateDirunalState(); +} + +void MainWindow::stabilityCheckBoxClicked() +{ + AppState& state = AppState::instance(); + state.isStabilityInputToggled = ui->stabilityCheckBox->isChecked(); + + bool enabled = ui->diurnalCheckBox->isChecked() || ui->stabilityCheckBox->isChecked(); + for(int row = 0; row < ui->domainAverageTable->rowCount(); row++) + { + domainAverageInput->timeEdits[row]->setEnabled(enabled); + domainAverageInput->dateEdits[row]->setEnabled(enabled); + domainAverageInput->cloudSpins[row]->setEnabled(enabled); + domainAverageInput->airTempSpins[row]->setEnabled(enabled); + } + + emit updateStabilityState(); +} + +void MainWindow::outputDirectoryButtonClicked() +{ + QString currentPath = ui->outputDirectoryLineEdit->text(); + QString dirPath = QFileDialog::getExistingDirectory(this, "Select a directory", currentPath, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + if (!dirPath.isEmpty()) + { + ui->outputDirectoryLineEdit->setText(dirPath); + ui->outputDirectoryLineEdit->setToolTip(dirPath); + } else + { + ui->outputDirectoryLineEdit->setText(currentPath); + ui->outputDirectoryLineEdit->setToolTip(currentPath); + } +} + +void MainWindow::solveButtonClicked() +{ + AppState& state = AppState::instance(); + + maxProgress = 100; + //progressDialog = new QProgressDialog("Initializing Runs...", "Cancel", 0, maxProgress, ui->centralwidget); + progressDialog = new QProgressDialog(ui->centralwidget); + progressDialog->setRange(0, maxProgress); + progressDialog->setValue(0); + progressDialog->setLabelText("Initializing Runs..."); + progressDialog->setCancelButtonText("Cancel"); + + progressDialog->setWindowModality(Qt::WindowModal); + progressDialog->setMinimumDuration(0); + progressDialog->setAutoClose(false); + progressDialog->setAutoReset(false); + + progressDialog->setMinimumSize(380, 100); + progressDialog->show(); + + ninjaErr = NINJA_SUCCESS; + + int numNinjas = 0; + ninjaArmy = nullptr; + char **papszOptions = nullptr; + const char *initializationMethod = nullptr; + + ninjaArmy = NinjaInitializeArmy(); + + ninjaErr = NinjaSetArmyComMessageHandler(ninjaArmy, &comMessageHandler, this, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetArmyComMessageHandler(): ninjaErr =" << ninjaErr; + } + + if (state.isDomainAverageInitializationValid) + { + initializationMethod = "domain_average"; + QList speeds; + QList directions; + QList years; + QList months; + QList days; + QList hours; + QList minutes; + QList cloudCovers; + QList airTemps; + + QString DEMTimeZone = ui->timeZoneComboBox->currentText(); + + numNinjas = domainAverageInput->countNumRuns(); + + // countNumRuns() returns 0 when ALL rows are 0.0, 0.0 spd, dir rows, + // but if diurnal is checked, we actually DO want to run that first 0.0, 0.0 spd, dir row as a single run + if(numNinjas == 0 && ui->diurnalCheckBox->isChecked() == true) + { + numNinjas = 1; + } + + for(int runIdx = 0; runIdx < numNinjas; runIdx++) + { + speeds << domainAverageInput->speedSpins[runIdx]->value(); + directions << domainAverageInput->dirSpins[runIdx]->value(); + + // always grab the values from the diurnal/stability inputs, + // whether they are the default values, or whatever the user has changed them to be + + // constructs using machine local time, may need to convert from machine local time to UTC time + QDateTime currentDateTime = QDateTime(domainAverageInput->dateEdits[runIdx]->date(), domainAverageInput->timeEdits[runIdx]->time()); + + years << currentDateTime.date().year(); + months << currentDateTime.date().month(); + days << currentDateTime.date().day(); + hours << currentDateTime.time().hour(); + minutes << currentDateTime.time().minute(); + + cloudCovers << domainAverageInput->cloudSpins[runIdx]->value(); + airTemps << domainAverageInput->airTempSpins[runIdx]->value(); + } + + bool momentumFlag = ui->momentumSolverCheckBox->isChecked(); + QString speedUnits = ui->tableSpeedUnits->currentText(); + QString airTempUnits = ui->tableTempUnits->currentText().remove("°"); + QString cloudCoverUnits = "percent"; + if(ninjaErr == NINJA_SUCCESS) + { + ninjaErr = NinjaMakeDomainAverageArmy(ninjaArmy, numNinjas, momentumFlag, speeds.data(), speedUnits.toUtf8().constData(), directions.data(), years.data(), months.data(), days.data(), hours.data(), minutes.data(), DEMTimeZone.toUtf8().data(), airTemps.data(), airTempUnits.toUtf8().constData(), cloudCovers.data(), cloudCoverUnits.toUtf8().constData(), papszOptions); + //ninjaErr = NinjaMakeDomainAverageArmy(ninjaArmy, -1, momentumFlag, speeds.data(), speedUnits.toUtf8().constData(), directions.data(), years.data(), months.data(), days.data(), hours.data(), minutes.data(), DEMTimeZone.toUtf8().data(), airTemps.data(), airTempUnits.toUtf8().constData(), cloudCovers.data(), cloudCoverUnits.toUtf8().constData(), papszOptions); // catches error as expected, now it triggers the NinjaMakeDomainAverageArmy() single messaging error, instead of the double messaging makeDomainAverageArmy() error. + //ninjaErr = NinjaMakeDomainAverageArmy(ninjaArmy, 0, momentumFlag, speeds.data(), speedUnits.toUtf8().constData(), directions.data(), years.data(), months.data(), days.data(), hours.data(), minutes.data(), DEMTimeZone.toUtf8().data(), airTemps.data(), airTempUnits.toUtf8().constData(), cloudCovers.data(), cloudCoverUnits.toUtf8().constData(), papszOptions); // catches error as expected, now it triggers the NinjaMakeDomainAverageArmy() single messaging error, instead of the double messaging makeDomainAverageArmy() error. + //ninjaErr = NinjaMakeDomainAverageArmy(ninjaArmy, numNinjas, momentumFlag, speeds.data(), speedUnits.toUtf8().constData(), directions.data(), years.data(), months.data(), days.data(), hours.data(), minutes.data(), "fudge", airTemps.data(), airTempUnits.toUtf8().constData(), cloudCovers.data(), cloudCoverUnits.toUtf8().constData(), papszOptions); // requires the try/catch form of IF_VALID_INDEX_TRY in ninjaArmy.h, but then catches error as expected, well it technically throws two separate error messages, but both are caught properly + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaMakeDomainAverageArmy: ninjaErr =" << ninjaErr; + } + } + } + else if (state.isPointInitializationValid) + { + initializationMethod = "point"; + + NinjaToolsH* ninjaTools = NinjaMakeTools(); + + ninjaErr = NinjaSetToolsComMessageHandler(ninjaTools, &comMessageHandler, this, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetToolsComMessageHandler(): ninjaErr =" << ninjaErr; + } + + QVector stationFiles = pointInitializationInput->getStationFiles(); + QString DEMTimeZone = ui->timeZoneComboBox->currentText(); + QByteArray timeZoneBytes = ui->timeZoneComboBox->currentText().toUtf8(); + + std::vector stationFilesBytes; + stationFilesBytes.reserve(stationFiles.size()); + std::vector stationFileNames; + stationFileNames.reserve(stationFiles.size()); + for (int i = 0; i < stationFiles.size(); i++) + { + stationFilesBytes.push_back(stationFiles[i].toUtf8()); + stationFileNames.push_back(stationFilesBytes.back().constData()); + } + + QString DEMPath = ui->elevationInputFileLineEdit->property("fullpath").toString(); + bool momentumFlag = ui->momentumSolverCheckBox->isChecked(); + + if(ui->pointInitializationTreeView->property("timeSeriesFlag").toBool()) + { + QDateTime start = ui->weatherStationDataStartDateTimeEdit->dateTime(); + QDateTime end = ui->weatherStationDataEndDateTimeEdit->dateTime(); + + QVector year = { start.date().year(), end.date().year() }; + QVector month = { start.date().month(), end.date().month() }; + QVector day = { start.date().day(), end.date().day() }; + QVector hour = { start.time().hour(), end.time().hour() }; + QVector minute = { start.time().minute(), end.time().minute() }; + + // runs fine for the single time run, as expected, + // and, errors and is properly caught for the multi-time run + /*QVector year = {start.date().year(), start.date().year()}; + QVector month = {start.date().month(), start.date().month()}; + QVector day = {start.date().day(), start.date().day()}; + QVector hour = {start.time().hour(), start.time().hour()}; + QVector minute = {start.time().minute(), start.time().minute()};*/ + + // runs fine for the single time run, as expected, + // and, errors and is properly caught for the multi-time run + /*QVector year = {end.date().year(), end.date().year()}; + QVector month = {end.date().month(), end.date().month()}; + QVector day = {end.date().day(), end.date().day()}; + QVector hour = {end.time().hour(), end.time().hour()}; + QVector minute = {end.time().minute(), end.time().minute()};*/ + + // runs fine for the single time run, as expected, + // and, errors and is properly caught for the multi-time run + /*QVector year = {start.date().year(), start.date().year()-1}; + QVector month = {start.date().month(), start.date().month()}; + QVector day = {start.date().day(), start.date().day()}; + QVector hour = {start.time().hour(), start.time().hour()}; + QVector minute = {start.time().minute(), start.time().minute()};*/ + + // runs fine for the single time run, as expected, + // and, errors and is properly caught for the multi-time run + // which is interesting because the download without an additional hour time difference should also error but does not always error, + // so this implies the time checking for the run from this, is more strict, and better + /*QVector year = {start.date().year(), start.date().year()}; + QVector month = {start.date().month(), start.date().month()}; + QVector day = {start.date().day(), start.date().day()}; + QVector hour = {start.time().hour(), start.time().hour()-1}; + QVector minute = {start.time().minute(), start.time().minute()};*/ + + // errors for both the single time run AND the multi-time run, + // and errors are properly caught for both cases + /*QVector year = {end.date().year()+1, end.date().year()}; + QVector month = {end.date().month(), end.date().month()}; + QVector day = {end.date().day(), end.date().day()}; + QVector hour = {end.time().hour(), end.time().hour()}; + QVector minute = {end.time().minute(), end.time().minute()};*/ + + // errors for both the single time run AND the multi-time run, + // and errors are properly caught for both cases + /*QVector year = {end.date().year(), end.date().year()}; + QVector month = {end.date().month(), end.date().month()}; + QVector day = {end.date().day(), end.date().day()}; + QVector hour = {end.time().hour()+1, end.time().hour()}; + QVector minute = {end.time().minute(), end.time().minute()};*/ + + int nTimeSteps = ui->weatherStationDataTimestepsSpinBox->value(); + //int nTimeSteps = 1; // runs fine for the single time, properly throws an error for multi-times, well the error implies out of index but maybe not at the proper step ("NinjaSetNumberCPUS", "Run 0: ERROR: Exception caught: invalid index 1". But the error is at least properly caught. + //int nTimeSteps = 2; // runs fine for 2 timestep multi-times, but for 1 timestep multi-times, an error is getting thrown, but apparently the solver isn't stopping because it is an error on just one single thread???? Quirky behavior that is not good. "ERROR 4: : No such file or directory, Run 1: ERROR: Exception caught: Cannot open input file for reading in ninja::readInputFile()." but then it continues with the run0 info to completion, then it ends hanging because it didn't stop at the error message and it finds it DID have some kind of error at the end. Ugh. I do see that it printed red, so it SAW that it was an error message, but I guess it wasn't a THROWN error message or something? So it didn't properly stop the solver?? Not sure what is going on here. + + QVector outYear(nTimeSteps); + QVector outMonth(nTimeSteps); + QVector outDay(nTimeSteps); + QVector outHour(nTimeSteps); + QVector outMinute(nTimeSteps); + + if(nTimeSteps == 1) + { + int startYear = year[0]; + int startMonth = month[0]; + int startDay = day[0]; + int startHour = hour[0]; + int startMinute = minute[0]; + + int endYear, endMonth, endDay, endHour, endMinute; + + ninjaErr = NinjaGenerateSingleTimeObject( + ninjaTools, + startYear, startMonth, startDay, startHour, startMinute, + timeZoneBytes.constData(), + &endYear, &endMonth, &endDay, &endHour, &endMinute + ); + //ninjaErr = NinjaGenerateSingleTimeObject( + // ninjaTools, + // startYear, startMonth, startDay, startHour, startMinute, + // "fudge", + // &endYear, &endMonth, &endDay, &endHour, &endMinute + // ); // breaks HARD, a smart pointer failing on assert somewhere along the pipeline, not sure if that occurs here, or later down the pipeline. And it gets past the try/catch error handling stuff, hrm. + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaGenerateSingleTimeObject: ninjaErr =" << ninjaErr; + } + + outYear[0] = endYear; + outMonth[0] = endMonth; + outDay[0] = endDay; + outHour[0] = endHour; + outMinute[0] = endMinute; + } + else + { + ninjaErr = NinjaGetTimeList( + ninjaTools, + year.data(), month.data(), day.data(), + hour.data(), minute.data(), + outYear.data(), outMonth.data(), outDay.data(), + outHour.data(), outMinute.data(), + nTimeSteps, timeZoneBytes.data() + ); + //ninjaErr = NinjaGetTimeList( + // ninjaTools, + // year.data(), month.data(), day.data(), + // hour.data(), minute.data(), + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), + // 1, timeZoneBytes.data() + // ); // catches error as expected, though month or date out of range wasn't quite the error I was expecting + //ninjaErr = NinjaGetTimeList( + // ninjaTools, + // year.data(), month.data(), day.data(), + // hour.data(), minute.data(), + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), + // nTimeSteps, "fudge" + // ); // breaks HARD, a smart pointer failing on assert somewhere along the pipeline, not sure if that occurs here, or later down the pipeline. And it gets past the try/catch error handling stuff, hrm. + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaGetTimeList: ninjaErr =" << ninjaErr; + } + } + + if(ninjaErr == NINJA_SUCCESS) + { + numNinjas = ui->weatherStationDataTimestepsSpinBox->value(); + + ninjaErr = NinjaMakePointArmy( ninjaArmy, + outYear.data(), outMonth.data(), outDay.data(), + outHour.data(), outMinute.data(), nTimeSteps, + DEMTimeZone.toUtf8().data(), stationFileNames.data(), + stationFileNames.size(), DEMPath.toUtf8().data(), + true, momentumFlag, papszOptions + ); + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), -1, + // DEMTimeZone.toUtf8().data(), stationFileNames.data(), + // stationFileNames.size(), DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + // ); // catches error as expected, now it triggers the NinjaMakePointArmy() single messaging error, instead of the double messaging makePointArmy() error, and instead of the single unexpected month or date out of range error that was seen for this case. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), 0, + // DEMTimeZone.toUtf8().data(), stationFileNames.data(), + // stationFileNames.size(), DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + // ); // catches error as expected, now it triggers the NinjaMakePointArmy() single messaging error, instead of the double messaging makePointArmy() error. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), nTimeSteps, + // DEMTimeZone.toUtf8().data(), stationFileNames.data(), + // -1, DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + // ); // catches error as expected, now it triggers the NinjaMakePointArmy() single messaging error, instead of the double messaging makePointArmy() error. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), nTimeSteps, + // DEMTimeZone.toUtf8().data(), stationFileNames.data(), + // 0, DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + // ); // catches error as expected, now it triggers the NinjaMakePointArmy() single messaging error, instead of the double messaging makePointArmy() error. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), nTimeSteps, + // DEMTimeZone.toUtf8().data(), stationFileNames.data(), + // stationFileNames.size(), "fudge", + // true, momentumFlag, papszOptions + // ); // um, it warns that the dem doesn't exist, but then continues on without throwing an error or a ninjaCom, so the solver continues as if everything is normal. The warning is "ERROR 4: fudge: No such file or directory" four times, yet still it continues as if nothing went wrong. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), nTimeSteps, + // "fudge", stationFileNames.data(), + // stationFileNames.size(), DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + // ); // no error or warning messages are thrown, just runs as if the dem timezone is good enough + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), nTimeSteps, + // DEMTimeZone.toUtf8().data(), stationFileNames.data(), + // stationFileNames.size(), DEMPath.toUtf8().data(), + // true, true, papszOptions + // ); // catches error as expected + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaMakePointArmy: ninjaErr =" << ninjaErr; + } + } + + if(ninjaErr != NINJA_SUCCESS) + { + // do cleanup before the return, similar to finishedSolve() + +// ninjaErr = NinjaDestroyTools(ninjaTools, papszOptions); +// if(ninjaErr != NINJA_SUCCESS) +// { +// printf("NinjaDestroyTools: ninjaErr = %d\n", ninjaErr); +// } + } + } + else + { + int year, month, day, hour, minute; + QDateTime date = ui->weatherStationDataLabel->property("simulationTime").toDateTime(); + year = date.date().year(); + month = date.date().month(); + day = date.date().day(); + hour = date.time().hour(); + minute = date.time().minute(); + + //year = -1; // catches error as expected + //year = 0; // catches error as expected + //year = 1235; // catches error as expected + //year = 2035; // um, apparently this one is an allowable year, it runs like normal without an error thrown, even though it probably shouldn't + + //month = -1; // catches error as expected + //month = 0; // catches error as expected + //month = 14; // catches error as expected + + //day = -1; // catches error as expected + //day = 0; // catches error as expected + //day = 33; // catches error as expected + + //hour = -1; // this one SHOULD error, but runs fine somehow, no errors thrown. Probably wraps around or sets it to a value of 0 or something. + //hour = 26; // this one SHOULD error, but runs fine somehow, no errors thrown. Probably wraps around or sets it to a value of 0 or something. + + //minute = -1; // this one SHOULD error, but runs fine somehow, no errors thrown. Probably wraps around or sets it to a value of 0 or something. + //minute = 78; // this one SHOULD error, but runs fine somehow, no errors thrown. Probably wraps around or sets it to a value of 0 or something. + + int outYear, outMonth, outDay, outHour, outMinute; + + ninjaErr = NinjaGenerateSingleTimeObject( + ninjaTools, + year, month, day, hour, minute, + timeZoneBytes.constData(), + &outYear, &outMonth, &outDay, &outHour, &outMinute + ); + //ninjaErr = NinjaGenerateSingleTimeObject( + // ninjaTools, + // year, month, day, hour, minute, + // "fudge", + // &outYear, &outMonth, &outDay, &outHour, &outMinute + // ); // breaks HARD, a smart pointer failing on assert somewhere along the pipeline, not sure if that occurs here, or later down the pipeline. And it gets past the try/catch error handling stuff, hrm. + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaGenerateSingleTimeObject: ninjaErr =" << ninjaErr; + } + + QVector yearVec = { outYear }; + QVector monthVec = { outMonth }; + QVector dayVec = { outDay }; + QVector hourVec = { outHour }; + QVector minuteVec = { outMinute }; + + numNinjas = 1; + int nTimeSteps = 1; + + if(ninjaErr == NINJA_SUCCESS) + { + ninjaErr = NinjaMakePointArmy( ninjaArmy, + yearVec.data(), monthVec.data(), dayVec.data(), + hourVec.data(), minuteVec.data(), nTimeSteps, + DEMTimeZone.toUtf8().data(), + stationFileNames.data(), + static_cast(stationFileNames.size()), + DEMPath.toUtf8().data(), + true, momentumFlag, papszOptions + ); + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // yearVec.data(), monthVec.data(), dayVec.data(), + // hourVec.data(), minuteVec.data(), -1, + // DEMTimeZone.toUtf8().data(), + // stationFileNames.data(), + // static_cast(stationFileNames.size()), + // DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + //); // catches error as expected, now it triggers the NinjaMakePointArmy() single messaging error, instead of the double messaging makePointArmy() error, and instead of the single unexpected month or date out of range error that was seen for this case. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // yearVec.data(), monthVec.data(), dayVec.data(), + // hourVec.data(), minuteVec.data(), 0, + // DEMTimeZone.toUtf8().data(), + // stationFileNames.data(), + // static_cast(stationFileNames.size()), + // DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + //); // catches error as expected, now it triggers the NinjaMakePointArmy() single messaging error, instead of the double messaging makePointArmy() error. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // yearVec.data(), monthVec.data(), dayVec.data(), + // hourVec.data(), minuteVec.data(), nTimeSteps, + // DEMTimeZone.toUtf8().data(), + // stationFileNames.data(), + // -1, + // DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + //); // catches error as expected, now it triggers the NinjaMakePointArmy() single messaging error, instead of the double messaging makePointArmy() error. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // yearVec.data(), monthVec.data(), dayVec.data(), + // hourVec.data(), minuteVec.data(), nTimeSteps, + // DEMTimeZone.toUtf8().data(), + // stationFileNames.data(), + // 0, + // DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + //); // catches error as expected, now it triggers the NinjaMakePointArmy() single messaging error, instead of the double messaging makePointArmy() error. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // yearVec.data(), monthVec.data(), dayVec.data(), + // hourVec.data(), minuteVec.data(), nTimeSteps, + // DEMTimeZone.toUtf8().data(), + // stationFileNames.data(), + // static_cast(stationFileNames.size()), + // "fudge", + // true, momentumFlag, papszOptions + //); // um, it warns that the dem doesn't exist, but then continues on without throwing an error or a ninjaCom, so the solver continues as if everything is normal. The warning is "ERROR 4: fudge: No such file or directory" four times, yet still it continues as if nothing went wrong. + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // yearVec.data(), monthVec.data(), dayVec.data(), + // hourVec.data(), minuteVec.data(), nTimeSteps, + // "fudge", + // stationFileNames.data(), + // static_cast(stationFileNames.size()), + // DEMPath.toUtf8().data(), + // true, momentumFlag, papszOptions + //); // no error or warning messages are thrown, just runs as if the dem timezone is good enough + //ninjaErr = NinjaMakePointArmy( ninjaArmy, + // yearVec.data(), monthVec.data(), dayVec.data(), + // hourVec.data(), minuteVec.data(), nTimeSteps, + // DEMTimeZone.toUtf8().data(), + // stationFileNames.data(), + // static_cast(stationFileNames.size()), + // DEMPath.toUtf8().data(), + // true, true, papszOptions + //); // catches error as expected + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaMakePointArmy ninjaErr =" << ninjaErr; + } + } + } + } + else //if (state.isWeatherModelInitializationValid) + { + QModelIndexList selectedIndexes = ui->weatherModelTimeTreeView->selectionModel()->selectedIndexes(); + int timeListSize = selectedIndexes.count(); + numNinjas = timeListSize; + initializationMethod = "wxmodel"; + std::string timeZone = ui->timeZoneComboBox->currentText().toStdString(); + + QModelIndex index = ui->weatherModelFileTreeView->currentIndex(); + QFileSystemModel *model = qobject_cast(ui->weatherModelFileTreeView->model()); + std::string filePath = model->filePath(index).toStdString(); + + // Allocate the char** array + const char **inputTimeList = new const char*[timeListSize]; + + for (int i = 0; i < timeListSize; ++i) + { + QString qstr = selectedIndexes[i].data().toString(); + std::string str = qstr.toStdString(); + inputTimeList[i] = strdup(str.c_str()); // allocate and copy each string + } + + ninjaErr = NinjaMakeWeatherModelArmy(ninjaArmy, filePath.c_str(), timeZone.c_str(), inputTimeList, timeListSize, ui->momentumSolverCheckBox->isChecked(), papszOptions); + //ninjaErr = NinjaMakeWeatherModelArmy(ninjaArmy, filePath.c_str(), timeZone.c_str(), inputTimeList, -1, ui->momentumSolverCheckBox->isChecked(), papszOptions); // catches error as expected, now it triggers the NinjaMakeWeatherModelArmy() single messaging error, instead of the double messaging makeWeatherArmy() error. + //ninjaErr = NinjaMakeWeatherModelArmy(ninjaArmy, filePath.c_str(), timeZone.c_str(), inputTimeList, 0, ui->momentumSolverCheckBox->isChecked(), papszOptions); // catches error as expected, now it triggers the NinjaMakeWeatherModelArmy() single messaging error, instead of the double messaging makeWeatherArmy() error. + //ninjaErr = NinjaMakeWeatherModelArmy(ninjaArmy, "fudge", timeZone.c_str(), inputTimeList, timeListSize, ui->momentumSolverCheckBox->isChecked(), papszOptions); // catches error as expected + //ninjaErr = NinjaMakeWeatherModelArmy(ninjaArmy, filePath.c_str(), "fudge", inputTimeList, timeListSize, ui->momentumSolverCheckBox->isChecked(), papszOptions); // catches error as expected + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaMakeWeatherModelArmy ninjaErr =" << ninjaErr; + } + } + + if(ninjaErr != NINJA_SUCCESS) + { + progressDialog->setValue(maxProgress); + progressDialog->setCancelButtonText("Close"); + + // do cleanup before the return, similar to finishedSolve() + + //disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + + char **papszOptions = nullptr; + int ninjaErr = NinjaDestroyArmy(ninjaArmy, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + printf("NinjaDestroyRuns: ninjaErr = %d\n", ninjaErr); + } + + // clear the progress values for the next set of runs + //runProgress.clear(); + + //futureWatcher->deleteLater(); + + return; + } + + writeToConsole(QString::number( numNinjas ) + " runs initialized. Starting solver..."); + + maxProgress = numNinjas*100; + + progressDialog->setRange(0, maxProgress); + progressDialog->setValue(0); + progressDialog->setLabelText("Solving..."); + + progressDialog->setCancelButtonText("Cancel"); + connect( progressDialog, SIGNAL( canceled() ), this, SLOT( cancelSolve() ) ); + + // initialize the progress values for the current set of runs + totalProgress = 0; + for(unsigned int i = 0; i < numNinjas; i++) + { + runProgress.push_back(0); + } + + futureWatcher = new QFutureWatcher(this); + + bool retVal = prepareArmy(ninjaArmy, numNinjas, initializationMethod); + if( retVal == false ) + { + progressDialog->setValue(maxProgress); + progressDialog->setCancelButtonText("Close"); + + // do cleanup before the return, similar to finishedSolve() + + disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + + char **papszOptions = nullptr; + int ninjaErr = NinjaDestroyArmy(ninjaArmy, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + printf("NinjaDestroyRuns: ninjaErr = %d\n", ninjaErr); + } + + // clear the progress values for the next set of runs + runProgress.clear(); + + futureWatcher->deleteLater(); + + return; + } + + // set progress dialog initial value and initial text for the set of runs + progressDialog->setValue(0); + progressDialog->setLabelText("Running..."); + + qDebug() << "Initializing runs..."; + writeToConsole( "Initializing runs..." ); + + connect(futureWatcher, &QFutureWatcher::finished, this, &MainWindow::finishedSolve); + + QFuture future = QtConcurrent::run(&MainWindow::startSolve, this, ui->numberOfProcessorsSpinBox->value()); + futureWatcher->setFuture(future); + +} + +void MainWindow::treeWidgetItemDoubleClicked(QTreeWidgetItem *item, int column) +{ + if (item->text(0) == "Conservation of Mass") + { + ui->massSolverCheckBox->click(); + } + else if (item->text(0) == "Conservation of Mass and Momentum") + { + ui->momentumSolverCheckBox->click(); + } + else if (item->text(0) == "Diurnal Input") + { + ui->diurnalCheckBox->click(); + } + else if (item->text(0) == "Stability Input") + { + ui->stabilityCheckBox->click(); + } + else if (item->text(0) == "Domain Average Wind") + { + ui->domainAverageGroupBox->setChecked(!ui->domainAverageGroupBox->isChecked()); + } + else if (item->text(0) == "Point Initialization") + { + ui->pointInitializationGroupBox->setChecked(!ui->pointInitializationGroupBox->isChecked()); + } + else if (item->text(0) == "Weather Model") + { + ui->weatherModelGroupBox->setChecked(!ui->weatherModelGroupBox->isChecked()); + } + else if (item->text(0) == "Surface Input") + { + ui->elevationInputFileOpenButton->click(); + } + else if (item->text(0) == "Google Earth") + { + ui->googleEarthCheckBox->setChecked(!ui->googleEarthCheckBox->isChecked()); + } + else if (item->text(0) == "Fire Behavior") + { + ui->fireBehaviorGroupBox->setChecked(!ui->fireBehaviorGroupBox->isChecked()); + } + else if (item->text(0) == "Shape Files") + { + ui->shapeFilesGroupBox->setChecked(!ui->shapeFilesGroupBox->isChecked()); + } + else if (item->text(0) == "Geospatial PDF Files") + { + ui->geospatialPDFFilesGroupBox->setChecked(!ui->geospatialPDFFilesGroupBox->isChecked()); + } + else if (item->text(0) == "VTK Files") + { + ui->VTKFilesCheckBox->click(); + } +} + +bool MainWindow::prepareArmy(NinjaArmyH *ninjaArmy, int numNinjas, const char* initializationMethod) +{ + OutputPDFSize PDFSize; + switch(ui->sizeDimensionsComboBox->currentIndex()) + { + case 0: + PDFSize.PDFHeight = 11.0; + PDFSize.PDFWidth = 8.5; + PDFSize.PDFDpi = 150; + break; + case 1: + PDFSize.PDFHeight = 14.0; + PDFSize.PDFWidth = 8.5; + PDFSize.PDFDpi = 150; + break; + case 2: + PDFSize.PDFHeight = 17.0; + PDFSize.PDFWidth = 11.0; + PDFSize.PDFDpi = 150; + break; + } + if (ui->sizeOrientationComboBox->currentIndex() == 1) + { + std::swap(PDFSize.PDFHeight, PDFSize.PDFWidth); + } + + char **papszOptions = nullptr; + + // can this one even be tested?? The way it is organized also makes it tough to setup a ninjaCom message + ninjaErr = NinjaSetAsciiAtmFile(ninjaArmy, ui->fireBehaviorResolutionCheckBox->isChecked(), papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetAsciiAtmFile: ninjaErr =" << ninjaErr; + return false; + } + + for(unsigned int i=0; ipointInitializationGroupBox->isChecked()) + { + if(ui->pointInitializationWriteStationKMLCheckBox->isChecked()) + { + // 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 + //ninjaErr = NinjaSetStationKML(ninjaArmy, i, ui->elevationInputFileLineEdit->property("fullpath").toString().toUtf8().constData(), ui->outputDirectoryLineEdit->text().toUtf8().constData(), "fudge", papszOptions); // test error handling // ran, but the functions need reorganized for proper messaging + //ninjaErr = NinjaSetStationKML(ninjaArmy, i, ui->elevationInputFileLineEdit->property("fullpath").toString().toUtf8().constData(), "fudge", ui->outputSpeedUnitsComboBox->currentText().toUtf8().constData(), papszOptions); // test error handling // function needs reorganized to handle this test + //ninjaErr = NinjaSetStationKML(ninjaArmy, i, "fudge", ui->outputDirectoryLineEdit->text().toUtf8().constData(), ui->outputSpeedUnitsComboBox->currentText().toUtf8().constData(), papszOptions); // test error handling // function needs reorganized to handle this test + if(ninjaErr != NINJA_SUCCESS) + { + printf("NinjaSetStationKML: ninjaErr = %d\n", ninjaErr); + return false; + } + } + } + + ninjaErr = NinjaSetNumberCPUs(ninjaArmy, i, ui->numberOfProcessorsSpinBox->value(), papszOptions); + //ninjaErr = NinjaSetNumberCPUs(ninjaArmy, i+10, ui->numberOfProcessorsSpinBox->value(), papszOptions); // test error handling + //ninjaErr = NinjaSetNumberCPUs(ninjaArmy, i, -1, papszOptions); // test error handling // requires the try/catch form of IF_VALID_INDEX_TRY in ninjaArmy.h + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetNumberCPUs: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetInitializationMethod(ninjaArmy, i, initializationMethod, ui->pointInitializationGroupBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetInitializationMethod(ninjaArmy, i+10, initializationMethod, ui->pointInitializationGroupBox->isChecked(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetInitializationMethod(ninjaArmy, i, "fudge", ui->pointInitializationGroupBox->isChecked(), papszOptions); // test error handling + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetInitializationMethod: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetDem(ninjaArmy, i, ui->elevationInputFileLineEdit->property("fullpath").toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetDem(ninjaArmy, i+10, ui->elevationInputFileLineEdit->property("fullpath").toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetDem(ninjaArmy, i, "fudge", papszOptions); // test error handling // requires the try/catch form of IF_VALID_INDEX_TRY in ninjaArmy.h + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetDem: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetPosition(ninjaArmy, i, papszOptions); // if setting up ninja.cpp function call to simply throw, this breaks, this requires the try/catch form of IF_VALID_INDEX_TRY in ninjaArmy.h + //ninjaErr = NinjaSetPosition(ninjaArmy, i+10, papszOptions); // test error handling + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetPosition: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetInputWindHeight(ninjaArmy, i, ui->inputWindHeightSpinBox->value(), ui->inputWindHeightUnitsComboBox->itemData(ui->inputWindHeightUnitsComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetInputWindHeight(ninjaArmy, i+10, ui->inputWindHeightSpinBox->value(), ui->inputWindHeightUnitsComboBox->itemData(ui->inputWindHeightUnitsComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetInputWindHeight(ninjaArmy, i, ui->inputWindHeightSpinBox->value(), "fudge", papszOptions); // test error handling + //ninjaErr = NinjaSetInputWindHeight(ninjaArmy, i, -1, ui->inputWindHeightUnitsComboBox->itemData(ui->inputWindHeightUnitsComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetInputWindHeight: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetDiurnalWinds(ninjaArmy, i, ui->diurnalCheckBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetDiurnalWinds(ninjaArmy, i+10, ui->diurnalCheckBox->isChecked(), papszOptions); // test error handling + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetDiurnalWinds: ninjaErr =" << ninjaErr; + return false; + } + + if(ui->vegetationStackedWidget->currentIndex() == 0) + { + ninjaErr = NinjaSetUniVegetation(ninjaArmy, i, ui->vegetationComboBox->currentText().toLower().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetUniVegetation(ninjaArmy, i+10, ui->vegetationComboBox->currentText().toLower().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetUniVegetation(ninjaArmy, i, "fudge", papszOptions); // test error handling + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetUniVegetation: ninjaErr =" << ninjaErr; + return false; + } + } + + if(ui->meshResolutionComboBox->currentIndex() == 3) // custom res + { + ninjaErr = NinjaSetMeshResolution(ninjaArmy, i, ui->meshResolutionSpinBox->value(), ui->meshResolutionUnitsComboBox->itemData(ui->meshResolutionUnitsComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetMeshResolution: ninjaErr =" << ninjaErr; + return false; + } + } else + { + ninjaErr = NinjaSetMeshResolutionChoice(ninjaArmy, i, ui->meshResolutionComboBox->currentText().toLower().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetMeshResolutionChoice(ninjaArmy, i+10, ui->meshResolutionComboBox->currentText().toLower().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetMeshResolutionChoice(ninjaArmy, i, "fudge", papszOptions); // test error handling + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetMeshResolutionChoice: ninjaErr =" << ninjaErr; + return false; + } + } + + ninjaErr = NinjaSetNumVertLayers(ninjaArmy, i, 20, papszOptions); + //ninjaErr = NinjaSetNumVertLayers(ninjaArmy, i+10, 20, papszOptions); // test error handling + //ninjaErr = NinjaSetNumVertLayers(ninjaArmy, i, -1, papszOptions); // test error handling // requires the try/catch form of IF_VALID_INDEX_TRY in ninjaArmy.h + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetNumVertLayers: ninjaErr =" << ninjaErr; + return false; + } + + bool retVal = setOutputFlags(ninjaArmy, i, numNinjas, PDFSize); + if( retVal == false ) + { + return false; + } + } + + return true; +} + +bool MainWindow::setOutputFlags(NinjaArmyH* ninjaArmy, + int i, + int numNinjas, + OutputPDFSize PDFSize) +{ + char **papszOptions = nullptr; + int ninjaErr; + + ninjaErr = NinjaSetOutputPath(ninjaArmy, i, ui->outputDirectoryLineEdit->text().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetOutputPath(ninjaArmy, i+10, ui->outputDirectoryLineEdit->text().toUtf8().constData(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetOutputPath: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetOutputWindHeight(ninjaArmy, i, ui->outputWindHeightSpinBox->value(), ui->outputWindHeightUnitsComboBox->itemData(ui->outputWindHeightUnitsComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetOutputWindHeight(ninjaArmy, i+10, ui->outputWindHeightSpinBox->value(), ui->outputWindHeightUnitsComboBox->itemData(ui->outputWindHeightUnitsComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetOutputWindHeight(ninjaArmy, i, ui->outputWindHeightSpinBox->value(), "fudge", papszOptions); // test error handling + //ninjaErr = NinjaSetOutputWindHeight(ninjaArmy, i, -1, ui->outputWindHeightUnitsComboBox->itemData(ui->outputWindHeightUnitsComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetOutputWindHeight: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetOutputSpeedUnits(ninjaArmy, i, ui->outputSpeedUnitsComboBox->currentText().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetOutputSpeedUnits(ninjaArmy, i+10, ui->outputSpeedUnitsComboBox->currentText().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetOutputSpeedUnits(ninjaArmy, i, "fudge", papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetOutputSpeedUnits: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetOutputBufferClipping(ninjaArmy, i, ui->clipOutputSpinBox->value(), papszOptions); + //ninjaErr = NinjaSetOutputBufferClipping(ninjaArmy, i+10, ui->clipOutputSpinBox->value(), papszOptions); // test error handling, looks good + //ninjaErr = NinjaSetOutputBufferClipping(ninjaArmy, i, -1, papszOptions); // test error handling + //ninjaErr = NinjaSetOutputBufferClipping(ninjaArmy, i, 50, papszOptions); // test error handling, message might need improved, but it IS an error, as it should be + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetOutputBufferClipping: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetGoogOutFlag(ninjaArmy, i, ui->googleEarthCheckBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetGoogOutFlag(ninjaArmy, i+10, ui->googleEarthCheckBox->isChecked(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetGoogOutFlag: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetGoogResolution(ninjaArmy, i, ui->googleEarthMeshResolutionSpinBox->value(), ui->googleEarthMeshResolutionComboBox->itemData(ui->googleEarthMeshResolutionComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetGoogResolution(ninjaArmy, i+10, ui->googleEarthMeshResolutionSpinBox->value(), ui->googleEarthMeshResolutionComboBox->itemData(ui->googleEarthMeshResolutionComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetGoogResolution(ninjaArmy, i, ui->googleEarthMeshResolutionSpinBox->value(), "fudge", papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetGoogResolution: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetGoogSpeedScaling(ninjaArmy, i, ui->legendComboBox->itemData(ui->legendComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetGoogSpeedScaling(ninjaArmy, i+10, ui->legendComboBox->itemData(ui->legendComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetGoogSpeedScaling(ninjaArmy, i, "fudge", papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetGoogSpeedScaling: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetGoogLineWidth(ninjaArmy, i, ui->googleEarthVectorsSpinBox->value(), papszOptions); + //ninjaErr = NinjaSetGoogLineWidth(ninjaArmy, i+10, ui->googleEarthVectorsSpinBox->value(), papszOptions); // test error handling + //ninjaErr = NinjaSetGoogLineWidth(ninjaArmy, i, -1, papszOptions); // test error handling // requires the try/catch form of IF_VALID_INDEX_TRY in ninjaArmy.h + //ninjaErr = NinjaSetGoogLineWidth(ninjaArmy, i, 101, papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetGoogLineWidth: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetGoogColor(ninjaArmy, i, ui->alternativeColorSchemeComboBox->itemData(ui->alternativeColorSchemeComboBox->currentIndex()).toString().toUtf8().constData(), ui->googleEarthVectorScalingCheckBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetGoogColor(ninjaArmy, i+10, ui->alternativeColorSchemeComboBox->itemData(ui->alternativeColorSchemeComboBox->currentIndex()).toString().toUtf8().constData(), ui->googleEarthVectorScalingCheckBox->isChecked(), papszOptions); // test error handling + ////ninjaErr = NinjaSetGoogColor(ninjaArmy, i, "fudge", ui->googleEarthVectorScalingCheckBox->isChecked(), papszOptions); // test error handling // requires the try/catch form of IF_VALID_INDEX_TRY in ninjaArmy.h // actually, the colorScheme string appears to not even be checked + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetGoogColor: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetGoogConsistentColorScale(ninjaArmy, i, ui->legendCheckBox->isChecked(), numNinjas, papszOptions); + //ninjaErr = NinjaSetGoogConsistentColorScale(ninjaArmy, i+10, ui->legendCheckBox->isChecked(), numNinjas, papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetGoogConsistentColorScale: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetAsciiOutFlag(ninjaArmy, i, ui->fireBehaviorGroupBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetAsciiOutFlag(ninjaArmy, i+10, ui->fireBehaviorGroupBox->isChecked(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetAsciiOutFlag: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetAsciiResolution(ninjaArmy, i, ui->fireBehaviorMeshResolutionSpinBox->value(), ui->fireBehaviorMeshResolutionComboBox->itemData(ui->fireBehaviorMeshResolutionComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetAsciiResolution(ninjaArmy, i+10, ui->fireBehaviorMeshResolutionSpinBox->value(), ui->fireBehaviorMeshResolutionComboBox->itemData(ui->fireBehaviorMeshResolutionComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetAsciiResolution(ninjaArmy, i, ui->fireBehaviorMeshResolutionSpinBox->value(), "fudge", papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetAsciiResolution: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetShpOutFlag(ninjaArmy, i, ui->shapeFilesGroupBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetShpOutFlag(ninjaArmy, i+10, ui->shapeFilesGroupBox->isChecked(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetShpOutFlag: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetShpResolution(ninjaArmy, i, ui->shapeFilesMeshResolutionSpinBox->value(), ui->shapeFilesMeshResolutionComboBox->itemData(ui->shapeFilesMeshResolutionComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetShpResolution(ninjaArmy, i+10, ui->shapeFilesMeshResolutionSpinBox->value(), ui->shapeFilesMeshResolutionComboBox->itemData(ui->shapeFilesMeshResolutionComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetShpResolution(ninjaArmy, i, ui->shapeFilesMeshResolutionSpinBox->value(), "fudge", papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetShpResolution: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetPDFOutFlag(ninjaArmy, i, ui->geospatialPDFFilesGroupBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetPDFOutFlag(ninjaArmy, i+10, ui->geospatialPDFFilesGroupBox->isChecked(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetPDFOutFlag: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetPDFLineWidth(ninjaArmy, i, ui->geospatialPDFFilesVectorsSpinBox->value(), papszOptions); + //ninjaErr = NinjaSetPDFLineWidth(ninjaArmy, i+10, ui->geospatialPDFFilesVectorsSpinBox->value(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetPDFLineWidth: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetPDFBaseMap(ninjaArmy, i, ui->basemapComboBox->currentIndex(), papszOptions); + //ninjaErr = NinjaSetPDFBaseMap(ninjaArmy, i+10, ui->basemapComboBox->currentIndex(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetPDFBaseMap: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetPDFDEM(ninjaArmy, i, ui->elevationInputFileLineEdit->property("fullpath").toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetPDFDEM(ninjaArmy, i+10, ui->elevationInputFileLineEdit->property("fullpath").toString().toUtf8().constData(), papszOptions); // test error handling + ////ninjaErr = NinjaSetPDFDEM(ninjaArmy, i, "fudge", papszOptions); // test error handling // the dem string is not even checked + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetPDFDEM: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetPDFSize(ninjaArmy, i, PDFSize.PDFHeight, PDFSize.PDFWidth, PDFSize.PDFDpi, papszOptions); + //ninjaErr = NinjaSetPDFSize(ninjaArmy, i+10, PDFSize.PDFHeight, PDFSize.PDFWidth, PDFSize.PDFDpi, papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetPDFSize: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetPDFResolution(ninjaArmy, i, ui->geospatialPDFFilesMeshResolutionSpinBox->value(), ui->geospatialPDFFilesMeshResolutionComboBox->itemData(ui->geospatialPDFFilesMeshResolutionComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); + //ninjaErr = NinjaSetPDFResolution(ninjaArmy, i+10, ui->geospatialPDFFilesMeshResolutionSpinBox->value(), ui->geospatialPDFFilesMeshResolutionComboBox->itemData(ui->geospatialPDFFilesMeshResolutionComboBox->currentIndex()).toString().toUtf8().constData(), papszOptions); // test error handling // hrm, ninjaCom isn't triggering for this one, though the error returns, leading to it hanging without a proper message. + //ninjaErr = NinjaSetPDFResolution(ninjaArmy, i, ui->geospatialPDFFilesMeshResolutionSpinBox->value(), "fudge", papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetPDFResolution: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetVtkOutFlag(ninjaArmy, i, ui->VTKFilesCheckBox->isChecked(), papszOptions); + //ninjaErr = NinjaSetVtkOutFlag(ninjaArmy, i+10, ui->VTKFilesCheckBox->isChecked(), papszOptions); // test error handling + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetVtkOutFlag: ninjaErr =" << ninjaErr; + return false; + } + + if(ui->rawWeatherModelOutputCheckBox->isCheckable() && ui->rawWeatherModelOutputCheckBox->isChecked()) + { + ninjaErr = NinjaSetWxModelGoogOutFlag(ninjaArmy, i, ui->googleEarthCheckBox->isChecked(), papszOptions); + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetWxModelGoogOutFlag: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetWxModelShpOutFlag(ninjaArmy, i, ui->shapeFilesGroupBox->isChecked(), papszOptions); + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetWxModelShpOutFlag: ninjaErr =" << ninjaErr; + return false; + } + + ninjaErr = NinjaSetWxModelAsciiOutFlag(ninjaArmy, i, ui->fireBehaviorGroupBox->isChecked(), papszOptions); + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetWxModelAsciiOutFlag: ninjaErr =" << ninjaErr; + return false; + } + } + + return true; +} + +int MainWindow::startSolve(int numProcessors) +{ + char **papszOptions = nullptr; + return NinjaStartRuns(ninjaArmy, ui->numberOfProcessorsSpinBox->value(), papszOptions); +} + +void MainWindow::finishedSolve() +{ + // get the return value of the QtConcurrent::run() function + int result = futureWatcher->future().result(); + + // ninjaCom handles most of the progress dialog, cli, and console window messaging now + if( result == 1 ) // simulation properly finished + { + progressDialog->setValue(maxProgress); + progressDialog->setLabelText("Simulations finished"); + progressDialog->setCancelButtonText("Close"); + + qDebug() << "Finished with simulations"; + writeToConsole("Finished with simulations", Qt::darkGreen); + } + //else if( result == NINJA_E_CANCELLED ) // the proper way to do this, but checking progressDialog->wasCanceled() seems way safer + else if( progressDialog->wasCanceled() ) // simulation was cancelled + { + progressDialog->setValue(maxProgress); + progressDialog->setCancelButtonText("Close"); + } + else // simulation ended in some known error + { + progressDialog->setValue(maxProgress); + progressDialog->setCancelButtonText("Close"); + } + + disconnect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelSolve())); + + // one more process to do after finishedSolve() stuff + plotKmzOutputs(); + + char **papszOptions = nullptr; + int ninjaErr = NinjaDestroyArmy(ninjaArmy, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + printf("NinjaDestroyRuns: ninjaErr = %d\n", ninjaErr); + } + + // clear the progress values for the next set of runs + runProgress.clear(); + + futureWatcher->deleteLater(); +} + +void MainWindow::plotKmzOutputs() +{ + // get the return value of the QtConcurrent::run() function + int result = futureWatcher->future().result(); + + if(result == 1 && !progressDialog->wasCanceled() && ui->googleEarthCheckBox->isChecked() == true) + { + // enable QWebInspector for degugging the google maps widget + if(CSLTestBoolean(CPLGetConfigOption("ENABLE_QWEBINSPECTOR", "NO"))) + { + QWidget* inspectorWindow = new QWidget(this); + inspectorWindow->setWindowTitle("Web Inspector - Developer Tools"); + inspectorWindow->setMinimumSize(800, 600); + + QWebEngineView* inspectorView = new QWebEngineView(inspectorWindow); + inspectorView->page()->setInspectedPage(webEngineView->page()); + + QVBoxLayout* layout = new QVBoxLayout(inspectorWindow); + layout->addWidget(inspectorView); + layout->setContentsMargins(0, 0, 0, 0); + + inspectorWindow->show(); + } + + // vars to be filled + int numRuns = 0; + char **kmzFilenames = NULL; + int numStationKmls = 0; + char **stationKmlFilenames = NULL; + char **weatherModelKmzFilenames = NULL; + + char **papszOptions = nullptr; + ninjaErr = NinjaGetRunKmzFilenames(ninjaArmy, &numRuns, &kmzFilenames, &numStationKmls, &stationKmlFilenames, &weatherModelKmzFilenames, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + printf("NinjaGetRunKmzFilenames: ninjaErr = %d\n", ninjaErr); + } + + std::vector kmzFilenamesStr; + std::vector stationKmlFilenamesStr; + std::vector wxModelKmzFilenamesStr; + + kmzFilenamesStr.reserve(numRuns); + wxModelKmzFilenamesStr.reserve(numRuns); + for(int i = 0; i < numRuns; i++) + { + kmzFilenamesStr.emplace_back(kmzFilenames[i]); + wxModelKmzFilenamesStr.emplace_back(weatherModelKmzFilenames[i]); + } + + stationKmlFilenamesStr.reserve(numStationKmls); + for(int j = 0; j < numStationKmls; j++) + { + stationKmlFilenamesStr.emplace_back(stationKmlFilenames[j]); + } + + outputKmzFilenames.push_back(std::move( kmzFilenamesStr )); + outputStationKmlFilenames.push_back(std::move( stationKmlFilenamesStr )); + outputWxModelKmzFilenames.push_back(std::move( wxModelKmzFilenamesStr )); + + for(int i = 0; i < numRuns; i++) + { + // plot the output kmz of the run + QString outFileStr = QString::fromStdString(kmzFilenames[i]); + qDebug() << "kmz outFile =" << outFileStr; + QFile outFile(outFileStr); + + outFile.open(QIODevice::ReadOnly); + QByteArray data = outFile.readAll(); + QString base64 = data.toBase64(); + + bool timeSeries = !ui->domainAverageGroupBox->isChecked(); + + webEngineView->page()->runJavaScript( + "loadKmzFromBase64('"+base64+"', "+(timeSeries ? "true" : "false")+");" + ); + + // if it is a point initialization run, and station kmls were created for the run, + // plot the station kmls of the first run + // (first run, because station kmls are SHARED across runs) + // if(ui->pointInitializationGroupBox->isChecked() && ui->pointInitializationWriteStationKMLCheckBox->isChecked() && i == 0) + // { + // for(int j = 0; j < numStationKmls; j++) + // { + // QString outFileStr = QString::fromStdString(stationKmlFilenames[j]); + // qDebug() << "station kml outFile =" << outFileStr; + // QFile outFile(outFileStr); + + // outFile.open(QIODevice::ReadOnly); + // QByteArray data = outFile.readAll(); + // QString base64 = data.toBase64(); + + // webEngineView->page()->runJavaScript("loadKmzFromBase64('"+base64+"')"); + // } + // } + + // // if it is a weather model run, and weather model kmzs were created for the run, + // // plot the weather model kmz of the run + if(ui->weatherModelGroupBox->isChecked() && ui->googleEarthCheckBox->isChecked()) + { + QString outFileStr = QString::fromStdString(weatherModelKmzFilenames[i]); + qDebug() << "wx model kmz outFile =" << outFileStr; + QFile outFile(outFileStr); + + outFile.open(QIODevice::ReadOnly); + QByteArray data = outFile.readAll(); + QString base64 = data.toBase64(); + + webEngineView->page()->runJavaScript("loadOutputFromBase64('"+base64+"')"); + } + } + + ninjaErr = NinjaDestroyRunKmzFilenames(numRuns, kmzFilenames, numStationKmls, stationKmlFilenames, weatherModelKmzFilenames, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + printf("NinjaDestroyRunKmzFilenames: ninjaErr = %d\n", ninjaErr); + } + + } // if(result == 1 && !progressDialog->wasCanceled() && ui->googleEarthCheckBox->isChecked() == true) +} + +void MainWindow::writeSettings() +{ + writeToConsole("Saving settings..."); + + QSettings settings(QSettings::UserScope, "Firelab", "WindNinja"); + settings.setDefaultFormat(QSettings::IniFormat); + //qDebug() << "settings filename =" << settings.fileName(); + + //input file path + settings.setValue("inputFileDir", ui->elevationInputFileLineEdit->property("fullpath")); + + //momentum flag + settings.setValue("momentumFlag", ui->momentumSolverCheckBox->isChecked()); + //veg choice + settings.setValue("vegChoice", ui->vegetationComboBox->currentIndex()); + //mesh choice + settings.setValue("meshChoice", ui->meshResolutionComboBox->currentIndex()); + //mesh units + settings.setValue("meshUnits", ui->meshResolutionUnitsComboBox->currentIndex()); + //number of processors + settings.setValue("nProcessors", ui->numberOfProcessorsSpinBox->value()); + + //time zone + //settings.setValue("timeZone", ui->timeZoneComboBox->currentIndex()); + + //settings.setValue("pointFile", tree->point->stationFileName ); + + //if(ui->meshResolutionComboBox->currentIndex() == 3) // custom res + //{ + // settings.setValue("customRes", ui->meshResolutionSpinBox->value()); + //} + // need to write it every time, the past value will get left there without getting updated otherwise, doesn't delete past settings values + settings.setValue("customRes", ui->meshResolutionSpinBox->value()); + + writeToConsole("Settings saved."); +} + +void MainWindow::readSettings() +{ + QSettings settings(QSettings::UserScope, "Firelab", "WindNinja"); + settings.setDefaultFormat(QSettings::IniFormat); + + if(settings.contains("inputFileDir")) + { + if(QFile::exists(settings.value("inputFileDir").toString())) + { + ui->elevationInputFileLineEdit->setText(settings.value("inputFileDir").toString()); + } + } + else + { + // std::string oTmpPath = FindNinjaRootDir(); + // inputFileDir = CPLFormFilename(oTmpPath.c_str(), "etc/windninja/example-files", NULL); + } + + // TODO: some of the following might be overriding the values computed by inputFileDir, when the other way around might be better + if(settings.contains("momentumFlag")) + { + bool momentumFlag = settings.value("momentumFlag").toBool(); + if(momentumFlag == true) + { + ui->momentumSolverCheckBox->setChecked(true); + emit momentumSolverCheckBoxClicked(); + } + } + if(settings.contains("vegChoice")) + { + ui->vegetationComboBox->setCurrentIndex(settings.value("vegChoice").toInt()); + } + if(settings.contains("meshUnits")) // putting this after loading meshChoice results in overwriting the value by an extra set of units + { + ui->meshResolutionUnitsComboBox->setCurrentIndex(settings.value("meshUnits").toInt()); + } + if(settings.contains("meshChoice")) + { + int choice = settings.value("meshChoice").toInt(); + ui->meshResolutionComboBox->setCurrentIndex(choice); + if(choice == 3) + { + if(!settings.contains("customRes")) + { + qDebug() << "Error. WindNinja settings does not contain \"customRes\""; + } + ui->meshResolutionSpinBox->setValue(settings.value("customRes").toDouble()); + } + } + if(settings.contains("nProcessors")) + { + ui->numberOfProcessorsSpinBox->setValue(settings.value("nProcessors").toInt()); + } + // won't we want the timezone of the dem every time, to avoid accidentally doing a weird combination of time zones? + if(settings.contains("timeZone")) + { + // QString v = settings.value("timeZone").toString(); + // int index = tree->surface->timeZone->tzComboBox->findText(v); + // if(index == -1) + // tree->surface->timeZone->tzCheckBox->setChecked( true ); + // index = tree->surface->timeZone->tzComboBox->findText(v); + // if( index == 0 ) + // tree->surface->timeZone->tzComboBox->setCurrentIndex(index + 1); + // true->surface->timeZone->tzComboBox->setCurrentIndex(index); + } + else + { + // tree->surface->timeZone->tzComboBox->setCurrentIndex(2); + // tree->surface->timeZone->tzComboBox->setCurrentIndex(1); + } + if(settings.contains("pointFile")) + { + // QString f = settings.value("pointFile").toString(); + // tree->point->stationFileName = f; + } +} + +void MainWindow::showEvent(QShowEvent *event) +{ + QMainWindow::showEvent(event); + +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + writeSettings(); + QMainWindow::closeEvent(event); +} diff --git a/src/gui/qt6/mainWindow.h b/src/gui/qt6/mainWindow.h new file mode 100644 index 000000000..a7cd74518 --- /dev/null +++ b/src/gui/qt6/mainWindow.h @@ -0,0 +1,166 @@ +/****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Main Window that handles GUI scraping and state changes + * Author: Mason Willman + * + ****************************************************************************** + * + * 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 MAINWINDOW_H +#define MAINWINDOW_H + +#include "outputs.h" +#include "surfaceInput.h" +#include "menuBar.h" +#include "domainAverageInput.h" +#include "pointInitializationInput.h" +#include "mapBridge.h" +#include "serverBridge.h" +#include "weatherModelInput.h" +#include "ui_mainWindow.h" +#include "appState.h" +#include "windninja.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct OutputPDFSize { + double PDFHeight; + double PDFWidth; + double PDFDpi; +}; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + void toggleExpandCollapse(const QModelIndex &index); + void loadMapKMZ(const std::vector& input); + + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +signals: + void updateDirunalState(); + void updateStabilityState(); + void updateMetholodyState(); + void updateProgressValueSignal(int run, int progress); + void updateProgressMessageSignal(const QString &msg); + void writeToConsoleSignal(const QString &msg, QColor color=Qt::black); + +protected: + void showEvent(QShowEvent *event) override; + void closeEvent(QCloseEvent *event) override; + +private slots: + void massSolverCheckBoxClicked(); + void momentumSolverCheckBoxClicked(); + void diurnalCheckBoxClicked(); + void stabilityCheckBoxClicked(); + void treeWidgetItemSelectionChanged(); + void treeWidgetItemDoubleClicked(QTreeWidgetItem *item, int column); + void solveButtonClicked(); + void outputDirectoryButtonClicked(); + void writeToConsole(QString message, QColor color=Qt::black); + void updateProgressValue(int run, int progress); + void updateProgressMessage(const QString message); + void cancelSolve(); + +private: + Ui::MainWindow *ui; + QWebEngineView *webEngineView; + QWebChannel *webChannel; + MapBridge *mapBridge; + SurfaceInput *surfaceInput; + MenuBar *menuBar; + ServerBridge *serverBridge; + DomainAverageInput *domainAverageInput; + PointInitializationInput *pointInitializationInput; + WeatherModelInput *weatherModelInput; + Outputs *outputs; + + NinjaArmyH *ninjaArmy; + NinjaErr ninjaErr; + + std::vector runProgress; + int totalProgress; + int maxProgress; + + QProgressDialog *progressDialog; + QFutureWatcher *futureWatcher; + + int startSolve(int numProcessors); + void finishedSolve(); + void plotKmzOutputs(); + + std::vector> outputKmzFilenames; + std::vector> outputStationKmlFilenames; + std::vector> outputWxModelKmzFilenames; + + QString currentDEMFilePath; + + void connectSignals(); + bool prepareArmy(NinjaArmyH *ninjaArmy, int numNinjas, const char* initializationMethod); + bool setOutputFlags(NinjaArmyH* ninjaArmy, + int i, + int numNinjas, + OutputPDFSize PDFSize); + + int lineNumber; + + void writeSettings(); + void readSettings(); + void waitForLeaflet(); + +}; +#endif // MAINWINDOW_H diff --git a/src/gui/qt6/mainWindow.ui b/src/gui/qt6/mainWindow.ui new file mode 100755 index 000000000..ccfc557b7 --- /dev/null +++ b/src/gui/qt6/mainWindow.ui @@ -0,0 +1,6288 @@ + + + MainWindow + + + true + + + + 0 + 0 + 1969 + 1000 + + + + + 0 + 0 + + + + + 1000 + 800 + + + + + 1000 + 1000 + + + + + + + + + 0 + 0 + 0 + + + + + + + 246 + 245 + 244 + + + + + + + 255 + 255 + 255 + + + + + + + 250 + 250 + 249 + + + + + + + 123 + 122 + 122 + + + + + + + 164 + 163 + 163 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 246 + 245 + 244 + + + + + + + 0 + 0 + 0 + + + + + + + 233 + 84 + 32 + + + + + + + 255 + 255 + 255 + + + + + + + 250 + 250 + 249 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 233 + 84 + 32 + + + + + + + + + 0 + 0 + 0 + + + + + + + 246 + 245 + 244 + + + + + + + 255 + 255 + 255 + + + + + + + 250 + 250 + 249 + + + + + + + 123 + 122 + 122 + + + + + + + 164 + 163 + 163 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 246 + 245 + 244 + + + + + + + 0 + 0 + 0 + + + + + + + 233 + 84 + 32 + + + + + + + 255 + 255 + 255 + + + + + + + 250 + 250 + 249 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 233 + 84 + 32 + + + + + + + + + 123 + 122 + 122 + + + + + + + 246 + 245 + 244 + + + + + + + 255 + 255 + 255 + + + + + + + 250 + 250 + 249 + + + + + + + 123 + 122 + 122 + + + + + + + 164 + 163 + 163 + + + + + + + 123 + 122 + 122 + + + + + + + 255 + 255 + 255 + + + + + + + 123 + 122 + 122 + + + + + + + 246 + 245 + 244 + + + + + + + 246 + 245 + 244 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 246 + 245 + 244 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + 123 + 122 + 122 + + + + + + + 233 + 84 + 32 + + + + + + + + WindNinja + + + + :/wn-desktop.ico:/wn-desktop.ico + + + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + false + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Horizontal + + + 4 + + + false + + + + + 0 + 0 + + + + Qt::Orientation::Vertical + + + false + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 362 + + + + + 12 + + + + PointingHandCursor + + + + + + QFrame::Shape::StyledPanel + + + 0 + + + false + + + true + + + false + + + true + + + true + + + false + + + + Configuration + + + + + Solver Methodology + + + ItemIsSelectable|ItemIsEnabled + + + + Conservation of Mass + + + ItemIsSelectable|ItemIsEnabled + + + + + Conservation of Mass and Momentum + + + ItemIsSelectable|ItemIsEnabled + + + + + + Inputs + + + ItemIsSelectable|ItemIsEnabled + + + + Surface Input + + + ItemIsSelectable|ItemIsEnabled + + + + + Diurnal Input + + + ItemIsSelectable|ItemIsEnabled + + + + + Stability Input + + + ItemIsSelectable|ItemIsEnabled + + + + + Wind Input + + + ItemIsSelectable|ItemIsEnabled + + + + Domain Average Wind + + + ItemIsSelectable|ItemIsEnabled + + + + + Point Initialization + + + ItemIsSelectable|ItemIsEnabled + + + + + Weather Model + + + ItemIsSelectable|ItemIsEnabled + + + + + + + Outputs + + + + Google Earth + + + + + Fire Behavior + + + + + Shape Files + + + + + Geospatial PDF Files + + + + + VTK Files + + + + + + Solver + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + 0 + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Sunken + + + 10 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 6 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + PointingHandCursor + + + Use Conservation of Mass + + + false + + + + + + + This is the native WindNinja solver. It solves a conservation of mass equation, but not a conservation of momentum equation. This solver is fast-running, but may give less accurate wind predictions in regions where momentum effects are important, for example on the lee side of terrain obstacles. + + + false + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + PointingHandCursor + + + Use Conservation of Mass and Momentum + + + + + + + This solver conserves both mass and momentum. It is based on the OpenFOAM CFD toolbox. This solver should give more accurate wind predictions in regions where momentum effects are important, such as on the lee side of terrain obstacles. Because this solver is more computationally intensive than the conservation of mass solver, simulation times will be longer. Typical simulation times for this solver range from 10-30 minutes, but will depend on your domain, resolution, and computational resources. Note that some options (e.g., point initialization and non-neutral stability) are not available for this solver at this time. We plan to make these options available in future releases. + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::NoFrame + + + true + + + + + 0 + 0 + 530 + 385 + + + + + 0 + 0 + + + + + 6 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Elevation Input File + + + false + + + false + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + true + + + *.tif + + + + + + + PointingHandCursor + + + Open File + + + + :/folder_page.png:/folder_page.png + + + + + + + PointingHandCursor + + + Download File + + + + :/server_go.png:/server_go.png + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Vegetation + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + PointingHandCursor + + + + Grass + + + + + Brush + + + + + Trees + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + <html><head/><body><p>Vegetation Data Set Using LCP File</p></body></html> + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Mesh Resolution + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + PointingHandCursor + + + + Coarse + + + + + Medium + + + + + Fine + + + + + Custom + + + + + + + + false + + + + 0 + 0 + + + + PointingHandCursor + + + false + + + 0.010000000000000 + + + 10000.000000000000000 + + + 200.000000000000000 + + + + + + + + Meters + + + + + Feet + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Time Zone + + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PointingHandCursor + + + + + + -1 + + + 10 + + + QComboBox::SizeAdjustPolicy::AdjustToContentsOnFirstShow + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + Show All Zones + + + true + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + Display Time Zone Details + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 90 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Standard Name: Mountain Standard Time</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Daylight Name: Mountain Daylight Time</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Standard Offset from UTC: -07:00:00</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Daylight Offset from UTC: -06:00:00</p></body></html> + + + Qt::TextInteractionFlag::NoTextInteraction + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Expanding + + + + 20 + 40 + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + + + QLayout::SizeConstraint::SetNoConstraint + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + PointingHandCursor + + + Use Diurnal Wind + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Expanding + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + PointingHandCursor + + + Use Stability + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::NoFrame + + + 1 + + + true + + + + + 0 + 0 + 730 + 556 + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + Domain Average Wind + + + true + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 100 + + + + Input Wind Height + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + PointingHandCursor + + + + 20ft-US + + + + + 10m-SI + + + + + Custom + + + + + + + + false + + + PointingHandCursor + + + 0.000000000000000 + + + 10000.000000000000000 + + + 20.000000000000000 + + + + + + + false + + + + 0 + 0 + + + + false + + + + Feet + + + + + Meters + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + 0 + 0 + + + + Table Units: + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + PointingHandCursor + + + + mph + + + + + m/s + + + + + kph + + + + + kts + + + + + + + + + 0 + 0 + + + + + 40 + 0 + + + + + 40 + 16777215 + + + + PointingHandCursor + + + Qt::LayoutDirection::LeftToRight + + + + °F + + + + + °C + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + Clear Table + + + + :/cancel.png:/cancel.png + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 621 + + + + QFrame::Shape::StyledPanel + + + true + + + QAbstractItemView::DragDropMode::DragDrop + + + Qt::DropAction::MoveAction + + + true + + + false + + + true + + + false + + + 30 + + + Qt::DropAction::IgnoreAction + + + true + + + false + + + 80 + + + 80 + + + true + + + false + + + 20 + + + 20 + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Speed + + + + 10 + + + + + + Direction + + + + + + + 10 + + + + + + Time + + + + 10 + + + + + + Date + + + + 10 + + + + + + Cloud Cover (%) + + + + 10 + + + + + + Air Temp + + + + 10 + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::NoFrame + + + true + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTop|Qt::AlignmentFlag::AlignTrailing + + + + + 0 + 0 + 730 + 556 + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Point Initialization + + + true + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + + + 0 + 0 + + + + Select Weather Stations + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 140 + 16777215 + + + + PointingHandCursor + + + Download Data + + + + :/server_go.png:/server_go.png + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + 0 + + + + QFrame::Shadow::Sunken + + + true + + + 10 + + + 300 + + + true + + + false + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Select All + + + + + + + + 0 + 0 + + + + Select None + + + + + + + + + + 0 + 0 + + + + Qt::Orientation::Horizontal + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + 1 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + 0 + + + + + + 0 + 0 + + + + Stop Time + + + + + + + false + + + + 0 + 0 + 0 + 2000 + 1 + 1 + + + + M/d/yy h:mm + + + true + + + + + + + + 0 + 0 + + + + Number of Time Steps + + + + + + + + 0 + 0 + + + + Start Time + + + + + + + false + + + 1 + + + 99999 + + + 1 + + + 24 + + + + + + + false + + + PointingHandCursor + + + M/d/yy h:mm + + + true + + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Placeholder Text + + + + + + + + + + + + 0 + 0 + + + + Current Time Zone: + + + + + + + + 0 + 0 + + + + Current Min Time: + + + + + + + + 0 + 0 + + + + Current Max Time: + + + + + + + + 0 + 0 + + + + Qt::Orientation::Horizontal + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + Write Station KML + + + + :/weather_cloudy.png:/weather_cloudy.png + + + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::NoFrame + + + true + + + + + 0 + 0 + 730 + 556 + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + Weather Model Initialization + + + true + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Download Weather Data + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + PointingHandCursor + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + + + + hours + + + 1 + + + 6 + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + Download + + + + :/server_go.png:/server_go.png + + + + + + + + + + + 0 + 0 + + + + Minimum Pastcast Date: 07/30/2014 18:00 + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + M/d/yy h:mm  + + + true + + + Qt::TimeSpec::LocalTime + + + + + + + M/d/yy h:mm  + + + true + + + Qt::TimeSpec::LocalTime + + + + + + + + + + + 0 + 0 + + + + Downloaded Forecasts + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + true + + + + + + + + 0 + 0 + + + + Select Specific Time Steps + + + true + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + true + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Select All + + + + + + + Select None + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 100 + + + + + 0 + 70 + + + + Output Wind Height + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + PointingHandCursor + + + + 20ft-US + + + + + 10m-SI + + + + + Custom + + + + + + + + false + + + PointingHandCursor + + + 0.000000000000000 + + + 10000.000000000000000 + + + 20.000000000000000 + + + + + + + false + + + + 0 + 0 + + + + false + + + + Feet + + + + + Meters + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Output Speed Units: + + + + + + + + mph + + + + + mps + + + + + kph + + + + + kts + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Clip output by: + + + + + + + % + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + false + + + + 0 + 0 + + + + Write Raw Weather Model Output + + + true + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::Shape::NoFrame + + + true + + + + + 0 + 0 + 730 + 556 + + + + + 0 + 0 + + + + Qt::LayoutDirection::LeftToRight + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + NOTE: Version 4.0.0 requires Google Earth (KML/KMZ) files to display simulations on the map. This requirement will be removed in version 4.0.1. + + + true + + + + + + + false + + + + 0 + 0 + + + + Create Google Earth Files (*.kmz) + + + true + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Legend + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 131 + 0 + + + + + Uniform Range + + + + + Equal Count + + + + + + + + Use Consistent Color Scale + + + + + + + Qt::Orientation::Horizontal + + + + 153 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Vectors + + + + QLayout::SizeConstraint::SetNoConstraint + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Line Width: + + + + + + + 1 + + + 10.000000000000000 + + + 1.000000000000000 + + + + + + + Apply Vector Scaling + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Resolution + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 70 + + + + Use Mesh Resolution + + + true + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + 5000.000000000000000 + + + 200.000000000000000 + + + + + + + false + + + + 0 + 0 + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + + Meters + + + + + Feet + + + + + + + + Qt::Orientation::Horizontal + + + + 302 + 20 + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Alternative Color Schemes + + + true + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + Default + + + + + ROPGW (Red Orange Pink Green White) + + + + + Oranges + + + + + Blues + + + + + Pinks + + + + + Greens + + + + + Magic Beans + + + + + Pink to Green + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Create Fire Behavior Files (*.asc) + + + true + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 0 + 114 + + + + Resolution + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 70 + + + + Use Mesh Resolution + + + true + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + 5000.000000000000000 + + + 200.000000000000000 + + + + + + + false + + + + 0 + 0 + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + + Meters + + + + + Feet + + + + + + + + Qt::Orientation::Horizontal + + + + 302 + 20 + + + + + + + + + + + Create *.atm file(s) + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Create Shape Files (*.sph) + + + true + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Resolution + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 70 + + + + Use Mesh Resolution + + + true + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + 5000.000000000000000 + + + 200.000000000000000 + + + + + + + false + + + + 0 + 0 + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + + Meters + + + + + Feet + + + + + + + + Qt::Orientation::Horizontal + + + + 302 + 20 + + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::NoFrame + + + true + + + + + 0 + 0 + 730 + 556 + + + + + 6 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Create Geospatial PDF Files (*.pdf) + + + true + + + false + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Vectors + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Line Width: + + + + + + + 1 + + + 10.000000000000000 + + + 1.000000000000000 + + + + + + + Apply Vector Scaling + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Basemap: + + + + + + + + 0 + 0 + + + + + 86 + 0 + + + + + 170 + 16777215 + + + + + TopoFire topo maps + + + + + Hillshade + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Size: + + + + + + + + 0 + 0 + + + + + 160 + 16777215 + + + + + Letter-8 1/2 x 11 + + + + + Legal-8 1/2 x 14 + + + + + Tabloid-7 11 x 17 + + + + + + + + + Portrait + + + + + Landscape + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Resolution + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Use Mesh Resolution + + + true + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + + 0 + 0 + + + + 5000.000000000000000 + + + 200.000000000000000 + + + + + + + false + + + + 0 + 0 + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + + Meters + + + + + Feet + + + + + + + + Qt::Orientation::Horizontal + + + + 302 + 20 + + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Create VTK Files (*.vtk) + + + + + + + Write VTK surface and volume files + + + + + + + + Note that *vtk files are for advanced users and are rarely used by fire managers/modelers + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + QLayout::SizeConstraint::SetDefaultConstraint + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Placeholder Text + + + true + + + + + + + + + Number of Processors: + + + + + + + PointingHandCursor + + + 1 + + + + + + + false + + + + 0 + 0 + + + + PointingHandCursor + + + Solve + + + + :/cog_go.png:/cog_go.png + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + Output Directory + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + true + + + + + + + PointingHandCursor + + + Save output in... + + + + :/folder.png:/folder.png + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Elevation Input Type + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + + + 0 + 0 + + + + + Bounding Box + + + + + Point Radius + + + + + + + + + 0 + 0 + + + + + WORLD SRTM (30m) + + + + + WORLD GMTED (250m) + + + + + Landscape File + + + + + + + + Draw + + + + :/center_click_final.png:/center_click_final.png + + + true + + + false + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + North + + + + + + + + + + South + + + + + + + + + + East + + + + + + + + + + West + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Center Lat + + + + + + + + + + Center Lon + + + + + + + + + + + 0 + 0 + + + + Radius (miles) + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Download File + + + + :/server_go.png:/server_go.png + + + + + + + + 0 + 0 + + + + Cancel + + + + :/cancel.png:/cancel.png + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Expanding + + + + 20 + 40 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Fetch Station Data + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + + 200 + 16777215 + + + + PointingHandCursor + + + + Download from DEM + + + + + Download by Station ID + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Buffer Around DEM + + + + + + + PointingHandCursor + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + + mi + + + + + km + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + 16777215 + 16777215 + + + + SizeBDiagCursor + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Input Station IDs + + + + + + + KMSO, PNTM8 + + + false + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 250 + 16777215 + + + + PointingHandCursor + + + + Download Most Recent Data + + + + + Download Between Two Dates + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + 0 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Download the most recent weather data for a single time step simulation + + + + + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + + + 0 + 0 + + + + Start Time: + + + + + + + M/d/yy h:mm  + + + + + + + + 0 + 0 + + + + End Time: + + + + + + + M/d/yy h:mm  + + + + + + + Qt::Orientation::Horizontal + + + + 51 + 20 + + + + + + + + + + + 0 + 0 + + + + Current Time Zone: + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Download Data + + + + :/server_go.png:/server_go.png + + + + + + + + 0 + 0 + + + + + 80 + 16777215 + + + + Cancel + + + + :/cancel.png:/cancel.png + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 150 + + + + true + + + + + + + 1 + 0 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + 0 + 0 + 1969 + 22 + + + + + &File + + + true + + + + + + + + + + &Options + + + true + + + + + + + &Tools + + + true + + + + + + + &Help + + + true + + + + &Displaying Shapefiles + + + true + + + + + + &Tutorials + + + true + + + + + + + + + &Instructions + + + true + + + + + + + + + + + + + + + + + + + + + + &New Project + + + Open New Project + + + + + &Open Project + + + Open Existing Project + + + + + &Export Solution + + + Save Output From Current Project + + + + + &Close Project + + + Close Current Project + + + + + + :/cancel.png:/cancel.png + + + E&xit WindNinja + + + Exit WindNinja + + + Alt+F4 + + + + + true + + + true + + + &Enable Console Output + + + Show or Hide the Console Output Window + + + + + + :/disk.png:/disk.png + + + &Write Console Output + + + Write the console text to disk + + + Ctrl+W + + + + + + :/disk.png:/disk.png + + + &Write Blank Station File + + + Write a blank station file for point initialization + + + Ctrl+Alt+W + + + + + + :/cog_go.png:/cog_go.png + + + Set &Configuration Option + + + Set advanced runtime configuration options + + + Ctrl+Alt+O + + + + + + :/page_white_acrobat.png:/page_white_acrobat.png + + + &How to Display Shapefiles in ArcGIS Pro + + + Open ArcGIS Pro Tutorial + + + + + + :/page_white_acrobat.png:/page_white_acrobat.png + + + Tutorial &1: The Basics + + + Get started using WindNinja + + + Ctrl+1 + + + + + + :/page_white_acrobat.png:/page_white_acrobat.png + + + Tutorial &2: Diurnal Winds and Non-Neutral Stability + + + Using Diurnal Winds in WindNinja + + + Ctrl+2 + + + + + + :/page_white_acrobat.png:/page_white_acrobat.png + + + Tutorial &3: Point Initialization + + + Using Point Initialization in WindNinja + + + Ctrl+3 + + + + + + :/page_white_acrobat.png:/page_white_acrobat.png + + + Tutorial &4: Weather Model Initialization + + + Using Weather Model Initialization in WindNinja + + + Ctrl+4 + + + + + + :/page_white_acrobat.png:/page_white_acrobat.png + + + &DEM Download Instructions + + + How to download DEM data with WindNinja + + + + + + :/page_white_acrobat.png:/page_white_acrobat.png + + + &fetch_dem Instructions + + + How to download DEM data with fetch_dem + + + + + + :/page_white_acrobat.png:/page_white_acrobat.png + + + &Command Line Interface Instructions + + + Using the Command Line Interface + + + Ctrl+L + + + + + + :/help.png:/help.png + + + &About WindNinja + + + Show About WindNinja Info + + + Ctrl+A + + + + + + :/citation.png:/citation.png + + + &Citation + + + How to cite WindNinja + + + Ctrl+T + + + + + + :/email.png:/email.png + + + &Email Us + + + Email bugs/comments/questions to the WindNinja team + + + Ctrl+E + + + + + + :/bug_link.png:/bug_link.png + + + Submit &Bug Report + + + Submit a bug report via GitHub (requires GitHub ID) + + + Ctrl+B + + + + + + + + About &Qt + + + Show the Qt library's About box + + + + + + + + diff --git a/src/gui/outputHeightWidget.h b/src/gui/qt6/mapBridge.cpp similarity index 55% rename from src/gui/outputHeightWidget.h rename to src/gui/qt6/mapBridge.cpp index 1c8c30d63..ef2d2840d 100644 --- a/src/gui/outputHeightWidget.h +++ b/src/gui/qt6/mapBridge.cpp @@ -1,20 +1,20 @@ -/****************************************************************************** + /****************************************************************************** * * $Id$ * * Project: WindNinja Qt GUI - * Purpose: Output wind option widget - * Author: Kyle Shannon + * Purpose: Connects the Qt GUI to the map.html using a Bridge + * Author: Mason Willman * ****************************************************************************** * * 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, + * 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 @@ -27,37 +27,32 @@ * *****************************************************************************/ -#ifndef OUTPUTHEIGHTWIDGET_H -#define OUTPUTHEIGHTWIDGET_H +#include "mapBridge.h" -#include -#include -#include -#include -#include -#include +void MapBridge::receiveBoundingBox(const QString &jsonCoords) + { + QJsonDocument doc = QJsonDocument::fromJson(jsonCoords.toUtf8()); + if (!doc.isObject()) + { + qWarning() << "Invalid bounding box JSON"; + return; + } -class outputHeightWidget : public QWidget -{ - Q_OBJECT + QJsonObject obj = doc.object(); - public: - outputHeightWidget(QWidget *parent = 0); - - //one lonsome widget needs to be places for "output" part in tree - //widget for output wind height - - QGroupBox *outputHeightGroupBox; + double north = obj["north"].toDouble(); + double south = obj["south"].toDouble(); + double east = obj["east"].toDouble(); + double west = obj["west"].toDouble(); - QComboBox *outputHeightComboBox; - QDoubleSpinBox *outputHeightDoubleSpinBox; - QRadioButton *feetRadioButton; - QRadioButton *meterRadioButton; + qDebug() << "Bounding box received:"; + qDebug() << "North:" << north << "South:" << south; + qDebug() << "East:" << east << "West:" << west; - QHBoxLayout *layout; - QVBoxLayout *mainLayout; - public slots: - void checkOutputHeight(int choice); -}; + emit boundingBoxReceived(north, south, east, west); +} -#endif /* OUTPUTHEIGHTWIDGET_H */ +void MapBridge::notifyReady() +{ + emit ready(); +} diff --git a/src/gui/windInput.h b/src/gui/qt6/mapBridge.h similarity index 62% rename from src/gui/windInput.h rename to src/gui/qt6/mapBridge.h index 8c3243b01..a67299a5b 100644 --- a/src/gui/windInput.h +++ b/src/gui/qt6/mapBridge.h @@ -1,20 +1,20 @@ -/****************************************************************************** + /****************************************************************************** * * $Id$ * * Project: WindNinja Qt GUI - * Purpose: Handles options for wind inputs - * Author: Kyle Shannon + * Purpose: Connects the Qt GUI to the map.html using a Bridge + * Author: Mason Willman * ****************************************************************************** * * 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, + * 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 @@ -27,35 +27,30 @@ * *****************************************************************************/ -#ifndef WINDINPUT_H -#define WINDINPUT_H +#ifndef BRIDGE_H +#define BRIDGE_H -#include -#include +#include +#include +#include +#include -#include "metaWindWidget.h" -#include "windInputTable.h" - -class windInput : public QWidget +class MapBridge : public QObject { - Q_OBJECT - - public: - windInput(QWidget *parent = 0); - - QGroupBox *windGroupBox; + Q_OBJECT - metaWindWidget *metaWind; - windInputTable *windTable; +public: + MapBridge(QObject *parent = nullptr) : QObject(parent) {} - QToolButton *clearButton; +signals: + void boundingBoxReceived(double north, double south, double east, double west); + void ready(); - QScrollArea *scrollArea; - QHBoxLayout *clearLayout; - QVBoxLayout *windLayout; - QVBoxLayout *mainLayout; +public slots: + void receiveBoundingBox(const QString &jsonCoords); + void notifyReady(); }; -#endif /* WINDINPUT_H */ +#endif // BRIDGE_H diff --git a/src/gui/qt6/menuBar.cpp b/src/gui/qt6/menuBar.cpp new file mode 100755 index 000000000..76247634a --- /dev/null +++ b/src/gui/qt6/menuBar.cpp @@ -0,0 +1,395 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Hands GUI related logic for the Menu Bar + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "menuBar.h" + +MenuBar::MenuBar(Ui::MainWindow* ui, QObject* parent) + : QObject(parent), ui(ui) +{ + char ** options; + const char * tmp = NinjaFindBinDir(options); + dataPath = QDir(QString::fromUtf8(tmp)); + + // QMenu fileMenu "File" actions + connect(ui->newProjectAction, &QAction::triggered, this, &MenuBar::newProjectActionTriggered); + connect(ui->openProjectAction, &QAction::triggered, this, &MenuBar::openProjectActionTriggered); + connect(ui->exportSolutionAction, &QAction::triggered, this, &MenuBar::exportSolutionActionTriggered); + connect(ui->closeProjectAction, &QAction::triggered, this, &MenuBar::closeProjectActionTriggered); + connect(ui->exitWindNinjaAction, &QAction::triggered, this, &QCoreApplication::quit); // exit the entire app + + // QMenu optionsMenu "Options" actions + //connect(ui->enableConsoleOutputAction, &QAction::toggled, ui->consoleDockWidget, &QDockWidget::setVisible); + //connect(ui->consoleDockWidget, &QDockWidget::visibilityChanged, ui->enableConsoleOutputAction, &QAction::setChecked); // if closed from the QDockWidget itself, unchecks the menuAction + connect(ui->writeConsoleOutputAction, &QAction::triggered, this, &MenuBar::writeConsoleOutputActionTriggered); + + // QMenu toolsMenu "Tools" actions + //connect(ui->resampleDataAction, &QAction::triggered, this, &MenuBar::resampleDataActionTriggered); + connect(ui->writeBlankStationFileAction, &QAction::triggered, this, &MenuBar::writeBlankStationFileActionTriggered); + connect(ui->setConfigurationOptionAction, &QAction::triggered, this, &MenuBar::setConfigurationOptionActionTriggered); + + // QMenu helpMenu "Help" actions + // sub QMenu displayingShapeFilesMenu "Displaying Shapefiles" actions + connect(ui->displayArcGISProGuideAction, &QAction::triggered, this, &MenuBar::displayArcGISProGuideActionTriggered); + + // sub QMenu tutorialsMenu "Tutorials" actions + connect(ui->displayTutorial1Action, &QAction::triggered, this, &MenuBar::displayTutorial1ActionTriggered); + connect(ui->displayTutorial2Action, &QAction::triggered, this, &MenuBar::displayTutorial2ActionTriggered); + connect(ui->displayTutorial3Action, &QAction::triggered, this, &MenuBar::displayTutorial3ActionTriggered); + connect(ui->displayTutorial4Action, &QAction::triggered, this, &MenuBar::displayTutorial4ActionTriggered); + + // sub QMenu instructionsMenu "Instructions" actions + connect(ui->displayDemDownloadInstructionsAction, &QAction::triggered, this, &MenuBar::displayDemDownloadInstructionsActionTriggered); + connect(ui->displayFetchDemInstructionsAction, &QAction::triggered, this, &MenuBar::displayFetchDemInstructionsActionTriggered); + connect(ui->displayCommandLineInterfaceInstructionsAction, &QAction::triggered, this, &MenuBar::displayCommandLineInterfaceInstructionsActionTriggered); + + // remaining non-sub QMenu actions + connect(ui->aboutWindNinjaAction, &QAction::triggered, this, &MenuBar::aboutWindNinjaActionTriggered); + connect(ui->citeWindNinjaAction, &QAction::triggered, this, &MenuBar::citeWindNinjaActionTriggered); + connect(ui->supportEmailAction, &QAction::triggered, this, &MenuBar::supportEmailActionTriggered); + connect(ui->submitBugReportAction, &QAction::triggered, this, &MenuBar::submitBugReportActionTriggered); + connect(ui->aboutQtAction, &QAction::triggered, this, &QApplication::aboutQt); + connect(ui->enableConsoleOutputAction, &QAction::toggled, this, &MenuBar::enableConsoleOutputActionToggled); +} + +void MenuBar::newProjectActionTriggered() +{ + emit writeToConsoleSignal("MenuBar: newProject() triggered"); +} + +void MenuBar::openProjectActionTriggered() +{ + emit writeToConsoleSignal("MenuBar: openProject() triggered"); +} + +void MenuBar::exportSolutionActionTriggered() +{ + emit writeToConsoleSignal("MenuBar: exportSolution() triggered"); +} + +void MenuBar::closeProjectActionTriggered() +{ + emit writeToConsoleSignal("MenuBar: closeProject() triggered"); +} + +void MenuBar::writeConsoleOutputActionTriggered() +{ + QString fileName = QFileDialog::getSaveFileName( + ui->centralwidget, + tr("Save Console Output"), + "console-output.txt", + tr("Text Files (*.txt)") + ); + + if (!fileName.isEmpty()) + { + QDateTime currentTime = QDateTime::currentDateTime(); + emit writeToConsoleSignal("writing console output to " + fileName, Qt::darkGreen); + emit writeToConsoleSignal("current time is " + currentTime.toString("MM/dd/yyyy hh:mm:ss t"), Qt::darkGreen); + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + emit writeToConsoleSignal("Cannot open " + fileName + " for writing.", Qt::red); + return; + } + + QTextStream out(&file); + out << ui->consoleTextEdit->toPlainText(); + } +} + +void MenuBar::writeBlankStationFileActionTriggered() +{ + QString fileName = QFileDialog::getSaveFileName( + ui->centralwidget, + tr("Save Blank Station File"), + "stations.csv", + tr("Text Files (*.csv)") + ); + + if(!fileName.isEmpty()) + { + emit writeToConsoleSignal("writing blank station file to " + fileName, Qt::darkGreen); + + char** papszOptions = nullptr; + int ninjaErr = NinjaWriteBlankWxStationFile(fileName.toStdString().c_str(), papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaWriteBlankWxStationFile: ninjaErr=" << ninjaErr; + emit writeToConsoleSignal("failed to write blank station file!", Qt::red); + } + } +} + +void MenuBar::setConfigurationOptionActionTriggered() +{ + setConfigurationOptionDialog configDialog; + + if (configDialog.exec() == QDialog::Rejected) + return; + + QString key = configDialog.getKey(); + QString val = configDialog.getValue(); + + if (key.isEmpty()) + return; + + qDebug() << "Setting configuration option "+key+"="+val; + emit writeToConsoleSignal("Setting configuration option " + key + "=" + val); + + CPLSetConfigOption( + key.toUtf8().constData(), + val.isEmpty() ? nullptr : val.toUtf8().constData() + ); +} + +void MenuBar::displayArcGISProGuideActionTriggered() +{ + QString displayFile = dataPath.absoluteFilePath("../share/windninja/doc/displaying_wind_vectors_in_ArcGIS_Pro.pdf"); + displayFile = QDir::cleanPath(displayFile); + + emit writeToConsoleSignal("Opening " + displayFile); + if(!QDesktopServices::openUrl(QUrl(displayFile))) + { + QMessageBox::warning( + ui->centralwidget, + tr("Broken Link."), + tr("The link to the tutorial is broken, you can get to it through the Start Menu."), + QMessageBox::Ok + ); + } +} +void MenuBar::displayTutorial1ActionTriggered() +{ + QString displayFile = dataPath.absoluteFilePath("../share/windninja/doc/tutorials/WindNinja_tutorial1.pdf"); + displayFile = QDir::cleanPath(displayFile); + + emit writeToConsoleSignal("Opening " + displayFile); + if (!QDesktopServices::openUrl(QUrl::fromLocalFile(displayFile))) + { + QMessageBox::warning( + ui->centralwidget, + tr("Broken Link"), + tr("The link to the tutorial is broken. You can access it through the Start Menu."), + QMessageBox::Ok + ); + } +} + +void MenuBar::displayTutorial2ActionTriggered() +{ + QString displayFile = dataPath.absoluteFilePath("../share/windninja/doc/tutorials/WindNinja_tutorial2.pdf"); + displayFile = QDir::cleanPath(displayFile); + + emit writeToConsoleSignal("Opening " + displayFile); + if (!QDesktopServices::openUrl(QUrl::fromLocalFile(displayFile))) + { + QMessageBox::warning( + ui->centralwidget, + tr("Broken Link"), + tr("The link to the tutorial is broken. You can access it through the Start Menu."), + QMessageBox::Ok + ); + } +} + +void MenuBar::displayTutorial3ActionTriggered() +{ + QString displayFile = dataPath.absoluteFilePath("../share/windninja/doc/tutorials/WindNinja_tutorial3.pdf"); + displayFile = QDir::cleanPath(displayFile); + + emit writeToConsoleSignal("Opening " + displayFile); + if (!QDesktopServices::openUrl(QUrl::fromLocalFile(displayFile))) + { + QMessageBox::warning( + ui->centralwidget, + tr("Broken Link"), + tr("The link to the tutorial is broken. You can access it through the Start Menu."), + QMessageBox::Ok + ); + } +} + +void MenuBar::displayTutorial4ActionTriggered() +{ + QString displayFile = dataPath.absoluteFilePath("../share/windninja/doc/tutorials/WindNinja_tutorial4.pdf"); + displayFile = QDir::cleanPath(displayFile); + + emit writeToConsoleSignal("Opening " + displayFile); + if (!QDesktopServices::openUrl(QUrl::fromLocalFile(displayFile))) + { + QMessageBox::warning( + ui->centralwidget, + tr("Broken Link"), + tr("The link to the tutorial is broken. You can access it through the Start Menu."), + QMessageBox::Ok + ); + } +} + +void MenuBar::displayDemDownloadInstructionsActionTriggered() +{ + QString displayFile = dataPath.absoluteFilePath("../share/windninja/doc/download_elevation_file.pdf"); + displayFile = QDir::cleanPath(displayFile); + + emit writeToConsoleSignal("Opening " + displayFile); + if (!QDesktopServices::openUrl(QUrl::fromLocalFile(displayFile))) + { + QMessageBox::warning( + ui->centralwidget, + tr("Broken Link"), + tr("The link to the tutorial is broken. You can access it through the Start Menu."), + QMessageBox::Ok + ); + } +} + +void MenuBar::displayFetchDemInstructionsActionTriggered() +{ + QString displayFile = dataPath.absoluteFilePath("../share/windninja/doc/fetch_dem_instructions.pdf"); + displayFile = QDir::cleanPath(displayFile); + + emit writeToConsoleSignal("Opening " + displayFile); + if (!QDesktopServices::openUrl(QUrl::fromLocalFile(displayFile))) + { + QMessageBox::warning( + ui->centralwidget, + tr("Broken Link"), + tr("The link to the tutorial is broken. You can access it through the Start Menu."), + QMessageBox::Ok + ); + } +} + +void MenuBar::displayCommandLineInterfaceInstructionsActionTriggered() +{ + QString displayFile = dataPath.absoluteFilePath("../share/windninja/doc/CLI_instructions.pdf"); + displayFile = QDir::cleanPath(displayFile); + + emit writeToConsoleSignal("Opening " + displayFile); + if (!QDesktopServices::openUrl(QUrl::fromLocalFile(displayFile))) + { + QMessageBox::warning( + ui->centralwidget, + tr("Broken Link"), + tr("The link to the tutorial is broken. You can access it through the Start Menu."), + QMessageBox::Ok + ); + } +} + +void MenuBar::aboutWindNinjaActionTriggered() +{ + QString aboutText = "

WindNinja

\n"; + + aboutText.append("

Version:

" + QString(NINJA_VERSION_STRING) + "

"); + + aboutText.append("

Git Commit:

" + QString(NINJA_SCM_VERSION) + "

"); + + aboutText.append("

Release Date:

" + QString(NINJA_RELEASE_DATE) + "

"); + + aboutText.append("

Developed by:

Jason Forthofer
" \ + "Natalie Wagenbrenner
" \ + "Kyle Shannon
" \ + "Loren Atwood
" \ + "Mason Willman"); \ + + aboutText.append("

Missoula Fire Sciences Laboratory
"); + aboutText.append("Rocky Mountain Research Station
"); + aboutText.append("USDA Forest Service
"); + aboutText.append("5775 Highway 10 W.
"); + aboutText.append("Missoula, MT 59808

"); + + aboutText.append("

Contributors

"); + aboutText.append("

Sponsored By:

"); + aboutText.append("USDA Forest Service
"); + aboutText.append("Center for Environmental Management of Military Lands at Colorado State University
"); + aboutText.append("Joint Fire Sciences Program
"); + aboutText.append("Washington State University

"); + aboutText.append("

Special Thanks

"); + aboutText.append("
"); + + QMessageBox::about( + ui->centralwidget, + tr("About WindNinja"), + aboutText + ); +} + +void MenuBar::citeWindNinjaActionTriggered() +{ + QString citeText = "

To cite WindNinja in a publication use:

"; + + citeText.append("Forthofer, J.M., Butler, B.W., Wagenbrenner, N.S., 2014. A comparison "); + citeText.append("of three approaches for simulating fine-scale surface winds in "); + citeText.append("support of wildland fire management. Part I. Model formulation and "); + citeText.append("comparison against measurements. International Journal of Wildland "); + citeText.append("Fire, 23:969-931. doi: 10.1071/WF12089."); + + citeText.append("

For additional WindNinja publications visit:

"); + + citeText.append("

https://ninjastorm.firelab.org/windninja/publications

"); + + QMessageBox::about( + ui->centralwidget, + tr("Cite WindNinja"), + citeText + ); +} + +void MenuBar::supportEmailActionTriggered() +{ + QUrl mailto("mailto:wind.ninja.support@gmail.com?subject=[windninja-support]"); + if (!QDesktopServices::openUrl(mailto)) { + QMessageBox::warning( + ui->centralwidget, + tr("Email Error"), + tr("Unable to open your default email client."), + QMessageBox::Ok + ); + }} + +void MenuBar::submitBugReportActionTriggered() +{ + QUrl bugUrl("https://github.com/firelab/windninja/issues/new"); + if (!QDesktopServices::openUrl(bugUrl)) + { + QMessageBox::warning( + ui->centralwidget, + tr("Network Error"), + tr("Unable to open the bug report page. Please check your internet connection."), + QMessageBox::Ok + ); + } +} + +void MenuBar::enableConsoleOutputActionToggled(bool toggled) +{ + ui->consoleTextEdit->setVisible(toggled); +} diff --git a/src/gui/qt6/menuBar.h b/src/gui/qt6/menuBar.h new file mode 100644 index 000000000..1a8794a5a --- /dev/null +++ b/src/gui/qt6/menuBar.h @@ -0,0 +1,93 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Hands GUI related logic for the Menu Bar + * Author: Mason Willman + * + ****************************************************************************** + * + * 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 MENUBAR_H +#define MENUBAR_H + +#include "setConfigurationDialogOption.h" +#include "windninja.h" +#include "gdal_util.h" +#include "ninja_version.h" +#include "ui_mainWindow.h" +#include +#include +#include +#include +#include +#include +#include + +class MenuBar : public QObject +{ + Q_OBJECT + +public: + MenuBar(Ui::MainWindow* ui, QObject* parent = nullptr); + +signals: + void writeToConsoleSignal(QString message, QColor color=Qt::black); + +private slots: + // functions for Menu actions + // functions for QMenu fileMenu "File" actions + void newProjectActionTriggered(); + void openProjectActionTriggered(); + void exportSolutionActionTriggered(); + void closeProjectActionTriggered(); + // functions for QMenu optionsMenu "Options" actions + void writeConsoleOutputActionTriggered(); + // functions for QMenu toolsMenu "Tools" actions + //void resampleDataActionTriggered(); + void writeBlankStationFileActionTriggered(); + void setConfigurationOptionActionTriggered(); + // functions for QMenu helpMenu "Help" actions + // functions for sub QMenu displayingShapeFilesMenu "Displaying Shapefiles" actions + void displayArcGISProGuideActionTriggered(); + // functions for sub QMenu tutorialsMenu "Tutorials" actions + void displayTutorial1ActionTriggered(); + void displayTutorial2ActionTriggered(); + void displayTutorial3ActionTriggered(); + void displayTutorial4ActionTriggered(); + // functions for sub QMenu instructionsMenu "Instructions" actions + void displayDemDownloadInstructionsActionTriggered(); + void displayFetchDemInstructionsActionTriggered(); + void displayCommandLineInterfaceInstructionsActionTriggered(); + // functions for remaining non-sub QMenu actions + void aboutWindNinjaActionTriggered(); + void citeWindNinjaActionTriggered(); + void supportEmailActionTriggered(); + void submitBugReportActionTriggered(); + void enableConsoleOutputActionToggled(bool toggled); + +private: + Ui::MainWindow* ui; + QDir dataPath; +}; + +#endif // MENUBAR_H diff --git a/src/gui/qt6/outputs.cpp b/src/gui/qt6/outputs.cpp new file mode 100644 index 000000000..4e76caca3 --- /dev/null +++ b/src/gui/qt6/outputs.cpp @@ -0,0 +1,196 @@ +#include "outputs.h" + +Outputs::Outputs(Ui::MainWindow *ui, + QObject* parent) + : QObject(parent), + ui(ui) +{ + QString downloadsPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + ui->outputDirectoryLineEdit->setText(downloadsPath); + ui->outputDirectoryButton->setIcon(QIcon(":/folder.png")); + + ui->inputWindHeightUnitsComboBox->setItemData(0, "ft"); + ui->inputWindHeightUnitsComboBox->setItemData(1, "m"); + ui->outputWindHeightUnitsComboBox->setItemData(0, "ft"); + ui->outputWindHeightUnitsComboBox->setItemData(1, "m"); + ui->meshResolutionUnitsComboBox->setItemData(0, "m"); + ui->meshResolutionUnitsComboBox->setItemData(1, "ft"); + ui->googleEarthMeshResolutionComboBox->setItemData(0, "m"); + ui->googleEarthMeshResolutionComboBox->setItemData(1, "ft"); + ui->fireBehaviorMeshResolutionComboBox->setItemData(0, "m"); + ui->fireBehaviorMeshResolutionComboBox->setItemData(1, "ft"); + ui->shapeFilesMeshResolutionComboBox->setItemData(0, "m"); + ui->shapeFilesMeshResolutionComboBox->setItemData(1, "ft"); + ui->geospatialPDFFilesMeshResolutionComboBox->setItemData(0, "m"); + ui->geospatialPDFFilesMeshResolutionComboBox->setItemData(1, "ft"); + ui->alternativeColorSchemeComboBox->setItemData(0, "default"); + ui->alternativeColorSchemeComboBox->setItemData(1, "ROPGW"); + ui->alternativeColorSchemeComboBox->setItemData(2, "oranges"); + ui->alternativeColorSchemeComboBox->setItemData(3, "blues"); + ui->alternativeColorSchemeComboBox->setItemData(4, "pinks"); + ui->alternativeColorSchemeComboBox->setItemData(5, "greens"); + ui->alternativeColorSchemeComboBox->setItemData(6, "magic_beans"); + ui->alternativeColorSchemeComboBox->setItemData(7, "pink_to_green"); + ui->legendComboBox->setItemData(0, "equal_interval"); + ui->legendComboBox->setItemData(1, "equal_color"); + + connect(ui->outputWindHeightComboBox, &QComboBox::currentIndexChanged, this, &Outputs::windHeightComboBoxCurrentIndexChanged); + connect(ui->googleEarthCheckBox, &QCheckBox::toggled, this, &Outputs::googleEarthCheckBoxToggled); + connect(ui->fireBehaviorGroupBox, &QGroupBox::toggled, this, &Outputs::fireBehaviorGroupBoxToggled); + connect(ui->shapeFilesGroupBox, &QGroupBox::toggled, this, &Outputs::shapeFilesGroupBoxToggled); + connect(ui->geospatialPDFFilesGroupBox, &QGroupBox::toggled, this, &Outputs::geospatialPDFFilesGroupBoxToggled); + connect(ui->VTKFilesCheckBox, &QCheckBox::clicked, this, &Outputs::VTKFilesCheckBoxClicked); + connect(ui->googleEarthMeshResolutionGroupBox, &QGroupBox::toggled, this, &Outputs::googleEarthMeshResolutionGroupBoxToggled); + connect(ui->fireBehaviorMeshResolutionGroupBox, &QGroupBox::toggled, this, &Outputs::fireBehaviorMeshResolutionGroupBoxToggled); + connect(ui->shapeFilesMeshResolutionGroupBox, &QGroupBox::toggled, this, &Outputs::shapeFilesMeshResolutionGroupBoxToggled); + connect(ui->geospatialPDFFilesMeshResolutionGroupBox, &QGroupBox::toggled, this, &Outputs::geospatialPDFFilesMeshResolutionGroupBoxToggled); + connect(this, &Outputs::updateGoogleState, &AppState::instance(), &AppState::updateGoogleEarthOutputState); + connect(this, &Outputs::updateFireBehaviorState, &AppState::instance(), &AppState::updateFireBehaviorOutputState); + connect(this, &Outputs::updateShapeState, &AppState::instance(), &AppState::updateShapeFilesOutputState); + connect(this, &Outputs::updatePDFState, &AppState::instance(), &AppState::updateGeoSpatialPDFFilesOutputState); + connect(this, &Outputs::updateVTKState, &AppState::instance(), &AppState::updateVTKFilesOutputState); + connect(ui->meshResolutionSpinBox, &QDoubleSpinBox::valueChanged, this, &Outputs::meshResolutionSpinBoxValueChanged); + connect(ui->meshResolutionUnitsComboBox, &QComboBox::currentIndexChanged, this, &Outputs::meshResolutionUnitsComboBoxCurrentIndexChanged); + +} + +void Outputs::windHeightComboBoxCurrentIndexChanged(int index) +{ + switch(index) + { + case 0: + ui->outputWindHeightSpinBox->setValue(20.00); + ui->outputWindHeightSpinBox->setEnabled(false); + ui->outputWindHeightUnitsComboBox->setCurrentIndex(0); + break; + + case 1: + ui->outputWindHeightSpinBox->setValue(10.00); + ui->outputWindHeightSpinBox->setEnabled(false); + ui->outputWindHeightUnitsComboBox->setCurrentIndex(1); + break; + + case 2: + ui->outputWindHeightSpinBox->setValue(0.00); + ui->outputWindHeightSpinBox->setEnabled(true); + ui->outputWindHeightUnitsComboBox->setEnabled(true); + break; + } +} + +void Outputs::googleEarthCheckBoxToggled(bool checked) +{ + AppState& state = AppState::instance(); + state.isGoogleEarthToggled = checked; + emit updateGoogleState(); +} + +void Outputs::fireBehaviorGroupBoxToggled(bool checked) +{ + AppState& state = AppState::instance(); + state.isFireBehaviorToggled = checked; + emit updateFireBehaviorState(); +} + +void Outputs::shapeFilesGroupBoxToggled(bool checked) +{ + AppState& state = AppState::instance(); + state.isShapeFilesToggled = checked; + emit updateShapeState(); +} + +void Outputs::geospatialPDFFilesGroupBoxToggled(bool checked) +{ + AppState& state = AppState::instance(); + state.isGeoSpatialPDFFilesToggled = checked; + emit updatePDFState(); +} + +void Outputs::VTKFilesCheckBoxClicked(bool checked) +{ + AppState& state = AppState::instance(); + state.isVTKFilesToggled = checked; + emit updateVTKState(); +} + +void Outputs::googleEarthMeshResolutionGroupBoxToggled(bool checked) +{ + ui->googleEarthMeshResolutionSpinBox->setEnabled(!checked); + ui->googleEarthMeshResolutionComboBox->setEnabled(!checked); + + emit meshResolutionSpinBoxValueChanged(ui->meshResolutionSpinBox->value()); + emit meshResolutionUnitsComboBoxCurrentIndexChanged(ui->meshResolutionUnitsComboBox->currentIndex()); +} + +void Outputs::fireBehaviorMeshResolutionGroupBoxToggled(bool checked) +{ + ui->fireBehaviorMeshResolutionSpinBox->setEnabled(!checked); + ui->fireBehaviorMeshResolutionComboBox->setEnabled(!checked); + + emit meshResolutionSpinBoxValueChanged(ui->meshResolutionSpinBox->value()); + emit meshResolutionUnitsComboBoxCurrentIndexChanged(ui->meshResolutionUnitsComboBox->currentIndex()); +} + +void Outputs::shapeFilesMeshResolutionGroupBoxToggled(bool checked) +{ + ui->shapeFilesMeshResolutionSpinBox->setEnabled(!checked); + ui->shapeFilesMeshResolutionComboBox->setEnabled(!checked); + + emit meshResolutionSpinBoxValueChanged(ui->meshResolutionSpinBox->value()); + emit meshResolutionUnitsComboBoxCurrentIndexChanged(ui->meshResolutionUnitsComboBox->currentIndex()); +} + +void Outputs::geospatialPDFFilesMeshResolutionGroupBoxToggled(bool checked) +{ + ui->geospatialPDFFilesMeshResolutionSpinBox->setEnabled(!checked); + ui->geospatialPDFFilesMeshResolutionComboBox->setEnabled(!checked); + + emit meshResolutionSpinBoxValueChanged(ui->meshResolutionSpinBox->value()); + emit meshResolutionUnitsComboBoxCurrentIndexChanged(ui->meshResolutionUnitsComboBox->currentIndex()); +} + +void Outputs::meshResolutionSpinBoxValueChanged(double value) +{ + if(ui->googleEarthMeshResolutionGroupBox->isChecked()) + { + ui->googleEarthMeshResolutionSpinBox->setValue(value); + } + + if(ui->fireBehaviorMeshResolutionGroupBox->isChecked()) + { + ui->fireBehaviorMeshResolutionSpinBox->setValue(value); + } + + if(ui->shapeFilesMeshResolutionGroupBox->isChecked()) + { + ui->shapeFilesMeshResolutionSpinBox->setValue(value); + } + + if(ui->geospatialPDFFilesMeshResolutionGroupBox->isChecked()) + { + ui->geospatialPDFFilesMeshResolutionSpinBox->setValue(value); + } +} + +void Outputs::meshResolutionUnitsComboBoxCurrentIndexChanged(int index) +{ + if(ui->googleEarthMeshResolutionGroupBox->isChecked()) + { + ui->googleEarthMeshResolutionComboBox->setCurrentIndex(index); + } + + if(ui->fireBehaviorMeshResolutionGroupBox->isChecked()) + { + ui->fireBehaviorMeshResolutionComboBox->setCurrentIndex(index); + } + + if(ui->shapeFilesMeshResolutionGroupBox->isChecked()) + { + ui->shapeFilesMeshResolutionComboBox->setCurrentIndex(index); + } + + if(ui->geospatialPDFFilesMeshResolutionGroupBox->isChecked()) + { + ui->geospatialPDFFilesMeshResolutionComboBox->setCurrentIndex(index); + } +} diff --git a/src/gui/qt6/outputs.h b/src/gui/qt6/outputs.h new file mode 100644 index 000000000..9e46c95db --- /dev/null +++ b/src/gui/qt6/outputs.h @@ -0,0 +1,41 @@ +#ifndef OUTPUTS_H +#define OUTPUTS_H + +#include "ui_mainWindow.h" +#include "appState.h" +#include +#include + + +class Outputs : public QObject +{ + Q_OBJECT +public: + explicit Outputs(Ui::MainWindow *ui, QObject* parent); + +signals: + void updateGoogleState(); + void updateFireBehaviorState(); + void updateShapeState(); + void updatePDFState(); + void updateVTKState(); + +private slots: + void windHeightComboBoxCurrentIndexChanged(int index); + void googleEarthCheckBoxToggled(bool checked); + void fireBehaviorGroupBoxToggled(bool checked); + void shapeFilesGroupBoxToggled(bool checked); + void geospatialPDFFilesGroupBoxToggled(bool checked); + void VTKFilesCheckBoxClicked(bool checked); + void googleEarthMeshResolutionGroupBoxToggled(bool checked); + void fireBehaviorMeshResolutionGroupBoxToggled(bool checked); + void shapeFilesMeshResolutionGroupBoxToggled(bool checked); + void geospatialPDFFilesMeshResolutionGroupBoxToggled(bool checked); + void meshResolutionSpinBoxValueChanged(double value); + void meshResolutionUnitsComboBoxCurrentIndexChanged(int index); + +private: + Ui::MainWindow *ui; +}; + +#endif // OUTPUTS_H diff --git a/src/gui/qt6/pointInitializationInput.cpp b/src/gui/qt6/pointInitializationInput.cpp new file mode 100644 index 000000000..fd873f884 --- /dev/null +++ b/src/gui/qt6/pointInitializationInput.cpp @@ -0,0 +1,797 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Handles GUI related logic for Point Initialization Page + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "pointInitializationInput.h" + +PointInitializationInput::PointInitializationInput(Ui::MainWindow* ui, QObject* parent) + : QObject(parent), + ui(ui) +{ + ui->pointInitializationDataTimeStackedWidget->setCurrentIndex(0); + ui->weatherStationDataSourceStackedWidget->setCurrentIndex(0); + ui->weatherStationDataTimeStackedWidget->setCurrentIndex(0); + ui->weatherStationDataStartDateTimeEdit->setDateTime(QDateTime::currentDateTime().addDays(-1)); + ui->weatherStationDataEndDateTimeEdit->setDateTime(QDateTime::currentDateTime()); + + connect(ui->pointInitializationGroupBox, &QGroupBox::toggled, this, &PointInitializationInput::pointInitializationGroupBoxToggled); + connect(ui->pointInitializationDownloadDataButton, &QPushButton::clicked, this, &PointInitializationInput::pointInitializationDownloadDataButtonClicked); + connect(ui->weatherStationDataDownloadCancelButton, &QPushButton::clicked, this, &PointInitializationInput::weatherStationDataDownloadCancelButtonClicked); + connect(ui->weatherStationDataSourceComboBox, &QComboBox::currentIndexChanged, this, &PointInitializationInput::weatherStationDataSourceComboBoxCurrentIndexChanged); + connect(ui->weatherStationDataTimeComboBox, &QComboBox::currentIndexChanged, this, &PointInitializationInput::weatherStationDataTimeComboBoxCurrentIndexChanged); + connect(ui->weatherStationDataDownloadButton, &QPushButton::clicked, this, &PointInitializationInput::weatherStationDataDownloadButtonClicked); + connect(ui->pointInitializationSelectAllButton, &QPushButton::clicked, this, &PointInitializationInput::pointInitializationSelectAllButtonClicked); + connect(ui->pointInitializationSelectNoneButton, &QPushButton::clicked, this, &PointInitializationInput::pointInitializationSelectNoneButtonClicked); + connect(ui->pointInitializationTreeView, &QTreeView::expanded, this, &PointInitializationInput::folderExpanded); + connect(ui->pointInitializationTreeView, &QTreeView::collapsed, this, &PointInitializationInput::folderCollapsed); + connect(ui->weatherStationDataTimestepsSpinBox, &QSpinBox::valueChanged, this, &PointInitializationInput::weatherStationDataTimestepsSpinBoxValueChanged); + connect(this, &PointInitializationInput::updateState, &AppState::instance(), &AppState::updatePointInitializationInputState); + + connect(this, &PointInitializationInput::updateProgressMessageSignal, this, &PointInitializationInput::updateProgressMessage, Qt::QueuedConnection); +} + +void PointInitializationInput::pointInitializationGroupBoxToggled(bool toggled) +{ + AppState& state = AppState::instance(); + + state.isPointInitializationToggled = toggled; + if (toggled) + { + ui->domainAverageGroupBox->setChecked(false); + ui->weatherModelGroupBox->setChecked(false); + state.isDomainAverageInitializationToggled = ui->domainAverageGroupBox->isChecked(); + state.isWeatherModelInitializationToggled = ui->weatherModelGroupBox->isChecked(); + } + + emit updateState(); +} + +void PointInitializationInput::pointInitializationDownloadDataButtonClicked() +{ + ui->weatherStationDataSourceComboBox->setCurrentIndex(0); + ui->weatherStationDataTimeComboBox->setCurrentIndex(0); + + ui->downloadFromStationIDLineEdit->clear(); + ui->downloadFromDEMSpinBox->setValue(0); + + ui->downloadBetweenDatesStartTimeDateTimeEdit->setDateTime(QDateTime::currentDateTime().addDays(-1)); + ui->downloadBetweenDatesEndTimeDateTimeEdit->setDateTime(QDateTime::currentDateTime()); + + ui->inputsStackedWidget->setCurrentIndex(17); +} + +void PointInitializationInput::weatherStationDataDownloadCancelButtonClicked() +{ + ui->pointInitializationTreeView->collapseAll(); + ui->inputsStackedWidget->setCurrentIndex(7); +} + +void PointInitializationInput::updateProgressMessage(const QString message) +{ +// QMessageBox::critical( +// nullptr, +// QApplication::tr("Error"), +// message +// ); + progress->setLabelText(message); + progress->setWindowTitle(tr("Error")); + progress->setCancelButtonText(tr("Close")); + progress->setAutoClose(false); + progress->setAutoReset(false); + progress->setRange(0, 1); + progress->setValue(progress->maximum()); +} + +static void comMessageHandler(const char *pszMessage, void *pUser) +{ + PointInitializationInput *self = static_cast(pUser); + + std::string msg = pszMessage; + if( msg.substr(msg.size()-1, 1) == "\n") + { + msg = msg.substr(0, msg.size()-1); + } + + size_t pos; + size_t startPos; + size_t endPos; + std::string clipStr; + + if( msg.find("Exception caught: ") != msg.npos || msg.find("ERROR: ") != msg.npos ) + { + if( msg.find("Exception caught: ") != msg.npos ) + { + pos = msg.find("Exception caught: "); + startPos = pos+18; + } + else // if( msg.find("ERROR: ") != msg.npos ) + { + pos = msg.find("ERROR: "); + startPos = pos+7; + } + clipStr = msg.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); + //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); + if( clipStr == "Cannot determine exception type." ) + { + emit self->updateProgressMessageSignal(QString::fromStdString("StationFetch ended with unknown error")); + emit self->writeToConsoleSignal(QString::fromStdString("unknown StationFetch error"), Qt::red); + } + else + { + emit self->updateProgressMessageSignal(QString::fromStdString("StationFetch ended in error:\n"+clipStr)); + emit self->writeToConsoleSignal(QString::fromStdString("StationFetch error: "+clipStr), Qt::red); + } + } + else if( msg.find("Warning: ") != msg.npos ) + { + if( msg.find("Warning: ") != msg.npos ) + { + pos = msg.find("Warning: "); + startPos = pos+9; + } + clipStr = msg.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + //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); + } + else + { + emit self->updateProgressMessageSignal(QString::fromStdString(msg)); + emit self->writeToConsoleSignal(QString::fromStdString(msg)); + } +} + +void PointInitializationInput::weatherStationDataDownloadButtonClicked() +{ + emit writeToConsoleSignal("Fetching station data..."); + + progress = new QProgressDialog("Fetching Station Data...", QString(), 0, 0, ui->centralwidget); + progress->setWindowModality(Qt::WindowModal); + progress->setCancelButton(nullptr); + progress->setMinimumDuration(0); + progress->setAutoClose(true); + progress->show(); + + NinjaToolsH* ninjaTools = NinjaMakeTools(); + + char **papszOptions = NULL; + NinjaErr ninjaErr = NinjaSetToolsComMessageHandler(ninjaTools, &comMessageHandler, this, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetToolsComMessageHandler(): ninjaErr =" << ninjaErr; + } + + QString DEMTimeZone = ui->timeZoneComboBox->currentText(); + QByteArray DEMTimeZoneBytes = ui->timeZoneComboBox->currentText().toUtf8(); + QDateTime start = ui->downloadBetweenDatesStartTimeDateTimeEdit->dateTime(); + QDateTime end = ui->downloadBetweenDatesEndTimeDateTimeEdit->dateTime(); + + QVector year = {start.date().year(), end.date().year()}; + QVector month = {start.date().month(), end.date().month()}; + QVector day = {start.date().day(), end.date().day()}; + QVector hour = {start.time().hour(), end.time().hour()}; + QVector minute = {start.time().minute(), end.time().minute()}; + QVector outYear(2), outMonth(2), outDay(2), outHour(2), outMinute(2); + + // this one should break so hard, the code won't even know what happened + // yeah, a qt error this time, "ASSERT failure in QList::operator[]: "index out of range"" + // well, that's confusing. This breaking only happens if doing "qDebug() << outTime[1];" rather than just [0], + // comment that out and it runs to completion fine for everything, it just uses the first time for everything. + // looks like it just uses the single time, as either a latestTime or a time-series file format, + // depending on the choice of the input download type, latestTime or time-series + /*QVector year = {start.date().year(), end.date().year()}; + QVector month = {start.date().month(), end.date().month()}; + QVector day = {start.date().day(), end.date().day()}; + QVector hour = {start.time().hour(), end.time().hour()}; + QVector minute = {start.time().minute(), end.time().minute()}; + QVector outYear(1), outMonth(1), outDay(1), outHour(1), outMinute(1);*/ + + // this set will probably work fine + // yup, worked fine. The time-series became a time-series from and to the same exact time + /*QVector year = {start.date().year(), start.date().year()}; + QVector month = {start.date().month(), start.date().month()}; + QVector day = {start.date().day(), start.date().day()}; + QVector hour = {start.time().hour(), start.time().hour()}; + QVector minute = {start.time().minute(), start.time().minute()}; + QVector outYear(2), outMonth(2), outDay(2), outHour(2), outMinute(2);*/ + + // this set will probably work fine + // yup, worked fine. The time-series became a time-series from and to the same exact time + /*QVector year = {end.date().year(), end.date().year()}; + QVector month = {end.date().month(), end.date().month()}; + QVector day = {end.date().day(), end.date().day()}; + QVector hour = {end.time().hour(), end.time().hour()}; + QVector minute = {end.time().minute(), end.time().minute()}; + QVector outYear(2), outMonth(2), outDay(2), outHour(2), outMinute(2);*/ + + // the latestTime fetch just uses the first time from the list + // the time-series fetch throws an error as expected, but the message seems to drop what is going on to cause the error + /*QVector year = {start.date().year(), start.date().year()-1}; + QVector month = {start.date().month(), start.date().month()}; + QVector day = {start.date().day(), start.date().day()}; + QVector hour = {start.time().hour(), start.time().hour()}; + QVector minute = {start.time().minute(), start.time().minute()}; + QVector outYear(2), outMonth(2), outDay(2), outHour(2), outMinute(2);*/ + + // should die pretty hard + // well, it runs this part fine, just runs normally when run by the later fetch (because the fetch SHOULD die, but does not right now for this case), + // unless it is a latestTime fetch in which case it just uses the first time from the list + /*QVector year = {start.date().year(), start.date().year()}; + QVector month = {start.date().month(), start.date().month()}; + QVector day = {start.date().day(), start.date().day()}; + QVector hour = {start.time().hour(), start.time().hour()-1}; + QVector minute = {start.time().minute(), start.time().minute()}; + QVector outYear(2), outMonth(2), outDay(2), outHour(2), outMinute(2);*/ + + // the latestTime fetch just uses the first time from the list + // the time-series fetch throws an error as expected, but the message seems to drop what is going on to cause the error + /*QVector year = {start.date().year(), start.date().year()}; + QVector month = {start.date().month(), start.date().month()}; + QVector day = {start.date().day(), start.date().day()}; + QVector hour = {start.time().hour(), start.time().hour()-2}; // had to use 3 instead of 2 for this to work, when I was right at and close to the hour + QVector minute = {start.time().minute(), start.time().minute()}; + QVector outYear(2), outMonth(2), outDay(2), outHour(2), outMinute(2);*/ + + // the latestTime fetch just uses the first time from the list + // the time-series fetch throws an error as expected, but the message seems to drop what is going on to cause the error + /*QVector year = {end.date().year()+1, end.date().year()}; + QVector month = {end.date().month(), end.date().month()}; + QVector day = {end.date().day(), end.date().day()}; + QVector hour = {end.time().hour(), end.time().hour()}; + QVector minute = {end.time().minute(), end.time().minute()}; + QVector outYear(2), outMonth(2), outDay(2), outHour(2), outMinute(2);*/ + + // the latestTime fetch just uses the first time from the list + // the time-series fetch throws an error as expected, but the message seems to drop what is going on to cause the error + /*QVector year = {end.date().year(), end.date().year()}; + QVector month = {end.date().month(), end.date().month()}; + QVector day = {end.date().day(), end.date().day()}; + QVector hour = {end.time().hour()+1, end.time().hour()}; + QVector minute = {end.time().minute(), end.time().minute()}; + QVector outYear(2), outMonth(2), outDay(2), outHour(2), outMinute(2);*/ + + ninjaErr = NinjaGetTimeList( + ninjaTools, + year.data(), month.data(), day.data(), + hour.data(), minute.data(), + outYear.data(), outMonth.data(), outDay.data(), + outHour.data(), outMinute.data(), + 2, DEMTimeZoneBytes.data() + ); + //ninjaErr = NinjaGetTimeList( + // ninjaTools, + // year.data(), month.data(), day.data(), + // hour.data(), minute.data(), + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), + // 1, DEMTimeZoneBytes.data() + // ); // this one shouldn't hurt anything, but want to see what happens // gives an error, for both latestTime and time-series: "Day of month value is out of range 1..31". + //ninjaErr = NinjaGetTimeList( + // ninjaTools, + // year.data(), month.data(), day.data(), + // hour.data(), minute.data(), + // outYear.data(), outMonth.data(), outDay.data(), + // outHour.data(), outMinute.data(), + // 2, "fudge" + // ); // should break, but probably won't, will probably be treated like the dem timezone // turns out it breaks HARD, a smart pointer failing on assert somewhere along the pipeline, not sure if that occurs here, or later down the pipeline. Definitely a break not necessarily related directly with the dem, breaks probably because of something sized wrong because of the dem being off, or it breaks because it IS trying to read the dem even though it shouldn't. And it gets past the try/catch error handling stuff, hrm. + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaGetTimeList: ninjaErr =" << ninjaErr; + + progress->setWindowTitle(tr("Error")); + progress->setCancelButtonText("Close"); + progress->setAutoClose(false); + progress->setAutoReset(false); + progress->setRange(0, 1); + progress->setValue(progress->maximum()); + + // do cleanup before the return, similar to finishedSolve() + +// ninjaErr = NinjaDestroyTools(ninjaTools, papszOptions); +// if(ninjaErr != NINJA_SUCCESS) +// { +// printf("NinjaDestroyTools: ninjaErr = %d\n", ninjaErr); +// } + + //futureWatcher->deleteLater(); + + return; + } + + if(ui->weatherStationDataTimeComboBox->currentIndex() == 1) // TODO: Add proper error handling for a bad time duration (someone downloads too much data) + { + //outYear[0] = outYear[1]-2; // doing more than a year worth of time SHOULD be what triggers it, this is NOT a check on the times themselves // hrm, it returns an error code of 8, but no ninjaCom seems to be sent so it leaves it hanging without a proper message. runs fine for a latestTime simulation. + //outYear[1] = outYear[0]+2; // not sure if it can even handle when the endYear goes past the current year // same result as the above test. + ninjaErr = NinjaCheckTimeDuration(ninjaTools, outYear.data(), outMonth.data(), outDay.data(), outHour.data(), outMinute.data(), 2, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaCheckTimeDuration ninjaErr=" << ninjaErr; + + progress->setWindowTitle(tr("Error")); + progress->setCancelButtonText("Close"); + progress->setAutoClose(false); + progress->setAutoReset(false); + progress->setRange(0, 1); + progress->setValue(progress->maximum()); + + // do cleanup before the return, similar to finishedSolve() + +// ninjaErr = NinjaDestroyTools(ninjaTools, papszOptions); +// if(ninjaErr != NINJA_SUCCESS) +// { +// printf("NinjaDestroyTools: ninjaErr = %d\n", ninjaErr); +// } + + //futureWatcher->deleteLater(); + + return; + } + } + + bool fetchLatestFlag = ui->weatherStationDataTimeComboBox->currentIndex() ? 0 : 1; + QString outputPath = ui->outputDirectoryLineEdit->text(); + QString elevationFile = ui->elevationInputFileLineEdit->property("fullpath").toString(); + + futureWatcher = new QFutureWatcher(this); + QFuture future; + if(ui->weatherStationDataSourceComboBox->currentIndex() == 0) + { + QString units = ui->downloadFromDEMComboBox->currentText(); + double buffer = ui->downloadFromDEMSpinBox->value(); + future = QtConcurrent::run(&PointInitializationInput::fetchStationFromBbox, this, + ninjaTools, + outYear, outMonth, outDay, outHour, outMinute, + elevationFile, buffer, units, + DEMTimeZone, fetchLatestFlag, outputPath); + } + else + { + QString stationList = ui->downloadFromStationIDLineEdit->text(); + future = QtConcurrent::run(&PointInitializationInput::fetchStationByName, this, + ninjaTools, + outYear, outMonth, outDay, outHour, outMinute, + elevationFile, stationList, + DEMTimeZone, fetchLatestFlag, outputPath); + } + futureWatcher->setFuture(future); + + connect(futureWatcher, &QFutureWatcher::finished, this, &PointInitializationInput::fetchStationDataFinished); +} + +int PointInitializationInput::fetchStationFromBbox(NinjaToolsH* ninjaTools, + QVector year, + QVector month, + QVector day, + QVector hour, + QVector minute, + QString elevationFile, + double buffer, + QString units, + QString osTimeZone, + bool fetchLatestFlag, + QString outputPath) +{ + // apparently if it is a latestTime run, changing the times means nothing, they get ignored + // though looks like the values that find themselves in there before editing for a latestTime run, + // are endDateTime of current time , in UTC + // and startDateTime of current time - 1 hour, in UTC + // meaning that setting a latestTime run to a multi-time series, just runs fine and acts like a multi-time series + // hrm, when redownloading of the same type, multi-time series, but differing times, the same folder and sometimes filenames end up getting used, from the first download attempt. Seems like badly defined behavior, might be related to the stations info being a set of static variables? I'm not sure what is going on there, the inputs to the function ARE the proper updated set of times. + // hrm, in all cases, failing or otherwise, the wxStation folder ends up still getting written, does not end up getting cleaned up after the failing of the run. + + //fetchLatestFlag = TRUE; // on a single-time series, what will happen? // Turns out it runs fine, just uses the start time of the multi-time series. So kind of a useless test. + //fetchLatestFlag = FALSE; // try on a multi-time series, with good inputs or no, what will happen? // Turns out it runs fine, because the default values are a time series that is normally ignored. kind of behaves like a useful test, though that depends on how the inputs could change. + + //year[1] = year[0]-1; // throws an error as expected, though the message seems to drop what is going on to cause the error. The folder is still created, doesn't get cleaned up after the download fails. + //hour[1] = hour[0]-1; // need to carefully set the date to the same date for both times for this test to properly work. apparently this test SHOULD break, but something must be up in how it is handled within the ninja code because it ends up just downloading a single dateTime, of the earliest of the two sets of times. + //hour[1] = hour[0]-2; // throws an error as expected, though the message seems to drop what is going on to cause the error, but had to use 3 instead of 2 for this to work, when I was right at and close to the hour. need to carefully set the date to the same date for both times for this test to properly work. + //year[0] = year[1]+1; // throws an error as expected, though the message seems to drop what is going on to cause the error + //hour[0] = hour[1]+1; // need to carefully set the date to the same date for both times for this test to properly work. apparently this test SHOULD break, but something must be up in how it is handled within the ninja code because it ends up just downloading a single dateTime, of the earliest of the two sets of times. Seems to work better when not close to the hour. + //hour[0] = hour[1]+2; // throws an error as expected. need to carefully set the date to the same date for both times for this test to properly work. + //elevationFile = "fudge"; // throws error as expected, after a message of "ERROR 4: fudge: No such file or directory", BUT, the message seems to drop what is going on to cause the error + //buffer = -1; // runs fine, no error messages, just downloads the data within the area of the dem + //units = "fudge"; // runs fine, no error messages, just downloads the data within the area of the dem + //osTimeZone = "fudge"; // runs fine, no error messages, seems to just download the data assuming the timezone of the dem + + //year[0] = 9999; // throws an error as expected, though the message seems to drop what is going on to cause the error + //hour[0] = 9999; // throws an error as expected, though the message seems to drop what is going on to cause the error + //year[1] = 9999; // throws an error as expected, though the message seems to drop what is going on to cause the error + //hour[1] = 9999; // um, this one did NOT throw an error, but actually ran successfully, creating data as if the endTime was the same thing as the startTime + //year[0] = -1; // throws an error as expected, and actually a good informative error this time + //hour[0] = -1; // um, this one did NOT throw an error, but actually ran successfully, creating data as if the startHour was the same thing as the endHour + //year[1] = -1; // throws an error as expected, and actually a good informative error this time + //hour[1] = -1; // um, this one did NOT throw an error, but actually ran successfully, creating data as if the endHour was the same thing as the startHour + //year[0] = 0; // throws an error as expected, and actually a good informative error this time + //year[1] = 0; // throws an error as expected, and actually a good informative error this time + + //qDebug() << "year[0] month[0] day[0] hour[0] minute[0] =" << year[0] << month[0] << day[0] << hour[0] << minute[0]; + //qDebug() << "year[1] month[1] day[1] hour[1] minute[1] =" << year[1] << month[1] << day[1] << hour[1] << minute[1]; + + char ** options = NULL; + NinjaErr ninjaErr = NinjaFetchStationFromBBox( + ninjaTools, + year.data(), month.data(), day.data(), + hour.data(), minute.data(), year.size(), + elevationFile.toUtf8().constData(), buffer, + units.toUtf8().constData(), osTimeZone.toUtf8().constData(), + fetchLatestFlag, outputPath.toUtf8().constData(), + false, options + ); + + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaFetchStationFromBbox: ninjaErr =" << ninjaErr; + } + + return ninjaErr; +} + +int PointInitializationInput::fetchStationByName(NinjaToolsH* ninjaTools, + QVector year, + QVector month, + QVector day, + QVector hour, + QVector minute, + QString elevationFile, + QString stationList, + QString osTimeZone, + bool fetchLatestFlag, + QString outputPath) +{ + //stationList = "KMSO,PNTM8"; // this is a working list, what the list SHOULD be, and it works great. But apparently doing so with a SPACE between the commas of the stations, even though that is what is shown in the GUI as an example, DOES break the code. + //stationList = "KMSO, PNTM8"; // um, this should NOT thrown an error, but it does. So I guess something is wrong with how the files are parsed. Runs fine for a single station. The error message also drops telling what is going on, so annoying. + + // apparently if it is a latestTime run, changing the times means nothing, they get ignored + // though looks like the values that find themselves in there before editing for a latestTime run, + // are endDateTime of current time , in UTC + // and startDateTime of current time - 1 hour, in UTC + // meaning that setting a latestTime run to a multi-time series, just runs fine and acts like a multi-time series + // hrm, when redownloading of the same type, multi-time series, but differing times, the same folder and sometimes filenames end up getting used, from the first download attempt. Seems like badly defined behavior, might be related to the stations info being a set of static variables? I'm not sure what is going on there, the inputs to the function ARE the proper updated set of times. + // hrm, in all cases, failing or otherwise, the wxStation folder ends up still getting written, does not end up getting cleaned up after the failing of the run. + + //fetchLatestFlag = TRUE; // on a single-time series, what will happen? // Turns out it runs fine, just uses the start time of the multi-time series. So kind of a useless test. + //fetchLatestFlag = FALSE; // try on a multi-time series, with good inputs or no, what will happen? // Turns out it runs fine, because the default values are a time series that is normally ignored. kind of behaves like a useful test, though that depends on how the inputs could change. + + //year[1] = year[0]-1; // throws an error as expected, though the message seems to drop what is going on to cause the error. The folder is still created, doesn't get cleaned up after the download fails. + //hour[1] = hour[0]-1; // need to carefully set the date to the same date for both times for this test to properly work. apparently this test SHOULD break, but something must be up in how it is handled within the ninja code because it ends up just downloading a single dateTime, of the earliest of the two sets of times. + //hour[1] = hour[0]-2; // throws an error as expected, though the message seems to drop what is going on to cause the error, but had to use 3 instead of 2 for this to work, when I was right at and close to the hour. need to carefully set the date to the same date for both times for this test to properly work. + //year[0] = year[1]+1; // throws an error as expected, though the message seems to drop what is going on to cause the error + //hour[0] = hour[1]+1; // need to carefully set the date to the same date for both times for this test to properly work. apparently this test SHOULD break, but something must be up in how it is handled within the ninja code because it ends up just downloading a single dateTime, of the earliest of the two sets of times. Seems to work better when not close to the hour. + //hour[0] = hour[1]+2; // throws an error as expected. need to carefully set the date to the same date for both times for this test to properly work. + //elevationFile = "fudge"; // um, this is supposed to have thrown an error, but it didn't, data somehow downloaded fine with fudge in the name. Oh I see, it just uses the stations that are chosen to be downloaded. + //stationList = "fudge"; // throws error as expected, though the message seems to drop what is going on to cause the error. + //osTimeZone = "fudge"; // runs fine, no error messages, seems to just download the data assuming the timezone of the dem + + //year[0] = 9999; // throws an error as expected, though the message seems to drop what is going on to cause the error + //hour[0] = 9999; // throws an error as expected, though the message seems to drop what is going on to cause the error + //year[1] = 9999; // throws an error as expected, though the message seems to drop what is going on to cause the error + //hour[1] = 9999; // um, this one did NOT throw an error, but actually ran successfully, creating data as if the endTime was the same thing as the startTime + //year[0] = -1; // throws an error as expected, and actually a good informative error this time + //hour[0] = -1; // um, this one did NOT throw an error, but actually ran successfully, creating data as if the startHour was the same thing as the endHour + //year[1] = -1; // throws an error as expected, and actually a good informative error this time + //hour[1] = -1; // um, this one did NOT throw an error, but actually ran successfully, creating data as if the endHour was the same thing as the startHour + //year[0] = 0; // throws an error as expected, and actually a good informative error this time + //year[1] = 0; // throws an error as expected, and actually a good informative error this time + + //qDebug() << "year[0] month[0] day[0] hour[0] minute[0] =" << year[0] << month[0] << day[0] << hour[0] << minute[0]; + //qDebug() << "year[1] month[1] day[1] hour[1] minute[1] =" << year[1] << month[1] << day[1] << hour[1] << minute[1]; + + char ** options = NULL; + NinjaErr ninjaErr = NinjaFetchStationByName( + ninjaTools, + year.data(), month.data(), day.data(), + hour.data(), minute.data(), year.size(), + elevationFile.toUtf8().constData(), stationList.toUtf8().constData(), + osTimeZone.toUtf8().constData(), fetchLatestFlag, + outputPath.toUtf8().constData(), false, options + ); + + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaFetchStationFromBbox: ninjaErr =" << ninjaErr; + } + + return ninjaErr; +} + +void PointInitializationInput::fetchStationDataFinished() +{ + // get the return value of the QtConcurrent::run() function + int result = futureWatcher->future().result(); + + if(result == NINJA_SUCCESS) + { + emit writeToConsoleSignal("Finished fetching station data.", Qt::darkGreen); + + if (progress) + { + progress->close(); + progress->deleteLater(); + progress = nullptr; + } + + ui->inputsStackedWidget->setCurrentIndex(7); + + } else + { + emit writeToConsoleSignal("Failed to fetch station data."); + } + + // delete the futureWatcher every time, whether success or failure + if (futureWatcher) + { + futureWatcher->deleteLater(); + futureWatcher = nullptr; + } +} + +void PointInitializationInput::weatherStationDataSourceComboBoxCurrentIndexChanged(int index) +{ + ui->weatherStationDataSourceStackedWidget->setCurrentIndex(index); +} + +void PointInitializationInput::weatherStationDataTimeComboBoxCurrentIndexChanged(int index) +{ + ui->weatherStationDataTimeStackedWidget->setCurrentIndex(index); +} + +void PointInitializationInput::updateTreeView() +{ + AppState& state = AppState::instance(); + state.isStationFileSelectionValid = false; + emit updateState(); + + stationFileSystemModel = new QFileSystemModel(this); + QString path = ui->elevationInputFileLineEdit->property("fullpath").toString(); + QFileInfo fileInfo(path); + + stationFileSystemModel->setRootPath(fileInfo.absolutePath()); + stationFileSystemModel->setNameFilters({"*.csv", "WXSTATIONS-*"}); + stationFileSystemModel->setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + stationFileSystemModel->setNameFilterDisables(false); + + ui->pointInitializationTreeView->setModel(stationFileSystemModel); + ui->pointInitializationTreeView->setRootIndex(stationFileSystemModel->index(fileInfo.absolutePath())); + ui->pointInitializationTreeView->setSelectionMode(QAbstractItemView::MultiSelection); + ui->pointInitializationTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->pointInitializationTreeView->setAnimated(true); + ui->pointInitializationTreeView->header()->setSectionResizeMode(QHeaderView::Stretch); + ui->pointInitializationTreeView->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->pointInitializationTreeView->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); + ui->pointInitializationTreeView->setUniformRowHeights(true); + ui->pointInitializationTreeView->hideColumn(1); + ui->pointInitializationTreeView->hideColumn(2); + + connect(ui->pointInitializationTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PointInitializationInput::pointInitializationTreeViewItemSelectionChanged); +} + +void PointInitializationInput::pointInitializationTreeViewItemSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + AppState& state = AppState::instance(); + QModelIndexList selectedRows = ui->pointInitializationTreeView->selectionModel()->selectedRows(); + + stationFiles.clear(); + stationFileTypes.clear(); + + maxStationTime = QDateTime(); + minStationTime = QDateTime(); + + state.isStationFileSelected = false; + if (selectedRows.count() > 0) + { + state.isStationFileSelected = true; + } + + for(int i = 0; i < selectedRows.count(); i++) + { + if(stationFileSystemModel->isDir(selectedRows[i])) + { + ui->pointInitializationTreeView->selectionModel()->select(selectedRows[i], QItemSelectionModel::Deselect | QItemSelectionModel::Rows); + return; + } + + QString recentFileSelected = stationFileSystemModel->filePath(selectedRows[i]); + stationFiles.push_back(recentFileSelected); + //qDebug() << "[GUI-Point] Selected file path:" << recentFileSelected; + + QByteArray filePathBytes = recentFileSelected.toUtf8(); + const char* filePath = filePathBytes.constData(); + char** options = nullptr; + int stationHeader = NinjaGetWxStationHeaderVersion(filePath, options); + //qDebug() << "[GUI-Point] Station Header: " << stationHeader; + + bool timeSeriesFlag = true; + if (stationHeader != 1) + { + GDALDataset* hDS = (GDALDataset*) GDALOpenEx( + filePath, + GDAL_OF_VECTOR | GDAL_OF_READONLY, + NULL, + NULL, + NULL + ); + + OGRLayer* poLayer = hDS->GetLayer(0); + poLayer->ResetReading(); + qint64 lastIndex = poLayer->GetFeatureCount(); + //qDebug() << "[GUI-Point] Number of Time Entries:" << lastIndex; + + OGRFeature* poFeature = poLayer->GetFeature(1); // Skip header, row 1 is first time in series + QString startDateTime(poFeature->GetFieldAsString(15)); // Time should be in 15th (last) column (0-14) + //qDebug() << "[GUI-Point] Station start time:" << startDateTime; + + poFeature = poLayer->GetFeature(lastIndex); // last time in series + QString stopDateTime(poFeature->GetFieldAsString(15)); + //qDebug() << "[GUI-Point] Station end Time:" << stopDateTime; + + if (startDateTime.isEmpty() && stopDateTime.isEmpty()) // No time series + { + //qDebug() << "[GUI-Point] File cannot be used for Time Series"; + timeSeriesFlag = false; + stationFileTypes.push_back(0); + } + else if (!startDateTime.isEmpty() && !stopDateTime.isEmpty()) // Some type of time series + { + //qDebug() << "[GUI-Point] File can be used for Time Series, suggesting time series parameters..."; + readStationTime(startDateTime, stopDateTime); + stationFileTypes.push_back(1); + } + } + + ui->pointInitializationDataTimeStackedWidget->setCurrentIndex(timeSeriesFlag ? 0 : 1); + + if (!timeSeriesFlag) + { + QDateTime dateModified = QFileInfo(recentFileSelected).birthTime(); + ui->weatherStationDataLabel->setText("Simulation time set to: " + dateModified.toString()); + ui->weatherStationDataLabel->setProperty("simulationTime", dateModified); + } + ui->pointInitializationTreeView->setProperty("timeSeriesFlag", timeSeriesFlag); + } + + state.isStationFileSelectionValid = true; + for (int i = 0; i < stationFileTypes.size(); i++) + { + if (stationFileTypes[i] != stationFileTypes[0]) { + state.isStationFileSelectionValid = false; + break; + } + } + emit updateState(); +} + +void PointInitializationInput::pointInitializationSelectAllButtonClicked() +{ + QItemSelectionModel *selectionModel = ui->pointInitializationTreeView->selectionModel(); + + QModelIndex folderIndex; + if (openStationFolders.isEmpty()) + { + QFileInfo fileInfo(ui->elevationInputFileLineEdit->property("fullpath").toString()); + folderIndex = stationFileSystemModel->index(fileInfo.absolutePath()); + } + else + { + folderIndex = stationFileSystemModel->index(openStationFolders.back()); + } + + int rowCount = stationFileSystemModel->rowCount(folderIndex); + QItemSelection selection; + for (int i = 0; i < rowCount; i++) { + QModelIndex stationFile = stationFileSystemModel->index(i, 0, folderIndex); + + if (stationFileSystemModel->isDir(stationFile)) + continue; + + selection.select(stationFile, stationFile); + } + + selectionModel->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +void PointInitializationInput::readStationTime(QString startDateTime, QString stopDateTime) +{ + QString stationTimeFormat = "yyyy-MM-ddTHH:mm:ssZ"; + QString DEMTimeZone = ui->timeZoneComboBox->currentText(); + + QTimeZone timeZone(DEMTimeZone.toUtf8()); + if (!timeZone.isValid()) { + qWarning() << "[GUI-Point] Invalid time zone:" << DEMTimeZone; + timeZone = QTimeZone::systemTimeZone(); + } + + QDateTime startTimeUTC = QDateTime::fromString(startDateTime, stationTimeFormat); + QDateTime endTimeUTC = QDateTime::fromString(stopDateTime, stationTimeFormat); + startTimeUTC.setTimeSpec(Qt::UTC); + endTimeUTC.setTimeSpec(Qt::UTC); + + QDateTime DEMStartTime = startTimeUTC.toTimeZone(timeZone); + QDateTime DEMEndTime = endTimeUTC.toTimeZone(timeZone); + + if (minStationTime.isNull() || DEMStartTime < minStationTime) + { + minStationTime = DEMStartTime; + } + if(maxStationTime.isNull() || DEMEndTime > maxStationTime) + { + maxStationTime = DEMEndTime; + } + + //qDebug() << "[GUI-Point] Start Time (" << DEMTimeZone << "):" << minStationTime.toString(); + //qDebug() << "[GUI-Point] Stop Time (" << DEMTimeZone << "):" << maxStationTime.toString(); + + ui->weatherStationMinTimeLabel->setText("Current Min Time: " + minStationTime.toString()); + ui->weatherStationMaxTimeLabel->setText("Current Min Time: " + maxStationTime.toString()); + ui->weatherStationDataStartDateTimeEdit->setDateTime(QDateTime(minStationTime.date(), minStationTime.time(), Qt::LocalTime)); + ui->weatherStationDataEndDateTimeEdit->setDateTime(QDateTime(maxStationTime.date(), maxStationTime.time(), Qt::LocalTime)); + ui->weatherStationDataStartDateTimeEdit->setDateTimeRange(minStationTime, maxStationTime); + ui->weatherStationDataEndDateTimeEdit->setDateTimeRange(minStationTime, maxStationTime); + ui->weatherStationDataStartDateTimeEdit->setCalendarPopup(false); + ui->weatherStationDataEndDateTimeEdit->setCalendarPopup(false); + ui->weatherStationDataStartDateTimeEdit->setEnabled(true); + ui->weatherStationDataEndDateTimeEdit->setEnabled(true); + ui->weatherStationDataTimestepsSpinBox->setEnabled(true); + + int timesteps = qMax(2, static_cast(minStationTime.secsTo(maxStationTime) / 3600)); + ui->weatherStationDataTimestepsSpinBox->setValue(timesteps); + //qDebug() << "[GUI-Point] Suggested Timesteps:" << timesteps; +} + +void PointInitializationInput::pointInitializationSelectNoneButtonClicked() +{ + ui->pointInitializationTreeView->selectionModel()->clearSelection(); +} + +void PointInitializationInput::folderExpanded(const QModelIndex &index) +{ + openStationFolders.push_back(stationFileSystemModel->filePath(index)); +} + +void PointInitializationInput::folderCollapsed(const QModelIndex &index) +{ + QString path = stationFileSystemModel->filePath(index); + openStationFolders.removeOne(path); +} + +void PointInitializationInput::weatherStationDataTimestepsSpinBoxValueChanged(int value) +{ + ui->weatherStationDataEndDateTimeEdit->setDisabled(value == 1); +} + +QVector PointInitializationInput::getStationFiles() +{ + return stationFiles; +} + + + + diff --git a/src/gui/qt6/pointInitializationInput.h b/src/gui/qt6/pointInitializationInput.h new file mode 100644 index 000000000..d580930aa --- /dev/null +++ b/src/gui/qt6/pointInitializationInput.h @@ -0,0 +1,117 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Handles GUI related logic for Point Initialization Page + * Author: Mason Willman + * + ****************************************************************************** + * + * 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 POINTINITIALIZATIONINPUT_H +#define POINTINITIALIZATIONINPUT_H + +#include "appState.h" +#include "windninja.h" +#include "gdal_util.h" +#include "ui_mainWindow.h" +#include +#include +#include +#include +#include +#include +#include + +class PointInitializationInput : public QObject +{ + Q_OBJECT +public: + PointInitializationInput(Ui::MainWindow* ui, QObject* parent = nullptr); + QVector getStationFiles(); + +signals: + void updateState(); + void updateProgressMessageSignal(const QString &msg); + void writeToConsoleSignal(const QString &msg, QColor color=Qt::black); + +public slots: + void updateTreeView(); + +private slots: + void pointInitializationGroupBoxToggled(bool toggled); + void pointInitializationDownloadDataButtonClicked(); + void weatherStationDataDownloadCancelButtonClicked(); + void weatherStationDataSourceComboBoxCurrentIndexChanged(int index); + void weatherStationDataTimeComboBoxCurrentIndexChanged(int index); + void weatherStationDataDownloadButtonClicked(); + void pointInitializationTreeViewItemSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void pointInitializationSelectAllButtonClicked(); + void pointInitializationSelectNoneButtonClicked(); + void folderExpanded(const QModelIndex &index); + void folderCollapsed(const QModelIndex &index); + void weatherStationDataTimestepsSpinBoxValueChanged(int value); + void updateProgressMessage(const QString message); + +private: + Ui::MainWindow *ui; + + NinjaErr ninjaErr; + + QProgressDialog *progress; + QFutureWatcher *futureWatcher; + QFileSystemModel *stationFileSystemModel; + QDateTime maxStationTime, minStationTime; + QVector stationFiles; + QVector stationFileTypes; + QVector openStationFolders; + + int fetchStationFromBbox(NinjaToolsH* ninjaTools, + QVector year, + QVector month, + QVector day, + QVector hour, + QVector minute, + QString elevationFile, + double buffer, + QString units, + QString osTimeZone, + bool fetchLatestFlag, + QString outputPath); + int fetchStationByName(NinjaToolsH* ninjaTools, + QVector year, + QVector month, + QVector day, + QVector hour, + QVector minute, + QString elevationFile, + QString stationList, + QString osTimeZone, + bool fetchLatestFlag, + QString outputPath); + void fetchStationDataFinished(); + bool readTimeSeries(QModelIndex row); + void readStationTime(QString startDateTime, QString stopDateTime); + void updateTimeSteps(); +}; + +#endif // POINTINITIALIZATIONINPUT_H diff --git a/src/gui/qt6/serverBridge.cpp b/src/gui/qt6/serverBridge.cpp new file mode 100644 index 000000000..9b35c0ae3 --- /dev/null +++ b/src/gui/qt6/serverBridge.cpp @@ -0,0 +1,176 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Connects the Qt GUI with the ninjastorm server + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "serverBridge.h" + +ServerBridge::ServerBridge() {} + +/* +** Check for version updates, or messages from the server. +*/ +void ServerBridge::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.setTextFormat(Qt::RichText); + + QString formattedMsg = QString( + "
%1
" + ).arg(papszMsg); + mbox.setText(formattedMsg); + mbox.setTextInteractionFlags(Qt::TextBrowserInteraction); + mbox.setStandardButtons(QMessageBox::Ok); + mbox.setWindowModality(Qt::ApplicationModal); + mbox.exec(); + delete[] papszMsg; + } + } + } +} + +/* +** Query the ninjastorm.firelab.org/sqlitetest/messages.txt and ask for the most up to date version. +** There are three current values: +** +** VERSION -> a semantic version string, comparable with strcmp() +** MESSAGE -> One or more messages to display to the user +** ABORT -> There is a fundamental problem with windninja, and it should call +** abort() after displaying a message. +*/ +bool ServerBridge::NinjaCheckVersions(char * mostrecentversion, char * localversion) +{ + char comparemostrecentversion[256]; + char comparelocalversion[256]; + int mostrecentversionIndex = 0; + int localversionIndex = 0; + while (*mostrecentversion) { + if (*mostrecentversion != '.') { + comparemostrecentversion[mostrecentversionIndex++] = *mostrecentversion; + } + mostrecentversion++; + } + comparemostrecentversion[mostrecentversionIndex] = '\0'; + + while (*localversion) { + if (*localversion != '.') { + comparelocalversion[localversionIndex++] = *localversion; + } + localversion++; + } + + comparelocalversion[localversionIndex] = '\0'; + return strcmp(comparemostrecentversion, comparelocalversion) == 0; + +} + +char * ServerBridge::NinjaQueryServerMessages(bool checkAbort) +{ + CPLSetConfigOption("GDAL_HTTP_UNSAFESSL", "YES"); + CPLSetConfigOption("GDAL_HTTP_TIMEOUT", "5"); + const char* url = "https://ninjastorm.firelab.org/sqlitetest/messages.txt"; + CPLHTTPResult *poResult = CPLHTTPFetch(url, NULL); + CPLSetConfigOption( "GDAL_HTTP_TIMEOUT", NULL ); + if( !poResult || poResult->nStatus != 0 || poResult->nDataLen == 0 ) + { + CPLDebug( "NINJA", "Failed to reach the ninjastorm server." ); + return NULL; + } + + const char* pszTextContent = reinterpret_cast(poResult->pabyData); + std::vector messages; + std::istringstream iss(pszTextContent); + std::string message; + + // Read all lines into the vector + while (std::getline(iss, message)) + { + messages.push_back(message); + } + + // Process all lines except the last two + std::ostringstream oss; + if (checkAbort) + { + for (size_t i = 0; i < messages.size(); ++i) + { + if (i == messages.size()-1) + { + oss << messages[i] << "\n"; + } + } + } + else + { + bool versionisuptodate = NinjaCheckVersions(const_cast(messages[1].c_str()), const_cast(NINJA_VERSION_STRING)); + if (!versionisuptodate) + { + oss << "You are using an outdated WindNinja version, please update to version: " << messages[1] << "

"; + + oss << "Windows: Download the new " << messages[1] + << " installer here

"; + + oss << "Linux: See the " << messages[1] << " tag in our GitHub repo
"; + + } + if (messages[4].empty() == false) + { + for (size_t i = 3; i < messages.size() - 2; ++i) + { + if (!messages[i].empty()) + { + oss << messages[i] << "\n"; + } + } + } + if (messages[4].empty() && versionisuptodate) { + return NULL; + } + } + + std::string resultingmessage = oss.str(); + char* returnString = new char[resultingmessage.length() + 1]; + std::strcpy(returnString, resultingmessage.c_str()); + CPLHTTPDestroyResult(poResult); + return returnString; + + return NULL; +} diff --git a/src/gui/GoogleMapsInterface.cpp b/src/gui/qt6/serverBridge.h similarity index 65% rename from src/gui/GoogleMapsInterface.cpp rename to src/gui/qt6/serverBridge.h index b7c271bc0..421ae1914 100644 --- a/src/gui/GoogleMapsInterface.cpp +++ b/src/gui/qt6/serverBridge.h @@ -1,10 +1,10 @@ -/****************************************************************************** + /****************************************************************************** * - * $Id: GoogleMapsInterface.cpp 1757 2012-08-07 18:40:40Z kyle.shannon $ + * $Id$ * - * Project: WindNinja - * Purpose: Class for creating an interface between javascript and Qt - * Author: Cody Posey + * Project: WindNinja Qt GUI + * Purpose: Connects the Qt GUI with the ninjastorm server + * Author: Mason Willman * ****************************************************************************** * @@ -27,9 +27,27 @@ * *****************************************************************************/ -#include "GoogleMapsInterface.h" -GoogleMapsInterface::GoogleMapsInterface(QObject *parent) - : QObject(parent) +#ifndef SERVERBRIDGE_H +#define SERVERBRIDGE_H + +#include +#include "ninja_version.h" +#include +#include +#include + +class ServerBridge { -} +public: + ServerBridge(); + void checkMessages(void); + +private: + QString version; + + bool NinjaCheckVersions(char * mostrecentversion, char * localversion); + char * NinjaQueryServerMessages(bool checkAbort); +}; + +#endif // SERVERBRIDGE_H diff --git a/src/gui/qt6/setConfigurationDialogOption.cpp b/src/gui/qt6/setConfigurationDialogOption.cpp new file mode 100644 index 000000000..02a7d41b8 --- /dev/null +++ b/src/gui/qt6/setConfigurationDialogOption.cpp @@ -0,0 +1,25 @@ +#include "setConfigurationDialogOption.h" + +setConfigurationOptionDialog::setConfigurationOptionDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::setConfigurationOptionDialog) +{ + ui->setupUi(this); + + ui->keyLineEdit->setFocus(); +} + +setConfigurationOptionDialog::~setConfigurationOptionDialog() +{ + delete ui; +} + +QString setConfigurationOptionDialog::getKey() +{ + return ui->keyLineEdit->text(); +} + +QString setConfigurationOptionDialog::getValue() +{ + return ui->valueLineEdit->text(); +} diff --git a/src/gui/qt6/setConfigurationDialogOption.h b/src/gui/qt6/setConfigurationDialogOption.h new file mode 100644 index 000000000..883e8c6af --- /dev/null +++ b/src/gui/qt6/setConfigurationDialogOption.h @@ -0,0 +1,22 @@ +#ifndef SETCONFIGURATIONDIALOGOPTION_H +#define SETCONFIGURATIONDIALOGOPTION_H + +#include "ui_setConfigurationDialogOption.h" +#include + +class setConfigurationOptionDialog : public QDialog +{ + Q_OBJECT + +public: + explicit setConfigurationOptionDialog(QWidget *parent = nullptr); + ~setConfigurationOptionDialog(); + + QString getKey(); + QString getValue(); + +private: + Ui::setConfigurationOptionDialog *ui; +}; + +#endif // SETCONFIGURATIONDIALOGOPTION_H diff --git a/src/gui/qt6/setConfigurationDialogOption.ui b/src/gui/qt6/setConfigurationDialogOption.ui new file mode 100644 index 000000000..51c5fc125 --- /dev/null +++ b/src/gui/qt6/setConfigurationDialogOption.ui @@ -0,0 +1,103 @@ + + + setConfigurationOptionDialog + + + + 0 + 0 + 400 + 143 + + + + Set Configuration Option + + + true + + + + + 10 + 100 + 380 + 32 + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + + + + 10 + 10 + 380 + 80 + + + + + + + Option + + + + + + + + + + Value + + + + + + + + + + + + + setConfigurationButtonBox + rejected() + setConfigurationOptionDialog + reject() + + + 262 + 116 + + + 219 + 63 + + + + + setConfigurationButtonBox + accepted() + setConfigurationOptionDialog + accept() + + + 352 + 117 + + + 336 + 63 + + + + + diff --git a/src/gui/qt6/splashScreen.cpp b/src/gui/qt6/splashScreen.cpp new file mode 100644 index 000000000..f8b14b55e --- /dev/null +++ b/src/gui/qt6/splashScreen.cpp @@ -0,0 +1,117 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Splash screen that will show a pixmap and n messages for n seconds + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "splashScreen.h" +#include +#include +#include + +SplashScreen::SplashScreen(const QPixmap &pixmap, QStringList list, int time) + : QSplashScreen(pixmap), messages(list), messageTime(time), i(0), j(0), SplashScreenDone(false) +{ + setWindowFlags(Qt::WindowStaysOnTopHint | Qt::SplashScreen); + + numMessages = messages.size(); + messageFadeInterval = time; + alignment = Qt::AlignLeft | Qt::AlignBottom; + map = pixmap; + + opacityEffect = new QGraphicsOpacityEffect(this); + setGraphicsEffect(opacityEffect); + opacityEffect->setOpacity(0.0); + + fadeInAnimation = new QPropertyAnimation(opacityEffect, "opacity"); + fadeInAnimation->setDuration(1000); + fadeInAnimation->setStartValue(0.0); + fadeInAnimation->setEndValue(1.0); + connect(fadeInAnimation, &QPropertyAnimation::finished, this, &SplashScreen::onFadeInFinished); + + fadeOutAnimation = new QPropertyAnimation(opacityEffect, "opacity"); + fadeOutAnimation->setDuration(1000); + fadeOutAnimation->setStartValue(1.0); + fadeOutAnimation->setEndValue(0.0); + connect(fadeOutAnimation, &QPropertyAnimation::finished, this, &SplashScreen::onFadeOutFinished); + + messageTimer = new QTimer(this); + connect(messageTimer, &QTimer::timeout, this, &SplashScreen::update); +} + +void SplashScreen::display() +{ + show(); + fadeInAnimation->start(); +} + +void SplashScreen::update() +{ + if (SplashScreenDone || ++i >= numMessages) { + messageTimer->stop(); + fadeOut(); + return; + } + + clearMessage(); + showMessage(messages.value(i), alignment, Qt::gray); + QApplication::processEvents(); +} + +void SplashScreen::fadeOut() +{ + fadeOutAnimation->start(); +} + +void SplashScreen::onFadeInFinished() +{ + showMessage(messages.value(0), alignment, Qt::gray); + messageTimer->start(messageTime); +} + +void SplashScreen::onFadeOutFinished() +{ + if (!SplashScreenDone) { + emit done(); + } + close(); +} + +void SplashScreen::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + SplashScreenDone = true; + + messageTimer->stop(); + + fadeInAnimation->stop(); + fadeOutAnimation->stop(); + fadeOutAnimation->disconnect(this); + + emit done(); + close(); + } +} diff --git a/src/gui/splash.h b/src/gui/qt6/splashScreen.h similarity index 62% rename from src/gui/splash.h rename to src/gui/qt6/splashScreen.h index 460268a49..d4889a300 100644 --- a/src/gui/splash.h +++ b/src/gui/qt6/splashScreen.h @@ -1,11 +1,10 @@ -/****************************************************************************** + /****************************************************************************** * * $Id$ * * Project: WindNinja Qt GUI - * Purpose: Splash screen that shows a pixmap and n messages for m seconds - * each. - * Author: Kyle Shannon + * Purpose: Splash screen that will show a pixmap and n messages for n seconds + * Author: Mason Willman * ****************************************************************************** * @@ -28,56 +27,40 @@ * *****************************************************************************/ -#ifndef SPLASH_H_ -#define SPLASH_H_ - -#include #include +#include #include -#include -#include -#include +#include +#include -#define FRAMES_PER_MESSAGE 20 - -/** - * \brief Class to display an image and an array of string messages - * - * It 'fakes' loading to display images forcefully. Users can click on the - * image to bypass - */ -class splashScreen : public QSplashScreen +class SplashScreen : public QSplashScreen { -Q_OBJECT + Q_OBJECT public: - splashScreen(const QPixmap &pixmap, QStringList list, int time); - QStringList stringList; + SplashScreen(const QPixmap &pixmap, QStringList list, int time); void display(); - QTimer *messageTimer; - int messageTime; - int nMessages; - int nFrames; - int i, j; - float fade_interval; - int bDone; - - QPixmap map; - QPixmap orig_map; - - Qt::Alignment alignment; - QString pad; +protected: + void mousePressEvent(QMouseEvent *event) override; signals: void done(); -protected: - void mousePressEvent(QMouseEvent *event); - -public slots: +private slots: void update(); -}; - -#endif /* SPLASH_H_ */ + void fadeOut(); + void onFadeOutFinished(); + void onFadeInFinished(); +private: + QStringList messages; + int messageTime, numMessages, i, j, messageFadeInterval; + Qt::Alignment alignment; + QPixmap map; + QTimer *messageTimer; + QGraphicsOpacityEffect *opacityEffect; + QPropertyAnimation *fadeInAnimation; + QPropertyAnimation *fadeOutAnimation; + bool SplashScreenDone; +}; diff --git a/src/gui/qt6/surfaceInput.cpp b/src/gui/qt6/surfaceInput.cpp new file mode 100644 index 000000000..c87ddd828 --- /dev/null +++ b/src/gui/qt6/surfaceInput.cpp @@ -0,0 +1,888 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Hands GUI related logic for the Surface Input Page + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "surfaceInput.h" + +SurfaceInput::SurfaceInput(Ui::MainWindow *ui, + QWebEngineView *webEngineView, + QObject* parent) + : QObject(parent), + ui(ui), + webEngineView(webEngineView) +{ + ui->timeZoneDetailsTextEdit->setVisible(false); + ui->vegetationStackedWidget->setCurrentIndex(0); + + timeZoneAllZonesCheckBoxClicked(); + + connect(ui->boundingBoxNorthLineEdit, &QLineEdit::textChanged, this, &SurfaceInput::boundingBoxLineEditsTextChanged); + connect(ui->boundingBoxSouthLineEdit, &QLineEdit::textChanged, this, &SurfaceInput::boundingBoxLineEditsTextChanged); + connect(ui->boundingBoxEastLineEdit, &QLineEdit::textChanged, this, &SurfaceInput::boundingBoxLineEditsTextChanged); + connect(ui->boundingBoxWestLineEdit, &QLineEdit::textChanged, this, &SurfaceInput::boundingBoxLineEditsTextChanged); + + connect(ui->pointRadiusLatLineEdit,&QLineEdit::textChanged, this, &SurfaceInput::pointRadiusLineEditsTextChanged); + connect(ui->pointRadiusLonLineEdit,&QLineEdit::textChanged, this, &SurfaceInput::pointRadiusLineEditsTextChanged); + connect(ui->pointRadiusRadiusLineEdit,&QLineEdit::textChanged, this, &SurfaceInput::pointRadiusLineEditsTextChanged); + + connect(ui->elevationInputFileDownloadButton, &QPushButton::clicked, this, &SurfaceInput::elevationInputFileDownloadButtonClicked); + connect(ui->elevationInputFileOpenButton, &QPushButton::clicked, this, &SurfaceInput::elevationInputFileOpenButtonClicked); + connect(ui->elevationInputFileLineEdit, &QLineEdit::textChanged, this, &SurfaceInput::elevationInputFileLineEditTextChanged); + + connect(ui->elevationInputTypeComboBox, &QComboBox::currentIndexChanged, ui->elevationInputTypeStackedWidget, &QStackedWidget::setCurrentIndex); + connect(ui->meshResolutionComboBox, &QComboBox::currentIndexChanged, this, &SurfaceInput::meshResolutionComboBoxCurrentIndexChanged); + connect(ui->meshResolutionUnitsComboBox, &QComboBox::currentIndexChanged, this, &SurfaceInput::meshResolutionUnitsComboBoxCurrentIndexChanged); + connect(ui->timeZoneComboBox, &QComboBox::currentIndexChanged, this, &SurfaceInput::timeZoneComboBoxCurrentIndexChanged); + + connect(ui->surfaceInputDownloadCancelButton, &QPushButton::clicked, this, &SurfaceInput::surfaceInputDownloadCancelButtonClicked); + connect(ui->surfaceInputDownloadButton, &QPushButton::clicked, this, &SurfaceInput::surfaceInputDownloadButtonClicked); + connect(ui->elevationInputTypePushButton, &QPushButton::clicked, this, &SurfaceInput::elevationInputTypePushButtonClicked); + connect(ui->timeZoneAllZonesCheckBox, &QCheckBox::clicked, this, &SurfaceInput::timeZoneAllZonesCheckBoxClicked); + connect(ui->timeZoneDetailsCheckBox, &QCheckBox::clicked, this, &SurfaceInput::timeZoneDetailsCheckBoxClicked); + + connect(this, &SurfaceInput::updateProgressMessageSignal, this, &SurfaceInput::updateProgressMessage, Qt::QueuedConnection); + + connect(this, &SurfaceInput::updateState, &AppState::instance(), &AppState::updateSurfaceInputState); +} + +void SurfaceInput::meshResolutionUnitsComboBoxCurrentIndexChanged(int index) +{ + if(index == 0) // meters + { + ui->meshResolutionSpinBox->setValue(ui->meshResolutionSpinBox->value() * 0.3048); + } + else // if(index == 1) // feet + { + ui->meshResolutionSpinBox->setValue(ui->meshResolutionSpinBox->value() * 3.28084); + } +} + +void SurfaceInput::elevationInputTypePushButtonClicked() +{ + if(ui->elevationInputTypePushButton->isChecked()) + { + webEngineView->page()->runJavaScript("startRectangleDrawing();"); + } + else + { + webEngineView->page()->runJavaScript("stopRectangleDrawing();"); + } +} + +void SurfaceInput::boundingBoxReceived(double north, double south, double east, double west) +{ + ui->boundingBoxNorthLineEdit->blockSignals(true); + ui->boundingBoxEastLineEdit->blockSignals(true); + ui->boundingBoxSouthLineEdit->blockSignals(true); + ui->boundingBoxWestLineEdit->blockSignals(true); + + ui->boundingBoxNorthLineEdit->setText(QString::number(north)); + ui->boundingBoxEastLineEdit->setText(QString::number(east)); + ui->boundingBoxSouthLineEdit->setText(QString::number(south)); + ui->boundingBoxWestLineEdit->setText(QString::number(west)); + + ui->boundingBoxNorthLineEdit->blockSignals(false); + ui->boundingBoxEastLineEdit->blockSignals(false); + ui->boundingBoxSouthLineEdit->blockSignals(false); + ui->boundingBoxWestLineEdit->blockSignals(false); + + double pointRadius[3]; + computePointRadius(north, east, south, west, pointRadius); + + ui->pointRadiusLatLineEdit->blockSignals(true); + ui->pointRadiusLonLineEdit->blockSignals(true); + ui->pointRadiusRadiusLineEdit->blockSignals(true); + + ui->pointRadiusLatLineEdit->setText(QString::number(pointRadius[0])); + ui->pointRadiusLonLineEdit->setText(QString::number(pointRadius[1])); + ui->pointRadiusRadiusLineEdit->setText(QString::number(pointRadius[2])); + + ui->pointRadiusLatLineEdit->blockSignals(false); + ui->pointRadiusLonLineEdit->blockSignals(false); + ui->pointRadiusRadiusLineEdit->blockSignals(false); + + ui->elevationInputTypePushButton->setChecked(false); +} + + +void SurfaceInput::boundingBoxLineEditsTextChanged() +{ + if(ui->elevationInputTypeComboBox->currentIndex() == 0) + { + bool isNorthValid, isEastValid, isSouthValid, isWestValid; + double north = ui->boundingBoxNorthLineEdit->text().toDouble(&isNorthValid); + double east = ui->boundingBoxEastLineEdit->text().toDouble(&isEastValid); + double south = ui->boundingBoxSouthLineEdit->text().toDouble(&isSouthValid); + double west = ui->boundingBoxWestLineEdit->text().toDouble(&isWestValid); + + if (isNorthValid && isEastValid && isSouthValid && isWestValid) + { + QString js = QString("drawBoundingBox(%1, %2, %3, %4);") + .arg(north, 0, 'f', 10) + .arg(south, 0, 'f', 10) + .arg(east, 0, 'f', 10) + .arg(west, 0, 'f', 10); + webEngineView->page()->runJavaScript(js); + } + } +} + +void SurfaceInput::pointRadiusLineEditsTextChanged() +{ + if (ui->elevationInputTypeComboBox->currentIndex() == 1) + { + bool isLatValid, isLonValid, isRadiusValid; + double lat = ui->pointRadiusLatLineEdit->text().toDouble(&isLatValid); + double lon = ui->pointRadiusLonLineEdit->text().toDouble(&isLonValid); + double radius = ui->pointRadiusRadiusLineEdit->text().toDouble(&isRadiusValid); + double boundingBox[4]; + + if(isLatValid && isLonValid && isRadiusValid) + { + computeBoundingBox(lat, lon, radius, boundingBox); + QString js = QString("drawBoundingBox(%1, %2, %3, %4);") + .arg(boundingBox[0], 0, 'f', 10) + .arg(boundingBox[2], 0, 'f', 10) + .arg(boundingBox[1], 0, 'f', 10) + .arg(boundingBox[3], 0, 'f', 10); + webEngineView->page()->runJavaScript(js); + } + } +} + +void SurfaceInput::surfaceInputDownloadCancelButtonClicked() +{ + ui->inputsStackedWidget->setCurrentIndex(3); + + ui->elevationInputTypeComboBox->setCurrentIndex(0); + ui->elevationFileTypeComboBox->setCurrentIndex(0); + ui->elevationInputTypePushButton->setChecked(false); + + ui->boundingBoxNorthLineEdit->clear(); + ui->boundingBoxEastLineEdit->clear(); + ui->boundingBoxSouthLineEdit->clear(); + ui->boundingBoxWestLineEdit->clear(); + + ui->pointRadiusLatLineEdit->clear(); + ui->pointRadiusLonLineEdit->clear(); + ui->pointRadiusRadiusLineEdit->clear(); + + webEngineView->page()->runJavaScript("stopRectangleDrawing();"); + + if(!ui->elevationInputFileLineEdit->property("fullpath").toString().isEmpty()) + { + QStringList cornerStrs; + for (int i = 0; i < 8; ++i) + { + cornerStrs << QString::number(DEMCorners[i], 'f', 8); + } + QString js = QString("drawDEM([%1]);").arg(cornerStrs.join(", ")); + webEngineView->page()->runJavaScript(js); + } +} + +void SurfaceInput::surfaceInputDownloadButtonClicked() +{ + QVector boundingBox = { + ui->boundingBoxNorthLineEdit->text().toDouble(), + ui->boundingBoxEastLineEdit->text().toDouble(), + ui->boundingBoxSouthLineEdit->text().toDouble(), + ui->boundingBoxWestLineEdit->text().toDouble() + }; + + double resolution = 30; + + QString defaultName = ""; + QString downloadsPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + QDir dir(downloadsPath); + QString fullPath = dir.filePath(defaultName); + QString demFilePath = QFileDialog::getSaveFileName(ui->centralwidget, "Save DEM File", fullPath, "TIF Files (*.tif)"); + if (demFilePath.isEmpty()) { + return; + } + if (!demFilePath.endsWith(".tif", Qt::CaseInsensitive)) { + demFilePath += ".tif"; + } + ui->elevationInputFileLineEdit->setProperty("fullpath", demFilePath); + std::string demFile = demFilePath.toStdString(); + + std::string fetchType; + switch(ui->elevationFileTypeComboBox->currentIndex()) + { + case 0: + fetchType = "srtm"; + break; + case 1: + fetchType = "gmted"; + break; + case 2: + fetchType = "lcp"; + break; + } + + startFetchDEM(boundingBox, demFile, resolution, fetchType); +} + +void SurfaceInput::elevationInputFileDownloadButtonClicked() +{ + ui->inputsStackedWidget->setCurrentIndex(16); +} + +void SurfaceInput::meshResolutionComboBoxCurrentIndexChanged(int index) +{ + if (index == 3) + { + ui->meshResolutionSpinBox->setEnabled(true); + } + else + { + ui->meshResolutionSpinBox->setEnabled(false); + } + + ui->meshResolutionSpinBox->setValue(computeMeshResolution(ui->meshResolutionComboBox->currentIndex(), ui->momentumSolverCheckBox->isChecked())); + updateMeshResolutionByUnits(); +} + +void SurfaceInput::elevationInputFileLineEditTextChanged(const QString &demFilePath) +{ + QFileInfo file(demFilePath); + ui->outputDirectoryLineEdit->setText(file.absolutePath()); + + computeDEMFile(demFilePath); + if(demFileType == "LCP") + { + ui->vegetationStackedWidget->setCurrentIndex(1); + } + else + { + ui->vegetationStackedWidget->setCurrentIndex(0); + } + + ui->meshResolutionSpinBox->setValue(computeMeshResolution(ui->meshResolutionComboBox->currentIndex(), ui->momentumSolverCheckBox->isChecked())); + updateMeshResolutionByUnits(); + + QStringList cornerStrs; + for (int i = 0; i < 8; ++i) + cornerStrs << QString::number(DEMCorners[i], 'f', 8); + QString js = QString("drawDEM([%1]);").arg(cornerStrs.join(", ")); + webEngineView->page()->runJavaScript(js); + + ui->elevationInputFileLineEdit->setProperty("fullpath", demFilePath); + QSignalBlocker blocker(ui->elevationInputFileLineEdit); + ui->elevationInputFileLineEdit->setText(QFileInfo(demFilePath).fileName()); + ui->elevationInputFileLineEdit->setToolTip(demFilePath); + + emit updateState(); + emit updateTreeView(); +} + +void SurfaceInput::elevationInputFileOpenButtonClicked() +{ + QString directoryPath; + if(!ui->elevationInputFileLineEdit->property("fullpath").toString().isEmpty()) + { + directoryPath = ui->elevationInputFileLineEdit->property("fullpath").toString(); + } + else + { + directoryPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + } + + QString demFilePath = QFileDialog::getOpenFileName(ui->centralwidget, "Select a file", directoryPath, "(*.tif);;All Files (*)"); + + if (demFilePath.isEmpty()) + { + if (!ui->elevationInputFileLineEdit->property("fullpath").toString().isEmpty()) + { + ui->elevationInputFileLineEdit->setText(ui->elevationInputFileLineEdit->property("fullpath").toString()); + ui->elevationInputFileLineEdit->setToolTip(ui->elevationInputFileLineEdit->property("fullpath").toString()); + } + return; + } + + ui->elevationInputFileLineEdit->setText(demFilePath); + ui->elevationInputFileLineEdit->setToolTip(demFilePath); +} + +void SurfaceInput::startFetchDEM(QVector boundingBox, std::string demFile, double resolution, std::string fetchType) +{ + emit writeToConsoleSignal("Fetching DEM file..."); + + progress = new QProgressDialog("Fetching DEM file...", QString(), 0, 0, ui->centralwidget); + progress->setWindowModality(Qt::WindowModal); + progress->setCancelButton(nullptr); + progress->setMinimumDuration(0); + progress->setAutoClose(true); + progress->show(); + + futureWatcher = new QFutureWatcher(this); + QFuture future = QtConcurrent::run(&SurfaceInput::fetchDEMFile, this, boundingBox, demFile, resolution, fetchType); + futureWatcher->setFuture(future); + + connect(futureWatcher, &QFutureWatcher::finished, this, &SurfaceInput::fetchDEMFinished); +} + +void SurfaceInput::fetchDEMFinished() +{ + // get the return value of the QtConcurrent::run() function + int result = futureWatcher->future().result(); + + if(result == NINJA_SUCCESS) + { + emit writeToConsoleSignal("Finished fetching DEM file.", Qt::darkGreen); + + if (progress) + { + progress->close(); + progress->deleteLater(); + progress = nullptr; + } + + ui->elevationInputFileLineEdit->setText(ui->elevationInputFileLineEdit->property("fullpath").toString()); + ui->inputsStackedWidget->setCurrentIndex(3); + + } else + { + emit writeToConsoleSignal("Failed to fetch DEM file."); + } + + // delete the futureWatcher every time, whether success or failure + if (futureWatcher) + { + futureWatcher->deleteLater(); + futureWatcher = nullptr; + } +} + +void SurfaceInput::timeZoneComboBoxCurrentIndexChanged(int index) +{ + QString currentTimeZone = ui->timeZoneComboBox->currentText(); + QString timeZoneDetails = fetchTimeZoneDetails(currentTimeZone); + ui->timeZoneDetailsTextEdit->setText(timeZoneDetails); + ui->weatherStationTimeZoneLabel->setText("Current Time Zone: " + currentTimeZone); + ui->downloadBetweenDatesTimeZoneLabel->setText("Current Time Zone: " + currentTimeZone); +} + +void SurfaceInput::timeZoneAllZonesCheckBoxClicked() +{ + AppState& state = AppState::instance(); + + bool isShowAllTimeZonesSelected = ui->timeZoneAllZonesCheckBox->isChecked(); + QVector> displayData = fetchAllTimeZones(isShowAllTimeZonesSelected); + + ui->timeZoneComboBox->clear(); + for (const QVector& zone : displayData) + { + if (!zone.isEmpty()) + { + ui->timeZoneComboBox->addItem(zone[0]); + } + } + + // Default to America/Denver + ui->timeZoneComboBox->setCurrentText("America/Denver"); +} + +void SurfaceInput::timeZoneDetailsCheckBoxClicked() +{ + ui->timeZoneDetailsTextEdit->setVisible(ui->timeZoneDetailsCheckBox->isChecked()); +} + +QString SurfaceInput::fetchTimeZoneDetails(QString currentTimeZone) +{ + QVector matchedRow; + QFile file(":/date_time_zonespec.csv"); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + qWarning() << "Failed to open date_time_zonespec.csv"; + qDebug() << "No data found"; + } + + QTextStream in(&file); + bool firstLine = true; + + while (!in.atEnd()) + { + QString line = in.readLine(); + + if (firstLine) + { + firstLine = false; + continue; // skip header + } + + QStringList tokens = line.split(",", Qt::KeepEmptyParts); + QVector row; + + for (const QString& token : tokens) + row.append(token.trimmed().remove("\"")); + + QString fullZone = row.mid(0, 1).join("/"); + + if (fullZone == currentTimeZone) + { + matchedRow = row; + break; + } + } + + file.close(); + + if (matchedRow.isEmpty()) + { + qDebug() << "No matching time zone found."; + } + + QString standardName = matchedRow.value(2); + QString daylightName = matchedRow.value(4); + QString stdOffsetStr = matchedRow.value(5); + QString dstAdjustStr = matchedRow.value(6); + + auto timeToSeconds = [](const QString& t) -> int + { + QString s = t.trimmed(); + bool negative = s.startsWith('-'); + s = s.remove(QChar('+')).remove(QChar('-')); + + QStringList parts = s.split(':'); + if (parts.size() != 3) return 0; + + int h = parts[0].toInt(); + int m = parts[1].toInt(); + int sec = parts[2].toInt(); + + int total = h * 3600 + m * 60 + sec; + return negative ? -total : total; + }; + + // Convert total seconds back to HH:MM:SS with sign + auto secondsToTime = [](int totalSec) -> QString + { + QChar sign = totalSec < 0 ? '-' : '+'; + totalSec = std::abs(totalSec); + + int h = totalSec / 3600; + int m = (totalSec % 3600) / 60; + int s = totalSec % 60; + + return QString("%1%2:%3:%4") + .arg(sign) + .arg(h, 2, 10, QChar('0')) + .arg(m, 2, 10, QChar('0')) + .arg(s, 2, 10, QChar('0')); + }; + + int stdSecs = timeToSeconds(stdOffsetStr); + int dstSecs = timeToSeconds(dstAdjustStr); + QString combinedOffsetStr = secondsToTime(stdSecs + dstSecs); + + return QString("Standard Name:\t\t%1\n" + "Daylight Name:\t\t%2\n" + "Standard Offset from UTC:\t%3\n" + "Daylight Offset from UTC:\t%4") + .arg(standardName) + .arg(daylightName) + .arg(stdOffsetStr) + .arg(combinedOffsetStr); + +} + +QVector> SurfaceInput::fetchAllTimeZones(bool isShowAllTimeZonesSelected) +{ + QVector> fullData; + QVector> americaData; + + QFile file(":/date_time_zonespec.csv"); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + qDebug() << "Failed to open CSV file."; + return fullData; + } + + QTextStream in(&file); + bool firstLine = true; + + while (!in.atEnd()) + { + QString line = in.readLine(); + + if (firstLine) + { + firstLine = false; + continue; + } + + QStringList tokens = line.split(",", Qt::KeepEmptyParts); + QVector row; + for (const QString& token : tokens) + { + row.append(token.trimmed().remove('"')); + } + + if (!row.isEmpty()) + { + fullData.append(row); + } + + if (!row.isEmpty()) + { + QStringList parts = row[0].split("/", Qt::KeepEmptyParts); + if (!parts.isEmpty() && parts[0] == "America" || row[0] == "Pacific/Honolulu") + { + americaData.append(row); + } + } + } + + file.close(); + + if (isShowAllTimeZonesSelected) + { + return fullData; + } + else + { + return americaData; + } +} + +void SurfaceInput::updateProgressMessage(const QString message) +{ + progress->setLabelText(message); + progress->setWindowTitle(tr("Error")); + progress->setCancelButtonText(tr("Close")); + progress->setAutoClose(false); + progress->setAutoReset(false); + progress->setRange(0, 1); + progress->setValue(progress->maximum()); +} + +static void comMessageHandler(const char *pszMessage, void *pUser) +{ + SurfaceInput *self = static_cast(pUser); + + std::string msg = pszMessage; + if( msg.substr(msg.size()-1, 1) == "\n") + { + msg = msg.substr(0, msg.size()-1); + } + + size_t pos; + size_t startPos; + size_t endPos; + std::string clipStr; + + if( msg.find("Exception caught: ") != msg.npos || msg.find("ERROR: ") != msg.npos ) + { + if( msg.find("Exception caught: ") != msg.npos ) + { + pos = msg.find("Exception caught: "); + startPos = pos+18; + } + else // if( msg.find("ERROR: ") != msg.npos ) + { + pos = msg.find("ERROR: "); + startPos = pos+7; + } + clipStr = msg.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); + //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); + if( clipStr == "Cannot determine exception type." ) + { + emit self->updateProgressMessageSignal(QString::fromStdString("SurfaceFetch ended with unknown error")); + emit self->writeToConsoleSignal(QString::fromStdString("unknown SurfaceFetch error"), Qt::red); + } + else + { + emit self->updateProgressMessageSignal(QString::fromStdString("SurfaceFetch ended in error:\n"+clipStr)); + emit self->writeToConsoleSignal(QString::fromStdString("SurfaceFetch error: "+clipStr), Qt::red); + } + } + else if( msg.find("Warning: ") != msg.npos ) + { + if( msg.find("Warning: ") != msg.npos ) + { + pos = msg.find("Warning: "); + startPos = pos+9; + } + clipStr = msg.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); + //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); + emit self->updateProgressMessageSignal(QString::fromStdString("SurfaceFetch ended in warning:\n"+clipStr)); + emit self->writeToConsoleSignal(QString::fromStdString("SurfaceFetch warning: "+clipStr), Qt::yellow); + } + else + { + emit self->updateProgressMessageSignal(QString::fromStdString(msg)); + emit self->writeToConsoleSignal(QString::fromStdString(msg)); + } +} + +int SurfaceInput::fetchDEMFile(QVector boundingBox, std::string demFile, double resolution, std::string fetchType) +{ + NinjaToolsH* ninjaTools = NULL; + char ** papszOptions = NULL; + NinjaErr ninjaErr = 0; + + ninjaTools = NinjaMakeTools(); + + ninjaErr = NinjaSetToolsComMessageHandler(ninjaTools, &comMessageHandler, this, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetToolsComMessageHandler(): ninjaErr =" << ninjaErr; + } + + ninjaErr = NinjaFetchDEMBBox(ninjaTools, boundingBox.data(), demFile.c_str(), resolution, strdup(fetchType.c_str()), papszOptions); // some errors and warnings are caught, but only as error codes, not as messages. Would need to update how we do the messaging within the various SurfaceFetch calls themselves. CPLError( CE_Warning, ...); and CPLError( CE_Failure, ...); with return of an error code seems hard to try/catch with ninjaCom. + //ninjaErr = NinjaFetchDEMBBox(ninjaTools, boundingBox.data(), ".", resolution, strdup(fetchType.c_str()), papszOptions); // error was caught, but message is not properly passed + //ninjaErr = NinjaFetchDEMBBox(ninjaTools, boundingBox.data(), demFile.c_str(), resolution, "fudge", papszOptions); // actually catches this error, with a good message + //ninjaErr = NinjaFetchDEMBBox(ninjaTools, boundingBox.data(), demFile.c_str(), -10.0, strdup(fetchType.c_str()), papszOptions); // error was caught, but not even a message to be passed around with this one + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaFetchDEMBBox: ninjaErr =" << ninjaErr; + return ninjaErr; + } + + return NINJA_SUCCESS; +} + +void SurfaceInput::computeDEMFile(QString filePath) +{ + double adfGeoTransform[6]; + GDALDataset *poInputDS; + poInputDS = (GDALDataset*)GDALOpen(filePath.toStdString().c_str(), GA_ReadOnly); + + QString GDALDriverName = poInputDS->GetDriver()->GetDescription(); + if(GDALDriverName == "AAIGrid") + { + demFileType = "ASC"; + } + else if (GDALDriverName == "LCP") + { + demFileType = "LCP"; + } + else if (GDALDriverName == "GTiff") + { + int bandCount = GDALGetRasterCount(poInputDS); + if(bandCount >1) + { + demFileType = "LCP"; + } + else + { + demFileType = "GTIFF"; + } + } + else if (GDALDriverName == "IMG") + { + demFileType = "IMG"; + } + + GDALXSize = poInputDS->GetRasterXSize(); + GDALYSize = poInputDS->GetRasterYSize(); + GDALGetCorners(poInputDS, DEMCorners); + + double latitude, longitude; + GDALGetCenter(poInputDS, &longitude, &latitude); + std::string timeZone = FetchTimeZone(longitude, latitude, NULL); + int index = ui->timeZoneComboBox->findText(QString::fromStdString(timeZone)); + if (index >= 0) + { + ui->timeZoneComboBox->setCurrentIndex(index); + } + + if (poInputDS->GetGeoTransform(adfGeoTransform) == CE_None) + { + double c1, c2; + c1 = adfGeoTransform[1]; + c2 = adfGeoTransform[5]; + if (abs(c1) == abs(c2)) { + GDALCellSize = abs(c1); + } else { + GDALClose((GDALDatasetH)poInputDS); + } + } + + GDALRasterBand* band = poInputDS->GetRasterBand(1); + int gotMin = 0, gotMax = 0; + double minVal = band->GetMinimum(&gotMin); + double maxVal = band->GetMaximum(&gotMax); + if (!gotMin || !gotMax) { + band->ComputeStatistics(false, &minVal, &maxVal, nullptr, nullptr, nullptr, nullptr); + } + + GDALMinValue = minVal; + GDALMaxValue = maxVal; + + GDALClose((GDALDatasetH)poInputDS); +} + +double SurfaceInput::computeMeshResolution(int index, bool isMomemtumChecked) +{ + int coarse = 4000; + int medium = 10000; + int fine = 20000; + double meshResolution = 200.0; + + if( GDALCellSize == 0.0 || GDALXSize == 0 || GDALYSize == 0 ) + { + return meshResolution; + } + +#ifdef NINJAFOAM + if (isMomemtumChecked) { + coarse = 25000; + medium = 50000; + fine = 100000; + } +#endif //NINJAFOAM + + int targetNumHorizCells = fine; + switch (index) + { + case 0: + targetNumHorizCells = coarse; + break; + case 1: + targetNumHorizCells = medium; + break; + case 2: + targetNumHorizCells = fine; + break; + case 3: // custom + return ui->meshResolutionSpinBox->value(); + default: + return ui->meshResolutionSpinBox->value(); + } + + double XLength = GDALXSize * GDALCellSize; + double YLength = GDALYSize * GDALCellSize; + double nXcells = 2 * std::sqrt((double)targetNumHorizCells) * (XLength / (XLength + YLength)); + double nYcells = 2 * std::sqrt((double)targetNumHorizCells) * (YLength / (XLength + YLength)); + + double XCellSize = XLength / nXcells; + double YCellSize = YLength / nYcells; + + meshResolution = (XCellSize + YCellSize) / 2; + +#ifdef NINJAFOAM + if (isMomemtumChecked) + { + XLength = GDALXSize * GDALCellSize; + YLength = GDALYSize * GDALCellSize; + + double dz = GDALMaxValue - GDALMinValue; + double ZLength = std::max((0.1 * std::max(XLength, YLength)), (dz + 0.1 * dz)); + double zmin, zmax; + zmin = GDALMaxValue + 0.05 * ZLength; //zmin (above highest point in DEM for MDM) + zmax = GDALMaxValue + ZLength; //zmax + + double volume; + double cellCount; + double cellVolume; + + volume = XLength * YLength * (zmax-zmin); //volume of blockMesh + cellCount = targetNumHorizCells * 0.5; // cell count in volume 1 + cellVolume = volume/cellCount; // volume of 1 cell in blockMesh + double side = std::pow(cellVolume, (1.0/3.0)); // length of side of cell in blockMesh + + //determine number of rounds of refinement + int nCellsToAdd = 0; + int refinedCellCount = 0; + int nCellsInLowestLayer = int(XLength/side) * int(YLength/side); + int nRoundsRefinement = 0; + while(refinedCellCount < (0.5 * targetNumHorizCells)) + { + nCellsToAdd = nCellsInLowestLayer * 8; //each cell is divided into 8 cells + refinedCellCount += nCellsToAdd - nCellsInLowestLayer; //subtract the parent cells + nCellsInLowestLayer = nCellsToAdd/2; //only half of the added cells are in the lowest layer + nRoundsRefinement += 1; + } + + meshResolution = side/(nRoundsRefinement*2.0); + } +#endif //NINJAFOAM + + return meshResolution; +} + +void SurfaceInput::updateMeshResolutionByUnits() +{ + // units are expected to always already be calculated to be in meters + // UNLESS it is CUSTOM, then it needs to be left in whatever units it shows up in, + // units are then only altered when updating the units combo box + if(ui->meshResolutionComboBox->currentIndex() != 3) + { + if(ui->meshResolutionUnitsComboBox->currentIndex() == 1) // feet + { + ui->meshResolutionSpinBox->setValue(ui->meshResolutionSpinBox->value() * 3.28084); + } + } +} + +void SurfaceInput::computeBoundingBox(double centerLat, double centerLon, double radius, double boundingBox[4]) +{ + const double EARTH_RADIUS_MILES = 3958.7613; + const double DEG_TO_RAD = M_PI / 180.0; + + double deltaLat = radius / (2.0 * M_PI * EARTH_RADIUS_MILES / 360.0); + double latRadius = EARTH_RADIUS_MILES * std::cos(centerLat * DEG_TO_RAD); + double deltaLon = radius / (2.0 * M_PI * latRadius / 360.0); + + boundingBox[0] = centerLat + deltaLat; // North + boundingBox[1] = centerLon + deltaLon; // East + boundingBox[2] = centerLat - deltaLat; // South + boundingBox[3] = centerLon - deltaLon; // West +} + +void SurfaceInput::computePointRadius(double north, double east, double south, double west, double pointRadius[3]) +{ + const double EARTH_RADIUS_MILES = 3958.7613; + const double DEG_TO_RAD = M_PI / 180.0; + + double centerLat = (north + south) / 2.0; + double centerLon = (east + west) / 2.0; + + double deltaLat = std::abs(north - south) / 2.0; + double deltaLon = std::abs(east - west) / 2.0; + + double latMiles = (2.0 * M_PI * EARTH_RADIUS_MILES / 360.0) * deltaLat; + + double latRadius = EARTH_RADIUS_MILES * std::cos(centerLat * DEG_TO_RAD); + double lonMiles = (2.0 * M_PI * latRadius / 360.0) * deltaLon; + + double radius = (latMiles + lonMiles) / 2.0; + + pointRadius[0] = centerLat; + pointRadius[1] = centerLon; + pointRadius[2] = radius; +} + diff --git a/src/gui/qt6/surfaceInput.h b/src/gui/qt6/surfaceInput.h new file mode 100644 index 000000000..389f95e1d --- /dev/null +++ b/src/gui/qt6/surfaceInput.h @@ -0,0 +1,108 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Hands GUI related logic for the Surface Input Page + * Author: Mason Willman + * + ****************************************************************************** + * + * 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 SURFACEINPUT_H +#define SURFACEINPUT_H + +#include "ui_mainWindow.h" +#include "appState.h" +#include "windninja.h" +#include "gdal_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SurfaceInput : public QObject +{ + Q_OBJECT + +public: + SurfaceInput(Ui::MainWindow* ui, + QWebEngineView* webEngineView, + QObject* parent = nullptr); + double computeMeshResolution(int index, bool isMomemtumChecked); + void updateMeshResolutionByUnits(); + +signals: + void updateState(); + void updateTreeView(); + void updateProgressMessageSignal(const QString &msg); + void writeToConsoleSignal(const QString &msg, QColor color=Qt::black); + +public slots: + void boundingBoxReceived(double north, double south, double east, double west); + void elevationInputFileOpenButtonClicked(); + +private slots: + void surfaceInputDownloadCancelButtonClicked(); + void surfaceInputDownloadButtonClicked(); + void meshResolutionUnitsComboBoxCurrentIndexChanged(int index); + void elevationInputTypePushButtonClicked(); + void boundingBoxLineEditsTextChanged(); + void pointRadiusLineEditsTextChanged(); + void elevationInputFileDownloadButtonClicked(); + void elevationInputFileLineEditTextChanged(const QString &arg1); + void meshResolutionComboBoxCurrentIndexChanged(int index); + void fetchDEMFinished(); + void timeZoneDetailsCheckBoxClicked(); + void timeZoneComboBoxCurrentIndexChanged(int index); + void timeZoneAllZonesCheckBoxClicked(); + void updateProgressMessage(const QString message); + +private: + Ui::MainWindow *ui; + QWebEngineView *webEngineView; + + QProgressDialog *progress; + QFutureWatcher *futureWatcher; + + QString currentDEMFilePath; + QString demFileType; + int GDALXSize, GDALYSize; + double GDALCellSize, GDALMaxValue, GDALMinValue; + double DEMCorners[8]; + + QString fetchTimeZoneDetails(QString currentTimeZone); + QVector> fetchAllTimeZones(bool isShowAllTimeZonesSelected); + int fetchDEMFile(QVector boundingBox, std::string demFile, double resolution, std::string fetchType); + void computeDEMFile(QString filePath); + void computeBoundingBox(double centerLat, double centerLon, double radius, double boundingBox[4]); + void computePointRadius(double north, double east, double south, double west, double pointRadius[3]); + void startFetchDEM(QVector boundingBox, std::string demFile, double resolution, std::string fetchType); +}; + +#endif // SURFACEINPUT_H diff --git a/src/gui/qt6/weatherModelInput.cpp b/src/gui/qt6/weatherModelInput.cpp new file mode 100644 index 000000000..3bf3ac9ec --- /dev/null +++ b/src/gui/qt6/weatherModelInput.cpp @@ -0,0 +1,508 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Hands GUI related logic for the Weather Model Page + * Author: Mason Willman + * + ****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include "weatherModelInput.h" + +WeatherModelInput::WeatherModelInput(Ui::MainWindow* ui, QObject* parent) + : QObject(parent), + ui(ui) +{ + initNinjaTools(); + + ui->pastcastGroupBox->hide(); + int identifiersSize = 0; + const char** identifiers = NinjaGetAllWeatherModelIdentifiers(ninjaTools, &identifiersSize); + for (int i = 0; i < identifiersSize; i++) + { + ui->weatherModelComboBox->addItem(identifiers[i]); + } + NinjaFreeAllWeatherModelIdentifiers(identifiers, identifiersSize); + + weatherModelComboBoxCurrentIndexChanged(0); + updatePastcastDateTimeEdits(); + + connect(ui->weatherModelGroupBox, &QGroupBox::toggled, this, &WeatherModelInput::weatherModelGroupBoxToggled); + connect(ui->weatherModelDownloadButton, &QPushButton::clicked, this, &WeatherModelInput::weatherModelDownloadButtonClicked); + connect(ui->weatherModelComboBox, &QComboBox::currentIndexChanged, this, &WeatherModelInput::weatherModelComboBoxCurrentIndexChanged); + connect(ui->weatherModelTimeSelectAllButton, &QPushButton::clicked, this, &WeatherModelInput::weatherModelTimeSelectAllButtonClicked); + connect(ui->weatherModelTimeSelectNoneButton, &QPushButton::clicked, this, &WeatherModelInput::weatherModelTimeSelectNoneButtonClicked); + connect(ui->timeZoneComboBox, &QComboBox::currentTextChanged, this, &WeatherModelInput::updatePastcastDateTimeEdits); + + connect(this, &WeatherModelInput::updateProgressMessageSignal, this, &WeatherModelInput::updateProgressMessage, Qt::QueuedConnection); +} + +void WeatherModelInput::weatherModelDownloadButtonClicked() +{ + emit writeToConsoleSignal("Fetching weather model data..."); + + progress = new QProgressDialog("Fetching Forecast Data...", QString(), 0, 0, ui->centralwidget); + progress->setWindowModality(Qt::WindowModal); + progress->setCancelButton(nullptr); + progress->setMinimumDuration(0); + progress->setAutoClose(true); + progress->show(); + + int hours = ui->weatherModelSpinBox->value(); + + futureWatcher = new QFutureWatcher(this); + QFuture future; + + if (ui->weatherModelComboBox->currentText().contains("PASTCAST")) + { + progress->setLabelText("Fetching Pastcast Data..."); + + QDateTime start = ui->pastcastStartDateTimeEdit->dateTime(); + QDateTime end = ui->pastcastEndDateTimeEdit->dateTime(); + + future = QtConcurrent::run( + &WeatherModelInput::fetchPastcastWeather, + this, + ninjaTools, + ui->weatherModelComboBox->currentText(), + ui->elevationInputFileLineEdit->property("fullpath").toString(), + ui->timeZoneComboBox->currentText(), + start.date().year(), start.date().month(), start.date().day(), start.time().hour(), + end.date().year(), end.date().month(), end.date().day(), end.time().hour()); + } + else + { + future = QtConcurrent::run( + &WeatherModelInput::fetchForecastWeather, + this, + ninjaTools, + ui->weatherModelComboBox->currentText(), + ui->elevationInputFileLineEdit->property("fullpath").toString(), + hours); + } + + futureWatcher->setFuture(future); + + connect(futureWatcher, &QFutureWatcher::finished, + this, &WeatherModelInput::weatherModelDownloadFinished); +} + +void WeatherModelInput::updateProgressMessage(const QString message) +{ +// QMessageBox::critical( +// nullptr, +// QApplication::tr("Error"), +// message +// ); + progress->setLabelText(message); + progress->setWindowTitle(tr("Error")); + progress->setCancelButtonText(tr("Close")); + progress->setAutoClose(false); + progress->setAutoReset(false); + progress->setRange(0, 1); + progress->setValue(progress->maximum()); +} + +static void comMessageHandler(const char *pszMessage, void *pUser) +{ + WeatherModelInput *self = static_cast(pUser); + + std::string msg = pszMessage; + if( msg.substr(msg.size()-1, 1) == "\n") + { + msg = msg.substr(0, msg.size()-1); + } + + size_t pos; + size_t startPos; + size_t endPos; + std::string clipStr; + + if( msg.find("Exception caught: ") != msg.npos || msg.find("ERROR: ") != msg.npos ) + { + if( msg.find("Exception caught: ") != msg.npos ) + { + pos = msg.find("Exception caught: "); + startPos = pos+18; + } + else // if( msg.find("ERROR: ") != msg.npos ) + { + pos = msg.find("ERROR: "); + startPos = pos+7; + } + clipStr = msg.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + //emit self->updateProgressMessageSignal(QString::fromStdString(clipStr)); + //emit self->writeToConsoleSignal(QString::fromStdString(clipStr)); + if( clipStr == "Cannot determine exception type." ) + { + emit self->updateProgressMessageSignal(QString::fromStdString("WeatherModelFetch ended with unknown error")); + emit self->writeToConsoleSignal(QString::fromStdString("unknown WeatherModelFetch error"), Qt::red); + } + else + { + emit self->updateProgressMessageSignal(QString::fromStdString("WeatherModelFetch ended in error:\n"+clipStr)); + emit self->writeToConsoleSignal(QString::fromStdString("WeatherModelFetch error: "+clipStr), Qt::red); + } + } + else if( msg.find("Warning: ") != msg.npos ) + { + if( msg.find("Warning: ") != msg.npos ) + { + pos = msg.find("Warning: "); + startPos = pos+9; + } + clipStr = msg.substr(startPos); + //std::cout << "clipStr = \"" << clipStr << "\"" << std::endl; + //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); + } + else + { + emit self->updateProgressMessageSignal(QString::fromStdString(msg)); + emit self->writeToConsoleSignal(QString::fromStdString(msg)); + } +} + +int WeatherModelInput::fetchForecastWeather( + NinjaToolsH* ninjaTools, + const QString& modelIdentifierStr, + const QString& demFileStr, + int hours) +{ + QByteArray modelIdentifierTemp = modelIdentifierStr.toUtf8(); + QByteArray demFileTemp = demFileStr.toUtf8(); + + const char* modelIdentifier = modelIdentifierTemp.constData(); + const char* demFile = demFileTemp.constData(); + + NinjaErr ninjaErr = NinjaFetchWeatherData(ninjaTools, modelIdentifier, demFile, hours); // some errors and warnings are caught, but only as error codes, not as messages, for instance "ERROR 1: HTTP error code : 404", "ERROR 1: Failed to download file.", "Warning 1: Failed to download forecast, stepping back one forecast run time step.". Would need to update how we do the messaging within the various wxModelInitialization fetch calls themselves. CPLError( CE_Warning, ...); and CPLError( CE_Failure, ...); with return of an error code seems hard to try/catch with ninjaCom. + //NinjaErr ninjaErr = NinjaFetchWeatherData(ninjaTools, "fudge", demFile, hours); // works with proper error message, after non-caught message "ERROR 4: fudge: No such file or directory". + //NinjaErr ninjaErr = NinjaFetchWeatherData(ninjaTools, modelIdentifier, "fudge", hours); // works with proper error message, after non-caught message "ERROR 4: fudge: No such file or directory". + ////NinjaErr ninjaErr = NinjaFetchWeatherData(ninjaTools, modelIdentifier, demFile, -1); // um, this one somehow went forward as if it was a correct value? Stepped back one, but in the end I got a single weather model data file, not the usual 2 when it steps back like that. + //NinjaErr ninjaErr = NinjaFetchWeatherData(ninjaTools, modelIdentifier, demFile, 0); // only works as a test for certain specific weather models, that have minimums of 3 or 6 hrs, like UCAR-NDFD-CONUS-2.5-KM (currently breaking as a model, even on qt4 gui), UCAR-NAM-CONUS-12-KM (this works great as a test) + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaFetchWeatherData: ninjaErr =" << ninjaErr; + } + + return ninjaErr; +} + +int WeatherModelInput::fetchPastcastWeather( + NinjaToolsH* ninjaTools, + const QString& modelIdentifierStr, + const QString& demFileStr, + const QString& timeZoneStr, + int startYear, int startMonth, int startDay, int startHour, + int endYear, int endMonth, int endDay, int endHour) +{ + QByteArray modelIdentifierTemp = modelIdentifierStr.toUtf8(); + QByteArray demFileTemp = demFileStr.toUtf8(); + QByteArray timeZoneTemp = timeZoneStr.toUtf8(); + + const char* modelIdentifier = modelIdentifierTemp.constData(); + const char* demFile = demFileTemp.constData(); + const char* timeZone = timeZoneTemp.constData(); + + NinjaErr ninjaErr = NinjaFetchArchiveWeatherData( + ninjaTools, modelIdentifier, demFile, timeZone, + startYear, startMonth, startDay, startHour, + endYear, endMonth, endDay, endHour + ); // when run without authentication keys, I get the authentication key error when running this, which is correct. And now, with authentication keys set, it is no longer hanging. If the time is too late, it acts like it finishes while failing to download a file, no error message thrown. If using a good time, it downloads successfully. This was the old qt4 gui behavior. + //NinjaErr ninjaErr = NinjaFetchArchiveWeatherData( + // ninjaTools, "fudge", demFile, timeZone, + // startYear, startMonth, startDay, startHour, + // endYear, endMonth, endDay, endHour + // ); // works with proper error message. + //NinjaErr ninjaErr = NinjaFetchArchiveWeatherData( + // ninjaTools, modelIdentifier, "fudge", timeZone, + // startYear, startMonth, startDay, startHour, + // endYear, endMonth, endDay, endHour + // ); // works with proper error message, after non-caught message "ERROR 4: fudge: No such file or directory". + //NinjaErr ninjaErr = NinjaFetchArchiveWeatherData( + // ninjaTools, modelIdentifier, demFile, "fudge", + // startYear, startMonth, startDay, startHour, + // endYear, endMonth, endDay, endHour + // ); // no longer hangs, but it runs successfully, I guess maybe using the timezone of the dem, rather than throwing an error for having an incorrect timezone. + //NinjaErr ninjaErr = NinjaFetchArchiveWeatherData( + // ninjaTools, modelIdentifier, demFile, timeZone, + // startYear, startMonth, startDay, startHour, + // startYear-1, startMonth, startDay, startHour + // ); // it acts like it finishes while failing to download a file, no error message thrown. + //NinjaErr ninjaErr = NinjaFetchArchiveWeatherData( + // ninjaTools, modelIdentifier, demFile, timeZone, + // startYear, startMonth, startDay, startHour, + // startYear, startMonth, startDay, startHour-1 + // ); // it acts like it finishes while failing to download a file, no error message thrown. + + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaFetchArchiveWeatherData: ninjaErr =" << ninjaErr; + } + + return ninjaErr; +} + +void WeatherModelInput::weatherModelDownloadFinished() +{ + // get the return value of the QtConcurrent::run() function + int result = futureWatcher->future().result(); + + if(result == NINJA_SUCCESS) + { + emit writeToConsoleSignal("Finished fetching weather model data.", Qt::darkGreen); + + if (progress) + { + progress->close(); + progress->deleteLater(); + progress = nullptr; + } + } else + { + emit writeToConsoleSignal("Failed to fetch weather model data."); + } + + // delete the futureWatcher every time, whether success or failure + if (futureWatcher) + { + futureWatcher->deleteLater(); + futureWatcher = nullptr; + } +} + +void WeatherModelInput::weatherModelComboBoxCurrentIndexChanged(int index) +{ + if(ui->weatherModelComboBox->currentText().contains("PASTCAST")) + { + ui->weatherModelSpinBox->setDisabled(true); + ui->pastcastGroupBox->setVisible(true); + + return; + } + + QStringList tooltipList; + QString weatherModel = ui->weatherModelComboBox->currentText(); + for(int i = 0; i < modelGlossary.size(); i++) + { + int pos = modelGlossary[i].indexOf('='); + if (pos <= 0) + { + continue; + } + + QString key = modelGlossary[i].left(pos); + if(weatherModel.contains(key, Qt::CaseInsensitive)) + { + tooltipList << modelGlossary[i].mid(pos + 1); + } + } + ui->weatherModelComboBox->setToolTip(tooltipList.join(", ")); + + QByteArray modelIdentifierByte = ui->weatherModelComboBox->currentText().toUtf8(); + const char* modelIdentifier = modelIdentifierByte.constData(); + int starHour, endHour; + + NinjaErr ninjaErr = NinjaGetWeatherModelHours(ninjaTools, modelIdentifier, &starHour, &endHour); + if (ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaGetWeatherModelHours: ninjaErr=" << ninjaErr; + } + + ui->weatherModelSpinBox->setMinimum(starHour); + ui->weatherModelSpinBox->setMaximum(endHour); +} + +void WeatherModelInput::updateTreeView() +{ + AppState& state = AppState::instance(); + state.isWeatherModelForecastValid = false; + emit updateState(); + + // File Tree View + fileModel = new QFileSystemModel(this); + QString demFilePath = ui->elevationInputFileLineEdit->property("fullpath").toString(); + QFileInfo demFileInfo(demFilePath); + + fileModel->setRootPath(demFileInfo.absolutePath()); + fileModel->setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + fileModel->setNameFilterDisables(false); + + QStringList filters; + for(int i = 0; i < ui->weatherModelComboBox->count(); i++) + { + filters << ui->weatherModelComboBox->itemText(i) + "-" + demFileInfo.fileName(); + } + filters << "20*.zip"; + filters << "20*T*"; + filters << "*.nc"; + fileModel->setNameFilters(filters); + + ui->weatherModelFileTreeView->setModel(fileModel); + ui->weatherModelFileTreeView->setRootIndex(fileModel->index(demFileInfo.absolutePath())); + ui->weatherModelFileTreeView->setSelectionMode(QAbstractItemView::SingleSelection); + ui->weatherModelFileTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->weatherModelFileTreeView->setAnimated(true); + ui->weatherModelFileTreeView->setUniformRowHeights(true); + ui->weatherModelFileTreeView->hideColumn(1); + ui->weatherModelFileTreeView->hideColumn(2); + ui->weatherModelFileTreeView->collapseAll(); + + QHeaderView *fileHeader = ui->weatherModelFileTreeView->header(); + fileHeader->setStretchLastSection(false); + fileHeader->setSectionResizeMode(0, QHeaderView::Stretch); + fileHeader->resizeSection(0, 400); + fileHeader->setSectionResizeMode(3, QHeaderView::ResizeToContents); + fileHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); + + // Time Tree View + timeModel = new QStandardItemModel(this); + + ui->weatherModelTimeTreeView->setModel(timeModel); + ui->weatherModelTimeTreeView->setSortingEnabled(true); + ui->weatherModelTimeTreeView->sortByColumn(0, Qt::AscendingOrder); + ui->weatherModelTimeTreeView->setSelectionMode(QAbstractItemView::MultiSelection); + ui->weatherModelTimeTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->weatherModelTimeTreeView->selectAll(); + + QHeaderView *timeHeader = ui->weatherModelTimeTreeView->header(); + timeHeader->setVisible(false); + + connect(ui->weatherModelFileTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &WeatherModelInput::weatherModelFileTreeViewItemSelectionChanged); +} + +void WeatherModelInput::weatherModelFileTreeViewItemSelectionChanged(const QItemSelection &selected) +{ + AppState& state = AppState::instance(); + + if (selected.indexes().empty()) + { + state.isWeatherModelForecastValid = false; + + return; + } + + QModelIndex index = selected.indexes().first(); + QFileInfo fileInfo = fileModel->fileInfo(index); + if(fileInfo.isDir()) + { + timeModel->clear(); + + state.isWeatherModelForecastValid = false; + emit updateState(); + + return; + } + + state.isWeatherModelForecastValid = true; + + std::string modelFilePath = fileModel->filePath(index).toStdString(); + std::string timeZone = ui->timeZoneComboBox->currentText().toStdString(); + int timeListSize = 0; + + const char **timeList = NinjaGetWeatherModelTimeList(ninjaTools, &timeListSize, modelFilePath.c_str(), timeZone.c_str()); + if(timeList == NULL) + { + qDebug() << "NinjaGetWeatherModelTimeList: Empty Time List"; + } + + timeModel->clear(); + for (int i = 0; i < timeListSize; i++) + { + QString timestep = QString::fromUtf8(timeList[i]); + timeModel->appendRow(new QStandardItem(timestep)); + } + + ui->weatherModelTimeTreeView->selectAll(); + + NinjaErr ninjaErr = NinjaFreeWeatherModelTimeList(timeList, timeListSize); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaFreeWeatherModelTimeList: ninjaErr=" << ninjaErr; + } + + emit updateState(); +} + +void WeatherModelInput::weatherModelGroupBoxToggled(bool toggled) +{ + ui->rawWeatherModelOutputCheckBox->setEnabled(toggled); + + AppState& state = AppState::instance(); + state.isWeatherModelInitializationToggled = toggled; + + if (state.isWeatherModelInitializationToggled) + { + ui->domainAverageGroupBox->setChecked(false); + ui->pointInitializationGroupBox->setChecked(false); + state.isDomainAverageInitializationToggled = ui->domainAverageGroupBox->isChecked(); + state.isPointInitializationToggled = ui->pointInitializationGroupBox->isChecked(); + } + + emit updateState(); +} + +void WeatherModelInput::weatherModelTimeSelectAllButtonClicked() +{ + ui->weatherModelTimeTreeView->selectAll(); +} + +void WeatherModelInput::weatherModelTimeSelectNoneButtonClicked() +{ + ui->weatherModelTimeTreeView->clearSelection(); +} + +void WeatherModelInput::updatePastcastDateTimeEdits() +{ + QTimeZone timeZone(ui->timeZoneComboBox->currentText().toUtf8()); + + // Update Minimum Time + QDate earliestDate(2014, 7, 30); + QDateTime utcDateTime(earliestDate, QTime(18, 0), Qt::UTC); + QDateTime localDateTime = utcDateTime.toTimeZone(timeZone); + ui->pastcastGroupBox->setTitle("Earliest Pastcast Datetime: " + localDateTime.toString("MM/dd/yyyy hh:mm")); + ui->pastcastGroupBox->updateGeometry(); + + // Update Date Time Edits + QDateTime demTime = QDateTime::currentDateTime().toTimeZone(timeZone); + // Has to be set to avoid unnecessary conversions, use timeZoneComboBox for time zone info + demTime.setTimeSpec(Qt::LocalTime); + + ui->pastcastStartDateTimeEdit->setDateTime(demTime); + ui->pastcastEndDateTimeEdit->setDateTime(demTime); +} + +void WeatherModelInput::initNinjaTools() +{ + ninjaTools = NinjaMakeTools(); + + char **papszOptions = NULL; + NinjaErr ninjaErr = NinjaSetToolsComMessageHandler(ninjaTools, &comMessageHandler, this, papszOptions); + if(ninjaErr != NINJA_SUCCESS) + { + qDebug() << "NinjaSetToolsComMessageHandler(): ninjaErr =" << ninjaErr; + } +} diff --git a/src/gui/qt6/weatherModelInput.h b/src/gui/qt6/weatherModelInput.h new file mode 100644 index 000000000..33e72bd1f --- /dev/null +++ b/src/gui/qt6/weatherModelInput.h @@ -0,0 +1,113 @@ + /****************************************************************************** + * + * $Id$ + * + * Project: WindNinja Qt GUI + * Purpose: Hands GUI related logic for the Weather Model Page + * Author: Mason Willman + * + ****************************************************************************** + * + * 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 WEATHERMODELINPUT_H +#define WEATHERMODELINPUT_H + +#include "ui_mainWindow.h" +#include "windninja.h" +#include "appState.h" +#include "QTimeZone" +#include "QFileSystemModel" +#include +#include +#include +#include +#include +#include + +class WeatherModelInput : public QObject +{ + Q_OBJECT +public: + explicit WeatherModelInput(Ui::MainWindow* ui, QObject* parent = nullptr); + +signals: + void updateState(); + void updateProgressMessageSignal(const QString &msg); + void writeToConsoleSignal(const QString &msg, QColor color=Qt::black); + +public slots: + void updateTreeView(); + void updatePastcastDateTimeEdits(); + +private slots: + void weatherModelDownloadButtonClicked(); + void weatherModelFileTreeViewItemSelectionChanged(const QItemSelection &selected); + void weatherModelTimeSelectAllButtonClicked(); + void weatherModelTimeSelectNoneButtonClicked(); + void weatherModelGroupBoxToggled(bool toggled); + void weatherModelComboBoxCurrentIndexChanged(int index); + void weatherModelDownloadFinished(); + void updateProgressMessage(const QString message); + +private: + NinjaToolsH* ninjaTools; + + Ui::MainWindow *ui; + QFileSystemModel *fileModel; + QStandardItemModel *timeModel; + QProgressDialog *progress; + QFutureWatcher *futureWatcher; + const QVector modelGlossary = { + "UCAR=University Corporation for Atmospheric Research", + "NOMADS=NOAA Operational Model Archive and Distribution System", + "GCP=Google Cloud Platform", + "NDFD=National Digital Forecast Database", + "NAM=North American Mesoscale", + "RAP=Rapid Refresh", + "HRRR=High-Resolution Rapid Refresh", + "GFS=Global Forecast System", + "HIRES=High Resolution", + "NEST=Nested", + "ARW=Advanced Research WRF", + "NMM=Non-hydrostatic Mesoscale Model", + "NBM=National Blend of Models" + }; + + int fetchForecastWeather( + NinjaToolsH* ninjaTools, + const QString& modelIdentifierStr, + const QString& demFileStr, + int hours); + + int fetchPastcastWeather( + NinjaToolsH* ninjaTools, + const QString& modelIdentifierStr, + const QString& demFileStr, + const QString& timeZoneStr, + int startYear, int startMonth, int startDay, int startHour, + int endYear, int endMonth, int endDay, int endHour); + + // this function is for trying to force the static comMessageHandler() + // to be defined BEFORE the constructor call, without reorganizing the code + void initNinjaTools(); +}; + +#endif // WEATHERMODELINPUT_H diff --git a/src/gui/setconfigdialog.cpp b/src/gui/setconfigdialog.cpp deleted file mode 100644 index 67da15b08..000000000 --- a/src/gui/setconfigdialog.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "setconfigdialog.h" -#include "ui_setconfigdialog.h" - -SetConfigDialog::SetConfigDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SetConfigDialog) -{ - ui->setupUi(this); -} - -SetConfigDialog::~SetConfigDialog() -{ - delete ui; -} - -QString SetConfigDialog::GetKey() -{ - return ui->keyEdit->text(); -} -QString SetConfigDialog::GetVal() -{ - return ui->valEdit->text(); -} - - diff --git a/src/gui/setconfigdialog.h b/src/gui/setconfigdialog.h deleted file mode 100644 index 0e12f2fc3..000000000 --- a/src/gui/setconfigdialog.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef SETCONFIGDIALOG_H -#define SETCONFIGDIALOG_H - -#include - -namespace Ui { -class SetConfigDialog; -} - -class SetConfigDialog : public QDialog -{ - Q_OBJECT - -public: - explicit SetConfigDialog(QWidget *parent = 0); - ~SetConfigDialog(); - - QString GetKey(); - QString GetVal(); - -private: - Ui::SetConfigDialog *ui; -}; - -#endif // SETCONFIGDIALOG_H diff --git a/src/gui/setconfigdialog.ui b/src/gui/setconfigdialog.ui deleted file mode 100644 index ede4c3f61..000000000 --- a/src/gui/setconfigdialog.ui +++ /dev/null @@ -1,88 +0,0 @@ - - - SetConfigDialog - - - - 0 - 0 - 400 - 143 - - - - Set Configuration Option - - - - - - - - Option - - - - - - - - - - Value - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - SetConfigDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - SetConfigDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/gui/shapeOutput.cpp b/src/gui/shapeOutput.cpp deleted file mode 100644 index 62b346981..000000000 --- a/src/gui/shapeOutput.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Shape output option selection widget - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "shapeOutput.h" - -/** - * \brief Widget for shape output. - * - * \param parent parent widget - */ -shapeOutput::shapeOutput(QWidget *parent) : QWidget(parent) -{ - shapeGroupBox = new QGroupBox(tr("Create Shape Files (*.shp)")); - shapeGroupBox->setCheckable(true); - shapeGroupBox->setChecked(false); - - shapeResGroupBox = new QGroupBox(tr("Resolution")); - - shapeResSpinBox = new QDoubleSpinBox(this); - shapeResSpinBox->setRange(1, 5000); - shapeResSpinBox->setDecimals(2); - shapeResSpinBox->setAccelerated(true); - shapeResSpinBox->setValue(200); - shapeResSpinBox->setDisabled(true); - - shapeMetersRadioButton = new QRadioButton(tr("Meters")); - shapeMetersRadioButton->setChecked(true); - shapeMetersRadioButton->setDisabled(true); - shapeFeetRadioButton = new QRadioButton(tr("Feet")); - shapeFeetRadioButton->setDisabled(true); - - useMeshResCheckBox = new QCheckBox(tr("Use Mesh Resolution")); - useMeshResCheckBox->setChecked(true); - - //connect spinbox and checkbox - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - shapeResSpinBox, SLOT(setDisabled(bool))); - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - shapeMetersRadioButton, SLOT(setDisabled(bool))); - connect(useMeshResCheckBox, SIGNAL(toggled(bool)), - shapeFeetRadioButton, SLOT(setDisabled(bool))); - - shapeResLayout = new QGridLayout; - shapeResLayout->addWidget(shapeResSpinBox, 0, 0); - shapeResLayout->addWidget(shapeMetersRadioButton, 0, 1); - shapeResLayout->addWidget(shapeFeetRadioButton, 0, 2); - shapeResLayout->addWidget(useMeshResCheckBox, 1, 0); - - shapeResGroupBox->setLayout(shapeResLayout); - - shapeLayout = new QVBoxLayout; - shapeLayout->addWidget(shapeResGroupBox); - shapeGroupBox->setLayout(shapeLayout); - - mainLayout = new QVBoxLayout; - mainLayout->addWidget(shapeGroupBox); - mainLayout->addStretch(); - - setLayout(mainLayout); -} - - diff --git a/src/gui/shapeOutput.h b/src/gui/shapeOutput.h deleted file mode 100644 index c8614cf6e..000000000 --- a/src/gui/shapeOutput.h +++ /dev/null @@ -1,63 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Shape output option selection widget - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 SHAPEOUTPUT_H_ -#define SHAPEOUTPUT_H_ - -#include -#include -#include -#include -#include - -#include "WindNinjaInputs.h" - -class shapeOutput : public QWidget -{ - Q_OBJECT - -public: - shapeOutput(QWidget *parent = 0); - - WindNinjaInputs *localInputs; - - QGroupBox *shapeGroupBox; - QGroupBox *shapeResGroupBox; - QDoubleSpinBox *shapeResSpinBox; - QRadioButton *shapeMetersRadioButton, *shapeFeetRadioButton; - QCheckBox *useMeshResCheckBox; - - QGridLayout *shapeResLayout; - QVBoxLayout *shapeLayout; - QVBoxLayout *mainLayout; - -}; - -#endif /* SHAPEOUTPUT_H_ */ - diff --git a/src/gui/solvePage.cpp b/src/gui/solvePage.cpp deleted file mode 100644 index 2c30adf02..000000000 --- a/src/gui/solvePage.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Handles solve parameters and fires the solver - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "solvePage.h" - -solvePage::solvePage(QWidget *parent) : QWidget(parent) -{ - numProcLabel = new QLabel(tr("Number of Processors")); - - numProcSpinBox = new QSpinBox; - numProcSpinBox->setRange(1, 16); - numProcSpinBox->setValue(1); - numProcSpinBox->setAccelerated(true); - - numProcLabel->setBuddy(numProcSpinBox); - - solveToolButton = new QToolButton; - solveToolButton->setText("Solve"); - solveToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - solveToolButton->setIcon(QIcon(":cog_go.png")); - solveToolButton->setDisabled(true); - - numProcessors = getNumProcessors(); - if(numProcessors < 0) - { - availProcString = "Running Serial"; - numProcessors = 1; - } - else - availProcString = "Available Processors: " + QString::number(numProcessors); - - availProcLabel = new QLabel(availProcString); - - numProcSpinBox->setMaximum(numProcessors); - numProcSpinBox->setValue(numProcessors); - - outputDirLabel = new QLabel( this ); - outputDirLabel->setText( "Output Directory" ); - outputDirLineEdit = new QLineEdit( this ); - outputDirLineEdit->setReadOnly( true ); - - outputDirToolButton = new QToolButton( this ); - outputDirToolButton->setText( "Save output in..." ); - outputDirToolButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); - outputDirToolButton->setIcon( QIcon( ":folder.png" ) ); - - openOutputPathButton = new QToolButton( this ); - openOutputPathButton->setText( "Open Output Files Path" ); - openOutputPathButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); - openOutputPathButton->setIcon( QIcon( ":folder.png" ) ); - openOutputPathButton->setDisabled( true ); - - layout = new QVBoxLayout; - layout->addWidget(availProcLabel); - - pageLayout = new QHBoxLayout; - pageLayout->addWidget(numProcLabel); - pageLayout->addWidget(numProcSpinBox); - pageLayout->addWidget(solveToolButton); - pageLayout->addStretch(); - - outputPathLayout = new QHBoxLayout; - outputPathLayout->addWidget( outputDirLineEdit ); - outputPathLayout->addWidget( outputDirToolButton ); - outputPathLayout->addWidget( openOutputPathButton ); - - connect(outputDirToolButton, SIGNAL( clicked() ), - this, SLOT( chooseOutputDir() ) ); - - layout->addLayout(pageLayout); - layout->addWidget(outputDirLabel); - layout->addLayout( outputPathLayout ); - layout->addStretch(); - setLayout(layout); -} - -void solvePage::setOutputDir(QString dir) { - outputDirLineEdit->setText( dir ); -} - -void solvePage::chooseOutputDir() { - QString dir = QFileDialog::getExistingDirectory( this, - tr("Open Output Directory"), outputDirLineEdit->text(), QFileDialog::ShowDirsOnly ); - if( dir == "" ) - { - dir = outputDirLineEdit->text(); - } - outputDirLineEdit->setText( dir ); -} - -QString solvePage::outputDirectory() { - return outputDirLineEdit->text(); -} - -int solvePage::getNumProcessors() -{ - int procs = -1; -#ifdef _OPENMP - procs = omp_get_num_procs(); -#endif - return procs; -} diff --git a/src/gui/solvePage.h b/src/gui/solvePage.h deleted file mode 100644 index 1f8d61b97..000000000 --- a/src/gui/solvePage.h +++ /dev/null @@ -1,82 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Handles solve parameters and fires the solver - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 SOLVEPAGE_H -#define SOLVEPAGE_H - -#include -#include -#include -#include -#include -#include - -#include - -#ifdef _OPENMP -#include -#endif - - -class solvePage : public QWidget -{ - Q_OBJECT - - public: - solvePage(QWidget *parent = 0); - - int numProcessors; - int getNumProcessors(); - - QLabel *numProcLabel; - QString availProcString; - QLabel *availProcLabel; - QSpinBox *numProcSpinBox; - - QLabel *outputDirLabel; - QLineEdit *outputDirLineEdit; - QToolButton *outputDirToolButton; - QToolButton *openOutputPathButton; - - QToolButton *solveToolButton; - - QHBoxLayout *pageLayout; - QHBoxLayout *outputPathLayout; - QVBoxLayout *layout; - - QString outputDirectory(); - -public slots: - void setOutputDir(QString path); - -private slots: - void chooseOutputDir(); -}; - -#endif /* SOLVEPAGE_H */ diff --git a/src/gui/solveThread.cpp b/src/gui/solveThread.cpp deleted file mode 100644 index a4df00880..000000000 --- a/src/gui/solveThread.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: QThread that fires n threads for the solver - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "solveThread.h" - -/** - * QThread for running all of the simulations - * - * @param nProcs number of threads - * @param a army to run on - * - * @return true on success - */ -bool solveThread::run(int nProcs, ninjaArmy a) -{ - return a.startRuns(nProcs); -} - -/** - * Distribute ninja runs over the allotted number of threads - * - * @param numRuns number of ninja runs - * @param numProcessors number of processors to use - * @param windsim pointer to an array of ninjas - * - * @return false on failure, true otherwise - */ -bool solveThread::startRunsQt(int numRuns, int numProcessors, ninja *windsim) -{ - bool ninjaSuccess = true; - QString exceptString; - - bool *retval = new bool[numRuns]; - for(int i = 0;i < numRuns;i++) - retval[i] = true; - - if(numRuns < 1 || numProcessors < 1) - return false; - -#ifdef _OPENMP - omp_set_nested(false); - //omp_set_dynamic(true); -#endif - - if(numRuns == 1) { - //set number of threads for the run - windsim[0].set_numberCPUs( numProcessors ); - //start the run - try { - retval[0] = windsim[0].simulate_wind(); - } - catch (std::bad_alloc& e) { - exceptString = "Exception bad_alloc caught: " + QString( e.what() ); - exceptString += ". WindNinja appears to have run out of memory."; - ninjaSuccess = false; - } - catch (cancelledByUser& e) { - exceptString = "Exception caught: " + QString( e.what() ); - ninjaSuccess = false; - } - catch (std::exception& e) { - exceptString = "Exception caught: " + QString( e.what() ); - ninjaSuccess = false; - } - catch (...) { - exceptString = "Exception caught: Cannot determine exception type."; - ninjaSuccess = false; - } - //writeToConsole( exceptString, Qt::red ); - } - else { -#ifdef _OPENMP - omp_set_num_threads(numProcessors); -#endif - -#pragma omp parallel for schedule(static, 1) //spread runs on single threads - for(int j = 0; j < numRuns; j++) { - //start the run - try { - retval[j] = windsim[j].simulate_wind(); //runs are done on 1 thread each since omp_set_nested(false) - } - catch (std::bad_alloc& e) { - exceptString = "Exception bad_alloc caught: " + QString( e.what() ); - exceptString += ". WindNinja appears to have run out of memory."; - ninjaSuccess = false; - } - catch (cancelledByUser& e) { - exceptString = "Exception caught: " + QString( e.what() ); - ninjaSuccess = false; - } - catch (std::exception& e) { - exceptString = "Exception caught: " + QString( e.what() ); - ninjaSuccess = false; - } - catch (...) { - exceptString = "Exception caught: Cannot determine exception type."; - ninjaSuccess = false; - } - //writeToConsole( exceptString, Qt::red ); - } - } - for(int i = 0;i < numRuns;i++) { - if (retval[i] == false) - ninjaSuccess = false; - } - return ninjaSuccess; -} diff --git a/src/gui/solveThread.h b/src/gui/solveThread.h deleted file mode 100644 index 1c3e862d7..000000000 --- a/src/gui/solveThread.h +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: QThread that fires n threads for the solver - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 SOLVETHREAD_H -#define SOLVETHREAD_H -#include - -#ifndef Q_MOC_RUN -#include "ninja.h" -#endif - -#include "ninjaArmy.h" -//make thread classes - -class solveThread : public QThread -{ - Q_OBJECT - public: - bool startRunsQt(int numRuns, int numProcessors, ninja *windsim); - bool run(int nProcs, ninjaArmy a); -}; - -#endif /* SOLVETHREAD_H */ - diff --git a/src/gui/splash.cpp b/src/gui/splash.cpp deleted file mode 100644 index 1cae2e421..000000000 --- a/src/gui/splash.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Splash screen that shows a pixmap and n messages for m seconds - * each. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "splash.h" - -/** - * \brief Create the splash screen - * - * Display an image with text in the upper left corner. The time is the total - * each message is displayed. Total time is time * list.size() - * - * \param pixmap Image to display - * \param list of strings to display - * \param time to display each message in milliseconds - */ -splashScreen::splashScreen(const QPixmap &pixmap, QStringList list, int time) -{ - stringList = list; - messageTime = time; - fade_interval = (float)time / FRAMES_PER_MESSAGE; - nMessages = stringList.size(); - nFrames = time * FRAMES_PER_MESSAGE; - i = 0; - j = 0; - messageTimer = new QTimer; - alignment = Qt::AlignLeft | Qt::AlignBottom; - setFocus(Qt::OtherFocusReason); - orig_map = pixmap; - bDone = 0; - - int padding = 21; - for(int p = 0; p < padding; p++) - pad += " "; -} - -/** - * \brief Show the splash screen - * - * Show the image and start the timer, increment through the messages - */ -void splashScreen::display() -{ - show(); - messageTimer->start(fade_interval); - connect(messageTimer, SIGNAL(timeout()), this, SLOT(update())); -} - -static QPixmap &setAlpha( QPixmap &px, int val ) -{ - QPixmap alpha = px; - QPainter p(&alpha); - p.fillRect(alpha.rect(), QColor(val, val, val)); - p.end(); - px.setAlphaChannel(alpha); - return px; -} - -/** - * \brief Change the label until there are no more labels. - * - * Close the splash after the last label - */ -void splashScreen::update() -{ - if(bDone) - return; - map = orig_map; - /* temp vars */ - float f; - int k; - int alpha = 255; - if(i <= 1) - { - alpha = (float)255 / FRAMES_PER_MESSAGE * j; - } - if(i > nMessages - 1) - { - k = nMessages * FRAMES_PER_MESSAGE; - alpha = 255 - (int)((1 - ((float)k - j) / FRAMES_PER_MESSAGE) * 255); - } - if(j % FRAMES_PER_MESSAGE == 0) - { - if(i < nMessages) - { - showMessage(pad + stringList[i], alignment, Qt::gray); - i++; - } - else - { - messageTimer->stop(); - emit done(); - finish(this); - } - } - map = setAlpha(map, alpha); - setPixmap(map); - j++; -} - -/** - * \brief Close the image on left button mouse event - * - * \param event mouse event to process - */ -void splashScreen::mousePressEvent(QMouseEvent *event) -{ - if(event->buttons() & Qt::LeftButton) - { - bDone = 1; - emit done(); - finish(this); - } - else - return; -} - diff --git a/src/gui/stabilityInput.cpp b/src/gui/stabilityInput.cpp deleted file mode 100644 index 61d4ff5c8..000000000 --- a/src/gui/stabilityInput.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/****************************************************************************** - * - * $Id: stabilityInput.cpp 1304 2012-01-20 21:07:12Z kyle.shannon $ - * - * Project: WindNinja Qt GUI - * Purpose: OpenGL implementation for viewing DEM inputs - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "stabilityInput.h" - -/** - * Construct and layout the stabilityInput widget. This is only a checkable - * option now. - * - * @param parent parent widget - */ -stabilityInput::stabilityInput(QWidget *parent) : QWidget(parent) -{ - stabilityGroupBox = new QGroupBox(tr("Use Stability")); - stabilityGroupBox->setCheckable(true); - stabilityGroupBox->setChecked(false); - stabilityLayout = new QVBoxLayout; - - ninjafoamConflictLabel = new QLabel(tr("The non-neutral stability option is not currently available for the momentum solver.\n" - ), this); - ninjafoamConflictLabel->setHidden(true); - - stabilityGroupBox->setLayout(stabilityLayout); - - layout = new QVBoxLayout; - layout->addWidget(stabilityGroupBox); - layout->addWidget(ninjafoamConflictLabel); - layout->addStretch(); - setLayout(layout); -} diff --git a/src/gui/stabilityInput.h b/src/gui/stabilityInput.h deleted file mode 100644 index 0375cc6d8..000000000 --- a/src/gui/stabilityInput.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef STABILITYINPUT_H -#define STABILITYINPUT_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "gdal_priv.h" -#include "ogr_srs_api.h" - -#ifndef Q_MOC_RUN -#include "boost/date_time/local_time/local_time.hpp" -#include "boost/date_time/posix_time/posix_time_types.hpp" -#endif - -#include "latLonWidget.h" -#include "timeZoneWidget.h" - -#include "qdebug.h" - -class stabilityInput : public QWidget -{ - Q_OBJECT - - public: - - stabilityInput(QWidget *parent = 0); - QGroupBox *stabilityGroupBox; - QVBoxLayout *stabilityLayout; - QVBoxLayout *layout; - QLabel *ninjafoamConflictLabel; - -}; - -#endif /* STABILITYINPUT_H */ diff --git a/src/gui/stationFetchWidget.cpp b/src/gui/stationFetchWidget.cpp deleted file mode 100644 index d7933312c..000000000 --- a/src/gui/stationFetchWidget.cpp +++ /dev/null @@ -1,528 +0,0 @@ -/****************************************************************************** - * - * $Id: stationFetchWidget.cpp 1757 2012-08-07 18:40:40Z kyle.shannon $ - * - * Project: WindNinja - * Purpose: stationFetchWidget - * Author: tfinney@fs.fed.us - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -/** @file stationFetchWidget.cpp - * - * Fetch stations from the Mesonet API - * - */ - -#include "stationFetchWidget.h" -//#include - -stationFetchWidget::stationFetchWidget(QWidget *parent) - : QWidget(parent) -{ - setupUi(this); - connectInputs(); - fixTime(); - this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); - this->show(); - - currentBox->setVisible(false); - fetchMetaButton->setVisible(false); //Hide the metadata button from the gui - - 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 - stationFetchProgress->setAutoClose(false); -} - -/** - * @brief Deletes all allocated memory on destruction of class object - * - */ -stationFetchWidget::~stationFetchWidget() -{ - delete stationFetchProgress; -} -/** - * @brief stationFetchWidget::fixTime - * Sets the time correctly in the downloader widget - * Default is start time to be yesterday and - * end time to be now - */ -void stationFetchWidget::fixTime() -{ - startEdit->setDateTime( QDateTime::currentDateTime().addDays(-1) ); - endEdit->setDateTime(QDateTime::currentDateTime()); - - startEdit->setMaximumDateTime(QDateTime::currentDateTime().addSecs(-3600)); - endEdit->setMaximumDateTime(QDateTime::currentDateTime()); - - startEdit->setDisplayFormat( "MM/dd/yyyy HH:mm" ); - endEdit->setDisplayFormat( "MM/dd/yyyy HH:mm" ); -} - -/** - * @brief Connect all SLOTS and SIGNALS in the Qt GUI - * - */ -void stationFetchWidget::connectInputs() -{ - connect(fetchMetaButton, SIGNAL(clicked()),this, SLOT(getMetadata())); //Gets the metadata, indirectly used in mainwindow via config option - connect(fetchDataButton,SIGNAL(clicked()),this,SLOT(executeFetchStation())); //Fetches the data and - connect(endEdit,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(watchStopTime())); //Watches the time to make sure we don't go over - connect(startEdit,SIGNAL(dateTimeChanged(QDateTime)),this,SLOT(watchStartTime())); //Watches the time to make sure we don't go under - connect(closeButton,SIGNAL(clicked()),this,SLOT(close())); //closes stationFetchWidget -} - -void stationFetchWidget::updatetz(QString tz) //Updates the Time Zone -{ - tzString = tz; -} -void stationFetchWidget::setInputFile(QString file) //Gets the DEM -{ - demFileName = file; -} - -/** - * @brief stationFetchWidget::watchStartTime - * Watches the start time that the user puts in, if - * its bigger than the end time, correct the end time by adding 1 hour - * to the start time - */ -void stationFetchWidget::watchStartTime() -{ - if (endEdit->dateTime()dateTime()) - { - writeToConsole("Start Time is greater than End Time!, reverting..."); - CPLDebug("STATION_FETCH","START TIME > END TIME, FIXING END TIME"); - endEdit->setDateTime(startEdit->dateTime().addSecs(3600)); - } -} -/** - * @brief stationFetchWidget::watchStopTime - * watch the end time the user puts in, if it is smaller - * than the start time - * correct the start time by subtracting 1 hour from the end time - */ -void stationFetchWidget::watchStopTime() -{ - if (endEdit->dateTime()dateTime()) - { - writeToConsole("Start Time is greater than End Time!, reverting..."); - CPLDebug("STATION_FETCH","START TIME > END TIME, FIXING START TIME"); - startEdit->setDateTime(endEdit->dateTime().addSecs(-3600)); - } -} - -/** - * @brief stationFetchWidget::updateFetchProgress - * Updates the Progress Bar and tells the GUI - * when station fetch is done downloading, or if the user - * cancels the request - * - * Kills the request once it is downloaded - * - */ -void stationFetchWidget::updateFetchProgress() -{ - if (stationFetchProgress->wasCanceled()) //If the user hits the cancel button - { - stationFetchProgress->setLabelText("Canceling!"); - stationFetchProgress->setCancelButton(0); - stationFutureWatcher.waitForFinished(); - stationFetchProgress->cancel(); - setCursor(Qt::ArrowCursor); - } - else - { - stationFutureWatcher.waitForFinished(); - int result = stationFutureWatcher.result(); //Get the result, 1 good, -1 bad - - if (result==-1) //Means that we failed to get data, the error_msg should tell the user - { //What happened - stationFetchProgress->setLabelText(QString(pointInitialization::error_msg.c_str())); - stationFetchProgress->setRange(0,1); - stationFetchProgress->setValue(0); - stationFetchProgress->setCancelButtonText("Close"); - setCursor(Qt::ArrowCursor); - } - else if (result==-2) //Special type of error that we catch in the GUI - { - stationFetchProgress->setLabelText("ERROR: Selected Time Range is greater than 1 year! Input custom API KEY to remove limits"); - stationFetchProgress->setRange(0,1); - stationFetchProgress->setValue(0); - stationFetchProgress->setCancelButtonText("Close"); - setCursor(Qt::ArrowCursor); - } - else //IT WORKED! - { - stationFetchProgress->setRange(0,100); - stationFetchProgress->setValue(1); - stationFetchProgress->setValue(100); - stationFetchProgress->setLabelText("Data Downloaded Sucessfully!"); - stationFetchProgress->setCancelButtonText("Close"); - setCursor(Qt::ArrowCursor); //set the cursor back to normal - } - } -} - -/** - * @brief stationFetchWidget::executeFetchStation - * Executes fetchStation using Qt stuff - * based on widgetDownloadDEM - * - * so that we can see a progress bar - * and prevent program hanging - * - */ -void stationFetchWidget::executeFetchStation() -{ - stationFetchProgress->setLabelText("Downloading Station Data!"); - stationFetchProgress->setRange(0,0); //make it bounce back and forth - stationFetchProgress->setCancelButtonText("Cancel"); - stationFetchProgress->reset(); //Set the progress bar back to its basic state - - connect(&stationFutureWatcher,SIGNAL(finished()),this,SLOT(updateFetchProgress())); - connect(stationFetchProgress,SIGNAL(canceled()),this,SLOT(updateFetchProgress())); - - /* Note on Concurrent Processing: - * You can't update the GUI from a spawned process on another thread - * If you do: - * It will segfault or throw X11 errors at you and freeze - * This was encountered when updating the cursor was set inside fetchStation. - * Always update the UI from outside the spawned thread... - */ - - stationFutureWatcher.setFuture(QtConcurrent::run(this,&stationFetchWidget::fetchStation)); //Run the - //actual fetching - setCursor(Qt::WaitCursor);//Make the cursor spinny - stationFetchProgress->exec(); //Execute the progress bar to do its thing -// stationFutureWatcher.cancel(); //commented for now, probably can be deleted... - -} -/** - * @brief stationFetchWidget::removeWhiteSpace - * //Cleans up spaces in text - * if the user types in a station name and then puts a space - * strip it out - * @param str - * @return - */ -std::string stationFetchWidget::removeWhiteSpace(std::string str) -{ - std::string tofind=" "; - std::string toreplace=""; - size_t position = 0; - for ( position = str.find(tofind); position != std::string::npos; position = str.find(tofind,position) ) - { - str.replace(position ,1, toreplace); - } - return(str); -} -/** - * @brief stationFetchWidget::demButcher - * get rid of some crap in the dem so that the file name looks nice - * - * @return - */ -std::string stationFetchWidget::demButcher()//Cleans up the DEM for use in the downloader -{ - std::string demPath = std::string(CPLGetDirname(demFileName.toStdString().c_str())); -// std::string demRaw = demFileName.toStdString(); -// size_t lastDot=demRaw.find_last_of("/"); -// if (lastDot==std::string::npos) -// { -// return demRaw; -// } -// std::string demBetter=demRaw.substr(0,lastDot)+"/"; - return demPath; -} -/** - * @brief stationFetchWidget::fetchStation - * Fetches data from the Mesowest API based on GUI request - */ -int stationFetchWidget::fetchStation() -{ - writeToConsole("Downloading Station Data..."); - CPLDebug("STATION_FETCH","Fetch Station GUI Function"); - CPLDebug("STATION_FETCH","---------------------------------------"); - CPLDebug("STATION_FETCH","DEM FILE NAME: %s",demFileName.toStdString().c_str()); - CPLDebug("STATION_FETCH","TIME ZONE: %s",tzString.toStdString().c_str()); - CPLDebug("STATION_FETCH","geoLoc: %i",geoLoc->currentIndex()); - CPLDebug("STATION_FETCH","timeLoc: %i",timeLoc->currentIndex()); - CPLDebug("STATION_FETCH","---------------------------------------"); - std::string stid; - double buffer; - std::string bufferUnits; - bool fetchNow; - std::string blank="blank"; - std::string demUse=demButcher(); - std::string stationPathName; - CPLDebug("STATION_FETCH","USING DEM: %s",demUse.c_str()); - - int terrainPart=geoLoc->currentIndex(); - int timePart=timeLoc->currentIndex(); - - //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<currentIndex()==0) -// { -// cout<<"DEM VALUES"<text().toDouble()<currentText().toStdString()<currentIndex()==1) -// { -// cout<<"STID VALUES"<text().toStdString()<currentIndex()==0) -// { -// cout<<"Current Values"<isChecked()<currentIndex()==1) -// { -// cout<<"Time Series Values"<text().toStdString()<text().toStdString()< eTimeList; - boost::posix_time::ptime noTime; - eTimeList.push_back(noTime); // Create an Empty Time list for options that don't need it. - bool result; - - - //pointInitialization::SetRawStationFilename(demUse); - //This is a little different than in the CLI, because there is on option for a custom output path - //Instead, the "raw File", demFileName is the dem file, like it would be in the LCI - //and then demUse, which the is just the path to the dem acts as the output path - //So the directory storing the weather csvs is always the same level as the DEM - //The Path name has a time zone in it - // This is because the station file names have time in them and to specify to the user - // where they downloaded the times at - // This is only necessary for timeseries however, because the other options, such as 1 step/current data - //are time naive and require the user to specify a time for what is current. - - // This means DEM and Current Data 1 step - if (terrainPart==0 && timePart==0) - { - CPLDebug("STATION_FETCH","Fetch Params: DEM and Current Data"); - buffer=bufferSpin->text().toDouble(); - bufferUnits=buffUnits->currentText().toStdString(); - fetchNow=true; - - //Set the Station Buffer - pointInitialization::setStationBuffer(buffer,bufferUnits); - //Generates the directory to store the file names, because current data is on, don't specify time zone - stationPathName=pointInitialization::generatePointDirectory(demFileName.toStdString(),demUse,true); - pointInitialization::SetRawStationFilename(stationPathName); - result = pointInitialization::fetchStationFromBbox(demFileName.toStdString(),eTimeList,tzString.toStdString(),fetchNow); - - CPLDebug("STATION_FETCH","Return: %i",result); - } - //DEM and Time series - if (terrainPart==0 && timePart==1) - { - CPLDebug("STATION_FETCH","Fetch Params: DEM and Time series"); - buffer=bufferSpin->text().toDouble(); - bufferUnits=buffUnits->currentText().toStdString(); - fetchNow=false; - - 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 - //Just need to generate a timelist for fetching purposes - - std::string StartTime=startEdit->text().toStdString(); - std::string EndTime=endEdit->text().toStdString(); - - istringstream(StartTime.substr(0,2))>>sMo; - istringstream(StartTime.substr(3,2))>>sD; - istringstream(StartTime.substr(6,4))>>sY; - istringstream(StartTime.substr(11,2))>>sH; - istringstream(StartTime.substr(14,2))>>sMi; - - istringstream(EndTime.substr(0,2))>>eMo; - istringstream(EndTime.substr(3,2))>>eD; - istringstream(EndTime.substr(6,4))>>eY; - istringstream(EndTime.substr(11,2))>>eH; - istringstream(EndTime.substr(14,2))>>eMi; - - std::vector timeList; - timeList=pointInitialization::getTimeList(sY,sMo,sD,sH,sMi,eY,eMo,eD,eH,eMi, - numSteps,tzString.toStdString()); - int duration_check = pointInitialization::checkFetchTimeDuration(timeList); //Check the timelist duration - if(duration_check==-2) //Means that they select too much and we have to quit - { - return duration_check; - } - - //Set station Buffer - pointInitialization::setStationBuffer(buffer,bufferUnits); - - //Generate Station directory, because timeseries is on, specify what time zone the stations will be - //downloaded in, based on DEM time zone settings, or user specified. - stationPathName=pointInitialization::generatePointDirectory(demFileName.toStdString(),demUse,false); - pointInitialization::SetRawStationFilename(stationPathName); - result = pointInitialization::fetchStationFromBbox(demFileName.toStdString(),timeList, - tzString.toStdString(),false); - CPLDebug("STATION_FETCH","Return: %i",result); - } - if (terrainPart==1 && timePart==0) //STation ID and 1 step - { - CPLDebug("STATION_FETCH","STID and Current Data"); - stid=removeWhiteSpace(idLine->text().toStdString()); - fetchNow=true; - //Fetch now is on, don't specify time zone in station path - stationPathName=pointInitialization::generatePointDirectory(demFileName.toStdString(),demUse,true); - pointInitialization::SetRawStationFilename(stationPathName); - - result = pointInitialization::fetchStationByName(stid,eTimeList,tzString.toStdString(),fetchNow); - - CPLDebug("STATION_FETCH","Return: %i",result); - - } - if (terrainPart==1 && timePart==1) //STATION ID and timeseries - { - CPLDebug("STATION_FETCH","STID and Timeseries"); - stid=removeWhiteSpace(idLine->text().toStdString()); - fetchNow=false; - 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(); - - istringstream(StartTime.substr(0,2))>>sMo; - istringstream(StartTime.substr(3,2))>>sD; - istringstream(StartTime.substr(6,4))>>sY; - istringstream(StartTime.substr(11,2))>>sH; - istringstream(StartTime.substr(14,2))>>sMi; - - istringstream(EndTime.substr(0,2))>>eMo; - istringstream(EndTime.substr(3,2))>>eD; - istringstream(EndTime.substr(6,4))>>eY; - istringstream(EndTime.substr(11,2))>>eH; - istringstream(EndTime.substr(14,2))>>eMi; - - std::vector timeList; - timeList=pointInitialization::getTimeList(sY,sMo,sD,sH,sMi,eY,eMo,eD,eH,eMi, - numSteps,tzString.toStdString()); - int duration_check = pointInitialization::checkFetchTimeDuration(timeList); //Check the timelist duration - if(duration_check==-2) //Means that they select too much and we have to quit - { - return duration_check; - } - - stationPathName=pointInitialization::generatePointDirectory(demFileName.toStdString(),demUse,false); - pointInitialization::SetRawStationFilename(stationPathName); - result = pointInitialization::fetchStationByName(stid,timeList,tzString.toStdString(),fetchNow); - - //timeseries, so specify time zone in path name. - - CPLDebug("STATION_FETCH","Return: %i",result); - } - if(result==false) //If there are no stations, tell the user - { - pointInitialization::removeBadDirectory(stationPathName); - writeToConsole("Could not read station File: Possibly no stations exist for request"); - return -1; - } - else - { - if(fetchNow==false) - { - pointInitialization::start_and_stop_times.clear(); //Need to clear these times to allow multiple downloads - } - return 1; - } -} - -/** - * @brief stationFetchWidget::getMetadata - * - * GUI wrapper for metadata fetcher, - * we don't currently use this - * and instead have a config option - * that fetches metadata at runtime - * - * leave this in incase we decide to add in a - * metadata button later - */ -void stationFetchWidget::getMetadata() -{ - QString fileName; - CPLDebug("STATION_FETCH","METADATA DOWNLOADER FOR STATIONS IN DEM: %s",demFileName.toStdString().c_str()); - - //the third param: QFileInfo sets the metadata - //save widget to the current directory and then - //appends .csv to the name to tell the user to save the metadata with - //that extension. This may have some bugs, needs testing 5/24/2018 - fileName = QFileDialog::getSaveFileName(this, - tr("Save Domain Metadata File"), - QFileInfo(demFileName).absoluteDir().absolutePath()+"/.csv", - tr("Comma Separated files (*.csv")); - - if (QFileInfo(fileName).suffix().compare("csv", Qt::CaseInsensitive)) - { - fileName += ".csv"; - if (QFileInfo(fileName).exists()) - { - int r = QMessageBox::warning(this, "WindNinja", - "The file " + fileName + - " exists, do you wish to" \ - " overwrite it?", - QMessageBox::Yes | - QMessageBox::No | - QMessageBox::Cancel); - if (r == QMessageBox::No || r == QMessageBox::Cancel) - return; - } - } - else - pointInitialization::fetchMetaData(fileName.toStdString(), demFileName.toStdString(), true); -} - -void stationFetchWidget::closeDEM() -{ - this->close(); -} - -void stationFetchWidget::closeEvent(QCloseEvent *event) -{ - event->ignore(); - exitWidget(); - event->accept(); -} - diff --git a/src/gui/stationFetchWidget.h b/src/gui/stationFetchWidget.h deleted file mode 100644 index b8dbb815b..000000000 --- a/src/gui/stationFetchWidget.h +++ /dev/null @@ -1,101 +0,0 @@ -/****************************************************************************** - * - * $Id: stationFetchWdiget.h 1757 2012-08-07 18:40:40Z kyle.shannon $ - * - * Project: WindNinja - * Purpose: stationFetchWidget - * Author: tfinney@fs.fed.us - * - ****************************************************************************** - * - * 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 STATIONFETCHWIDGET_H_ -#define STATIONFETCHWIDGET_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "fetch_factory.h" -#include "gdal_util.h" -#include "ninja_conv.h" -#include -#include -#include "ui_stationFetchWidget.h" -#include "GoogleMapsInterface.h" - -#include "pointInitialization.h" - -#ifndef PI -#define PI 3.14159 -#endif - -class stationFetchWidget : public QWidget, private Ui::stationFetchWidget -{ - Q_OBJECT - -public: - QString demFileName; - QDir demFileDir; - stationFetchWidget(QWidget *parent = 0); - ~stationFetchWidget(); - QDir settingsDir; - void connectInputs(); - QString tzString; - void updatetz(QString tz); - void fixTime(); - std::string removeWhiteSpace(std::string str); - -protected: - void closeEvent(QCloseEvent *event); - - private slots: - void closeDEM(); - void getMetadata(); - void setInputFile( QString file ); - int fetchStation(); - std::string demButcher(); - void watchStartTime(); - void watchStopTime(); - - //Progress Bar Slots - void updateFetchProgress(); - void executeFetchStation(); //Wrapper for fetch station, necessary for progress bar - - signals: - void writeToConsole(QString message); - void exitWidget(); - -private: - //Progress Bar Stuff - QProgressDialog *stationFetchProgress; - QFutureWatcher stationFutureWatcher; - -friend class pointInput; -}; - -#endif /* STATIONFETCHWIDGET_H_ */ - diff --git a/src/gui/stationFetchWidget.ui b/src/gui/stationFetchWidget.ui deleted file mode 100644 index 9b00eddfe..000000000 --- a/src/gui/stationFetchWidget.ui +++ /dev/null @@ -1,381 +0,0 @@ - - - stationFetchWidget - - - - 0 - 0 - 500 - 215 - - - - - 0 - 0 - - - - - 500 - 215 - - - - - 1500 - 2504 - - - - Station Downloader - - - - :/wn-icon.png:/wn-icon.png - - - - - - - - - - - - <html><head/><body><p>Download From DEM: Download all active weather stations with the bounds of the selected surface input.</p><p>Download By Station ID: Manually enter weather station IDs separated by commas. e.g. KMSO,PNTM8</p></body></html> - - - - Download From DEM - - - - - Download By Station ID - - - - - - - - - - - 10 - 0 - 141 - 20 - - - - Buffer Around DEM - - - - - - 10 - 20 - 151 - 26 - - - - <html><head/><body><p>Add a buffer to download stations outside the DEM.</p></body></html> - - - 100 - - - - - - 170 - 20 - 51 - 25 - - - - - mi - - - - - km - - - - - - - - - 0 - 30 - 221 - 25 - - - - - - - KMSO,PNTM8 - - - - - - 0 - 10 - 161 - 17 - - - - Input Station IDs - - - idLabel - idLine - - - - - - - - - - - - - - - <html><head/><body><p>Download Most Recent Data: Download one time step of the latest data available in the mesowest API.</p><p>Download Between Two Dates: Download all weather station data within a specified start and stop time.</p></body></html> - - - - Download Most Recent Data - - - - - Download Between Two Dates - - - - - - - - - - true - - - - 10 - 30 - 221 - 23 - - - - false - - - Fetch Current Weather Data - - - false - - - false - - - - - - 15 - 30 - 211 - 61 - - - - <html><head/><body><p>Download the most recent weather data for a single time step simulation.</p></body></html> - - - true - - - - - - - - 30 - 30 - 194 - 26 - - - - - - - true - - - - - - 30 - 90 - 194 - 26 - - - - true - - - - - - 30 - 10 - 101 - 17 - - - - Start Time - - - - - - 30 - 70 - 67 - 17 - - - - End Time - - - - - - - - - - - - - - - - - <html><head/><body><p>Fetches weather data based on above specified parameters.</p></body></html> - - - Download Data - - - - :/server_go.png:/server_go.png - - - Qt::ToolButtonTextBesideIcon - - - - - - - <html><head/><body><p>Fetches general information about nearby weather stations.</p><p>Such as: Latitude, Longitude, Elevation and Status,</p></body></html> - - - Download Metadata - - - false - - - Qt::ToolButtonTextOnly - - - - - - - <html><head/><body><p>Close Station Downloader</p></body></html> - - - Close - - - - :/cross.png:/cross.png - - - Qt::ToolButtonTextBesideIcon - - - - - - - - - - - - - geoLoc - currentIndexChanged(int) - geoPage - setCurrentIndex(int) - - - 128 - 24 - - - 128 - 252 - - - - - timeLoc - currentIndexChanged(int) - timePage - setCurrentIndex(int) - - - 371 - 24 - - - 371 - 252 - - - - - - slot1() - - diff --git a/src/gui/surfaceInput.cpp b/src/gui/surfaceInput.cpp deleted file mode 100644 index 0101107e2..000000000 --- a/src/gui/surfaceInput.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Handles surface inputs for the domain - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "surfaceInput.h" - -/** - * \brief Construct a widget for suface input data - * - * \param parent parent widget - */ -surfaceInput::surfaceInput(QWidget *parent) : QWidget(parent) -{ -#ifdef NINJAFOAM - //make ninjafoam case input controls - foamCaseGroupBox = new QGroupBox(tr("Use Existing Case")); - - foamCaseLineEdit = new QLineEdit; - foamCaseLineEdit->setReadOnly(true); - - foamCaseOpenToolButton = new QToolButton; - foamCaseOpenToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - foamCaseOpenToolButton->setText(tr("Open Case")); - foamCaseOpenToolButton->setIcon(QIcon(":folder_page.png")); -#endif //NINJAFOAM - - //make INPUT input controls - inputFileGroupBox = new QGroupBox(tr("Elevation Input File")); - - inputFileLineEdit = new QLineEdit; - inputFileLineEdit->setReadOnly(true); - - inputFileOpenToolButton = new QToolButton; - inputFileOpenToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - inputFileOpenToolButton->setText(tr("Open File")); - inputFileOpenToolButton->setIcon(QIcon(":folder_page.png")); - - downloadDEMButton = new QToolButton; - downloadDEMButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - downloadDEMButton->setText(tr("Download File")); - downloadDEMButton->setIcon(QIcon(":server_go.png")); - -#ifdef NINJAFOAM - foamCaseLayout = new QHBoxLayout; - foamCaseLayout->addWidget(foamCaseLineEdit); - foamCaseLayout->addWidget(foamCaseOpenToolButton); -#endif - - inputLayout = new QHBoxLayout; - inputLayout->addWidget(inputFileLineEdit); - inputLayout->addWidget(inputFileOpenToolButton); - inputLayout->addWidget(downloadDEMButton); - - //roughness combo box in a group - roughnessGroupBox = new QGroupBox(tr("Vegetation")); - - roughnessComboBox = new QComboBox; - roughnessComboBox->addItem("Grass"); - roughnessComboBox->addItem("Brush"); - roughnessComboBox->addItem("Trees"); - roughnessComboBox->setDisabled(true); - - roughnessLabel = new QLabel; - roughnessLabel->setText("Vegetation Data Set Using LCP File"); - roughnessLabel->setEnabled(true); - roughnessLabel->setVisible(false); - - roughnessLayout = new QHBoxLayout; - roughnessLayout->addWidget(roughnessComboBox); - roughnessLayout->addWidget(roughnessLabel); - roughnessLayout->addStretch(); - - //mesh resolution controls - meshResGroupBox = new QGroupBox(tr("Mesh Resolution")); - - meshResComboBox = new QComboBox; - meshResComboBox->addItem(tr("Coarse")); - meshResComboBox->addItem(tr("Medium")); - meshResComboBox->addItem(tr("Fine")); - meshResComboBox->addItem(tr("Custom")); - meshResComboBox->insertSeparator(3); - meshResComboBox->setCurrentIndex(2); - meshResComboBox->setEnabled(false); - - meshResDoubleSpinBox = new QDoubleSpinBox; - meshResDoubleSpinBox->setRange(1, 50000); - meshResDoubleSpinBox->setSingleStep(100.0); - meshResDoubleSpinBox->setDecimals(2); - meshResDoubleSpinBox->setAccelerated(true); - meshResDoubleSpinBox->setValue(200); - meshResDoubleSpinBox->setEnabled(false); - - meshMetersRadioButton = new QRadioButton(tr("Meters")); - meshMetersRadioButton->setChecked(true); - - meshFeetRadioButton = new QRadioButton(tr("Feet")); - - meshResLayout = new QHBoxLayout; - meshResLayout->addWidget(meshResComboBox); - meshResLayout->addWidget(meshResDoubleSpinBox); - meshResLayout->addWidget(meshMetersRadioButton); - meshResLayout->addWidget(meshFeetRadioButton); - meshResLayout->addStretch(); - - timeZoneGroupBox = new QGroupBox("Time Zone", this); - timeZone = new timeZoneWidget(this); - timeZoneLayout = new QHBoxLayout; - timeZoneLayout->addWidget(timeZone); - timeZoneLayout->addStretch(); - - //set layouts to boxes -#ifdef NINJAFOAM - foamCaseGroupBox->setLayout(foamCaseLayout); -#endif - inputFileGroupBox->setLayout(inputLayout); - roughnessGroupBox->setLayout(roughnessLayout); - meshResGroupBox->setLayout(meshResLayout); - timeZoneGroupBox->setLayout(timeZoneLayout); - - //main layout - mainLayout = new QVBoxLayout; -#ifdef NINJAFOAM - mainLayout->addWidget(foamCaseGroupBox); - foamCaseGroupBox->setHidden( true ); -#endif - mainLayout->addWidget(inputFileGroupBox); - mainLayout->addWidget(roughnessGroupBox); - mainLayout->addWidget(meshResGroupBox); - mainLayout->addWidget(timeZoneGroupBox); - mainLayout->addStretch(); - - setLayout(mainLayout); -} - diff --git a/src/gui/surfaceInput.h b/src/gui/surfaceInput.h deleted file mode 100644 index 9df5e737c..000000000 --- a/src/gui/surfaceInput.h +++ /dev/null @@ -1,98 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Handles surface inputs for the domain - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 SURFACEINPUT_H_ -#define SURFACEINPUT_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "WindNinjaInputs.h" -#include "timeZoneWidget.h" -#include "WidgetDownloadDEM.h" - -class surfaceInput : public QWidget -{ - Q_OBJECT - -public: - surfaceInput(QWidget *parent = 0); - -#ifdef NINJAFOAM - QGroupBox *foamCaseGroupBox; - QLineEdit *foamCaseLineEdit; - QToolButton *foamCaseOpenToolButton; -#endif - - QGroupBox *inputFileGroupBox; - - QLineEdit *inputFileLineEdit; - QToolButton *inputFileOpenToolButton; - QToolButton *downloadDEMButton; - - QGroupBox *roughnessGroupBox; - QComboBox *roughnessComboBox; - QLabel *roughnessLabel; - - QGroupBox *meshResGroupBox; - QComboBox *meshResComboBox; - QDoubleSpinBox *meshResDoubleSpinBox; - QRadioButton *meshMetersRadioButton, *meshFeetRadioButton; - - QGroupBox *timeZoneGroupBox; - timeZoneWidget *timeZone; - -#ifdef NINJAFOAM - QHBoxLayout *foamCaseLayout; -#endif - QHBoxLayout *inputLayout; - QHBoxLayout *roughnessLayout; - QHBoxLayout *roughnessLabelLayout; - QHBoxLayout *meshResLayout; - QHBoxLayout *timeZoneLayout; - QVBoxLayout *mainLayout; - -signals: - void writeToMainConsole(QString message); - -}; - -#endif /* SURFACEINPUT_H_ */ - diff --git a/src/gui/timeZoneWidget.cpp b/src/gui/timeZoneWidget.cpp deleted file mode 100644 index 2c8f57e78..000000000 --- a/src/gui/timeZoneWidget.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja - * Purpose: Widget for time zone access using boost local_time. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "timeZoneWidget.h" - -extern boost::local_time::tz_database globalTimeZoneDB; - -/** - * Construct a timeZoneWidget that relies on boost for time zone - * operations - * - * @param parent parent widget - */ -timeZoneWidget::timeZoneWidget( QWidget *parent ) : QWidget( parent ) -{ - tzComboBox = new QComboBox( this ); - tzCheckBox = new QCheckBox( tr( "Show All Zones" ), this ); - - tzDetailLabel = new QLabel( tr( "\n\n\n\n" ), this ); - tzDetailCheckBox = new QCheckBox( tr( "Display time zone details" ), this ); - - tzLayout = new QHBoxLayout(); - tzLayout->addWidget( tzComboBox ); - tzLayout->addWidget( tzCheckBox ); - tzLayout->addStretch(); - - layout = new QVBoxLayout(); - layout->addLayout( tzLayout ); - layout->addWidget( tzDetailCheckBox ); - layout->addWidget( tzDetailLabel ); - - setLayout( layout ); - - loadTimeZones(); - - createConnections(); - - tzCheckBox->setChecked( true ); - tzCheckBox->setChecked( false ); -} - -/** - * Destructor - * - */ -timeZoneWidget::~timeZoneWidget() -{ - -} - -/** - * Create connections to update the widgets - * - */ -void timeZoneWidget::createConnections() -{ - //connect the check box to toggle all time zones - connect( tzCheckBox, SIGNAL( toggled( bool ) ), - this, SLOT( toggleAllTimeZones( bool ) ) ); - - //connect the detail label visibility to the checkbox - connect( tzDetailCheckBox, SIGNAL( toggled( bool ) ), - this, SLOT( showDetails( bool ) ) ); - - //connect the combo box to the label text - connect( tzComboBox, SIGNAL( currentIndexChanged( int ) ), - this, SLOT( updateDetailString( int ) ) ); -} - -/** - * Load time zone information into the QStringList. - * From Boost Docs: - * @verbatim - Parameter is path to a time zone spec csv file (see Data File Details for - details on the contents of this file). This function populates the database - with time zone records found in the zone spec file. A - local_time::data_not_accessible exception will be thrown if - given zonespec file cannot be found. - local_time::bad_field_count exception will be thrown if the - number of fields in given zonespec file is incorrect. - @endverbatim - * - * - */ -void timeZoneWidget::loadTimeZones() -{ - std::vector tz_list = globalTimeZoneDB.region_list(); - for (unsigned int i = 0; i < tz_list.size(); i++) { - tzStringList << QString::fromStdString(tz_list[i]); - } -} -/** - * Load the default time zones for US. '~' represents standard zones. - * - * - ~America/Anchorage - * - America/Boise - * - ~America/Chicago - * - ~America/Denver - * - America/Detroit - * - America/Indiana/Indianapolis - * - America/Indiana/Knox - * - America/Indiana/Marengo - * - America/Indiana/Vevay - * - America/Indianapolis - * - America/Juneau - * - America/Kentucky/Louisville - * - America/Kentucky/Monticello - * - ~America/Los_Angeles - * - America/Louisville - * - ~America/New_York - * - America/Nome - * - America/North_Dakota/Center - * - ~America/Phoenix - * - ~Pacific/Honolulu - * - */ -void timeZoneWidget::loadDefaultTimeZones() -{ - QStringList tz_list, display_list; - tz_list << "America/Anchorage" - << "America/Boise" - << "America/Chicago" - << "America/Denver" - << "America/Detroit" - << "America/Indiana/Indianapolis" - << "America/Indiana/Knox" - << "America/Indiana/Marengo" - << "America/Indiana/Vevay" - << "America/Indianapolis" - << "America/Juneau" - << "America/Kentucky/Louisville" - << "America/Kentucky/Monticello" - << "America/Los_Angeles" - << "America/Louisville" - << "America/New_York" - << "America/Nome" - << "America/North_Dakota/Center" - << "America/Phoenix" - << "Pacific/Honolulu"; - display_list << "America/Anchorage(Alaska Time)" - << "America/Boise" - << "America/Chicago(Central Time)" - << "America/Denver(Mountain Time)" - << "America/Detroit" - << "America/Indiana/Indianapolis" - << "America/Indiana/Knox" - << "America/Indiana/Marengo" - << "America/Indiana/Vevay" - << "America/Indianapolis" - << "America/Juneau" - << "America/Kentucky/Louisville" - << "America/Kentucky/Monticello" - << "America/Los_Angeles(Pacific Time)" - << "America/Louisville" - << "America/New_York(Eastern Time)" - << "America/Nome" - << "America/North_Dakota/Center" - << "America/Phoenix" - << "Pacific/Honolulu(Hawaii Time)"; - - if( tz_list.size() != display_list.size() ) - qDebug() << "diurnalInput::loadDefaultTimeZones(): Wrong list size."; - - for( int i = 0;i < tz_list.size();i++ ) { - /* - * Check and make sure it's valid and on the boost list - */ - if( !tzStringList.contains( tz_list[i] ) ) - qDebug() << "Time Zone does not exist!" << tz_list[i]; - else - tzComboBox->addItem( display_list[i], - QVariant( tz_list[i] ) ); - } -} - -/** - * Slot to show all time zones or just US time zones - * - * @param showAll show all the timezones - */ -void timeZoneWidget::toggleAllTimeZones( bool showAll ) -{ - QString currentTimeZone = tzComboBox->currentText().split("(")[0]; - - if( showAll ) { - tzComboBox->clear(); - for( int i = 0;i < tzStringList.size();i++ ) { - tzComboBox->addItem( tzStringList[i], - QVariant( tzStringList[i] ) ); - } - } - else if( !showAll ) { - tzComboBox->clear(); - loadDefaultTimeZones(); - } - int index = 0; - if( !currentTimeZone.isEmpty() ) - { - index = tzComboBox->findText( currentTimeZone, Qt::MatchStartsWith ); - if( index < 0 ) - { - index = 0; - } - } - tzComboBox->setCurrentIndex( index ); -} - -/** - * Slot to update the description string for the time zone - * - * @param index combobox index after change - */ -void timeZoneWidget::updateDetailString( int index ) -{ - QString tzText = tzComboBox->itemData( index ).toString(); - emit tzChanged( tzText ); - if( tzDetailCheckBox->isChecked() == false ) - return; - else if( tzComboBox->count() == 0 ) - return; - else if( tzText.isEmpty() ) - return; - else { - blt::time_zone_ptr tz; - tz = globalTimeZoneDB.time_zone_from_region( tzText.toStdString() ); - bool has_dst = tz->has_dst(); - QString text = "Standard Name:\t\t"; - text += QString::fromStdString( tz->std_zone_name() ); - text += "\nDaylight Name:\t\t"; - if( has_dst ) - text += QString::fromStdString( tz->dst_zone_name() ) + "\n"; - else - text += "N/A\n"; - text += "Standard Offset from UTC:\t"; - text += QString::fromStdString( bpt::to_simple_string(tz->base_utc_offset() ) ); - text += "\nDaylight Offset from UTC:\t"; - if( has_dst ) { - bpt::time_duration d; - d = tz->base_utc_offset() + tz->dst_offset(); - text += QString::fromStdString( bpt::to_simple_string( d ) ); - } - else - text += "N/A"; - - tzDetailLabel->setText( text ); - } -} - -/** - * Slot to show or hide the detailed description of the time zone - * - * @param show show the label - */ -void timeZoneWidget::showDetails( bool show ) -{ - if( show ) - return updateDetailString( tzComboBox->currentIndex() ); - else - tzDetailLabel->setText( "\n" ); -} diff --git a/src/gui/timeZoneWidget.h b/src/gui/timeZoneWidget.h deleted file mode 100644 index c89683f3d..000000000 --- a/src/gui/timeZoneWidget.h +++ /dev/null @@ -1,86 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja - * Purpose: Widget for time zone access using boost local_time. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 TIME_ZONE_WIDGET_H -#define TIME_ZONE_WIDGET_H - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#ifndef Q_MOC_RUN -#include "boost/date_time/local_time/local_time.hpp" -#include "boost/date_time/posix_time/posix_time_types.hpp" -#endif - -namespace blt = boost::local_time; -namespace bpt = boost::posix_time; - -#include "ninjaException.h" -#include "ninja_conv.h" - -class timeZoneWidget : public QWidget -{ - Q_OBJECT - - public: - timeZoneWidget( QWidget *parent = 0 ); - ~timeZoneWidget(); - QComboBox *tzComboBox; /**< Select time zone */ - QCheckBox *tzCheckBox; /**< Display all zones, or US zones */ - QLabel *tzDetailLabel; /**< Show timezone details */ - QCheckBox *tzDetailCheckBox; /**< Toggle details */ - QStringList tzStringList; /**< List of zones read from boost */ - - void loadTimeZones(); - void loadDefaultTimeZones(); - - QHBoxLayout *tzLayout; - QVBoxLayout *layout; - - public slots: - void toggleAllTimeZones( bool showAll ); - private slots: - void updateDetailString( int index ); - void showDetails( bool show ); - private: - void createConnections(); - - signals: - void tzChanged( QString tz ); -}; - -#endif /* TIME_ZONE_WIDGET_H */ diff --git a/src/gui/ui/CMakeLists.txt b/src/gui/ui/CMakeLists.txt deleted file mode 100644 index 1da5c6c96..000000000 --- a/src/gui/ui/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -cmake_minimum_required(VERSION 3.1.0) - -# Find includes in corresponding build directories -set(CMAKE_INCLUDE_CURRENT_DIR ON) -# Instruct CMake to run moc automatically when needed -set(CMAKE_AUTOMOC ON) -# Create code from a list of Qt designer ui files -set(CMAKE_AUTOUIC ON) - -# Find the QtWidgets library -find_package(Qt5Core CONFIG REQUIRED) -find_package(Qt5Widgets CONFIG REQUIRED) -find_package(Qt5Gui CONFIG REQUIRED) - -include_directories( - ${Qt5Core_INCLUDE_DIRS} - ${Qt5Widgets_INCLUDE_DIRS} - ${Qt5Gui_INCLUDE_DIRS} - ${GDAL_INCLUDE_DIR} - ) - -add_definitions(${Qt5Widgets_DEFINITIONS}) - -qt5_add_resources(NINJA_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/windninja.qrc) - -# Populate a CMake variable with the sources -set(NINJA_GUI_SRCSXXX - mainwindow.ui - mainwindow.cpp - main.cpp -) -# Tell CMake to create the helloworld executable -add_executable(windninja5 ${NINJA_GUI_SRCSXXX} ${NINJA_RESOURCES}) -# Use the Widgets module from Qt 5 -target_link_libraries(windninja5 - ${Qt5Core_LIBRARIES} - ${Qt5Widgets_LIBRARIES} - ${Qt5Gui_LIBRARIES} - ${GDAL_LIBRARIES} - $<$:OpenMP::OpenMP_CXX> - ) diff --git a/src/gui/ui/icons/README b/src/gui/ui/icons/README deleted file mode 100644 index 07edb8f23..000000000 --- a/src/gui/ui/icons/README +++ /dev/null @@ -1,8 +0,0 @@ -# Icons - -Icons should be added from the tango project: - -tango-project.org - -Extract the tar archive and leave the base directory structure as organized in -the archive in the repository. diff --git a/src/gui/ui/icons/scalable/actions/document-new.svg b/src/gui/ui/icons/scalable/actions/document-new.svg deleted file mode 100644 index 1bfdb1640..000000000 --- a/src/gui/ui/icons/scalable/actions/document-new.svg +++ /dev/null @@ -1,448 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - New Document - - - Jakub Steiner - - - http://jimmac.musichall.cz - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/ui/icons/scalable/actions/document-open.svg b/src/gui/ui/icons/scalable/actions/document-open.svg deleted file mode 100644 index 55e6177d2..000000000 --- a/src/gui/ui/icons/scalable/actions/document-open.svg +++ /dev/null @@ -1,535 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Folder Icon Accept - 2005-01-31 - - - Jakub Steiner - - - - http://jimmac.musichall.cz - Active state - when files are being dragged to. - - - Novell, Inc. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/ui/icons/scalable/actions/document-save-as.svg b/src/gui/ui/icons/scalable/actions/document-save-as.svg deleted file mode 100644 index 01e2fb7a5..000000000 --- a/src/gui/ui/icons/scalable/actions/document-save-as.svg +++ /dev/null @@ -1,663 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Save As - - - Jakub Steiner - - - - - hdd - hard drive - save as - io - store - - - - - http://jimmac.musichall.cz - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/ui/icons/scalable/actions/document-save.svg b/src/gui/ui/icons/scalable/actions/document-save.svg deleted file mode 100644 index 2922c4331..000000000 --- a/src/gui/ui/icons/scalable/actions/document-save.svg +++ /dev/null @@ -1,619 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Save - - - Jakub Steiner - - - - - hdd - hard drive - save - io - store - - - - - http://jimmac.musichall.cz - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/ui/icons/scalable/actions/list-add.svg b/src/gui/ui/icons/scalable/actions/list-add.svg deleted file mode 100644 index 6eaed4481..000000000 --- a/src/gui/ui/icons/scalable/actions/list-add.svg +++ /dev/null @@ -1,436 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Add - 2006-01-04 - - - Andreas Nilsson - - - http://tango-project.org - - - add - plus - - - - - - - - - - - - - - - - - - diff --git a/src/gui/ui/main.cpp b/src/gui/ui/main.cpp deleted file mode 100644 index 0a1275f6a..000000000 --- a/src/gui/ui/main.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -#include -#include "mainwindow.h" - -int main(int argc, char **argv) { - QApplication app(argc, argv); - - MainWindow w; - w.show(); - return app.exec(); -} diff --git a/src/gui/ui/mainwindow.cpp b/src/gui/ui/mainwindow.cpp deleted file mode 100644 index aa5fd8f4a..000000000 --- a/src/gui/ui/mainwindow.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "mainwindow.h" -#include "ui_mainwindow.h" - -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) { - ui->setupUi(this); - - init(); -} - -MainWindow::~MainWindow() { - delete ui; -} - -void MainWindow::init() { - GDALAllRegister(); - setIcons(); - setConnections(); - OGRFormats(); - - // Set the number of cores available. We only have to do this once - int cores = QThread::idealThreadCount(); - ui->availCoreLabel->setText("Available Processors: " + QString::number(cores)); - ui->availCoreSpinBox->setMaximum(cores); -} - -void MainWindow::setIcons() { - ui->openElevButton->setIcon(QIcon(":icons/open.svg")); - ui->downloadElevButton->setIcon(QIcon(":icons/save-as.svg")); - - ui->addDomainRunButton->setIcon(QIcon(":icons/add.svg")); - - ui->downloadForecastButton->setIcon(QIcon(":icons/save-as.svg")); - ui->openForecastButton->setIcon(QIcon(":icons/open.svg")); -} - -void MainWindow::setConnections() { - connect(ui->openElevButton, SIGNAL(clicked()), this, SLOT(openElevation())); - connect(ui->meshChoiceCombo, SIGNAL(currentIndexChanged(int)), - this, SLOT(updateMesh(int))); - connect(ui->treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), - this, SLOT(updateStack(QTreeWidgetItem*, QTreeWidgetItem*))); -} - -void MainWindow::OGRFormats() { - GDALDriverH hDrv = 0; - const char *pszV = 0; - const char *pszC = 0; - QString longname, shortname, ext; - // Requires GDAL 2.0+ - for(int i = 0; i < GDALGetDriverCount(); i++) { - hDrv = GDALGetDriver(i); - assert(hDrv); - /* - ** TODO(kyle): We need to define a short list of 'normal' vector - ** drivers, with an option to show all writable vector formats. This - ** might be handled by newer versions of GDALOutput() stuff. There may - ** be more restrictions that need to be in place here as well (field - ** types, etc. - */ - pszV = GDALGetMetadataItem(hDrv, GDAL_DCAP_VECTOR, NULL); - pszC = GDALGetMetadataItem(hDrv, GDAL_DCAP_CREATE, NULL); - if(pszV && CPLTestBoolean(pszV) && pszC && CPLTestBoolean(pszC)) { - longname = QString(GDALGetDriverLongName(hDrv)); - shortname = QString(GDALGetDriverShortName(hDrv)); - ext = QString(GDALGetMetadataItem(hDrv, "", GDAL_DMD_EXTENSION)); - //qDebug() << longname << "," << shortname << "," << ext; - ui->ogrFormatCombo->addItem(longname, shortname); - } - } -} - -void MainWindow::openElevation() { - //ui->elevEdit->clear(); - //ui->vegCombo->setEnabled(true); - QString file = QFileDialog::getOpenFileName(this, - tr("Open Elevation Input File"), - "./", - tr("Elevation Input Files (*.asc *.lcp *.tif *.img)")); - if(file == "") { - return; - } - // Check file via API - QFileInfo info = QFileInfo(file); - GDALDatasetH hDS = GDALOpen(file.toLocal8Bit().data(), GA_ReadOnly); - if(hDS == NULL) { - return; - } - GDALDriverH hDrv = GDALGetDatasetDriver(hDS); - if(hDrv == NULL) { - GDALClose(hDS); - return; - } - if(EQUAL(GDALGetDriverShortName(hDrv), "LCP")) { - ui->vegCombo->setDisabled(true); - } - ui->elevEdit->setText(info.fileName()); - GDALClose(hDS); -} - -void MainWindow::updateMesh(int index) { - ui->meshSpinBox->setEnabled(index == 3); - ui->meshUnitCombo->setEnabled(index == 3); -} - -void MainWindow::updateStack(QTreeWidgetItem *current, QTreeWidgetItem *previous) { - QString name = current->text(0); - if(name == "Solver") { - ui->stackedWidget->setCurrentIndex(0); - } else if(name == "Site Information") { - ui->stackedWidget->setCurrentIndex(1); - } else if(name == "Initialization") { - ui->stackedWidget->setCurrentIndex(2); - } else if(name == "Output") { - ui->stackedWidget->setCurrentIndex(3); - } else if(name == "Run") { - ui->stackedWidget->setCurrentIndex(4); - } else { - assert(0); - } -} - - - - - - - - - - - - - - - - - - diff --git a/src/gui/ui/mainwindow.h b/src/gui/ui/mainwindow.h deleted file mode 100644 index 375a0c029..000000000 --- a/src/gui/ui/mainwindow.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace Ui { -class MainWindow; -} - -const int defaultMsgLength = 2500; - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); - -private: - Ui::MainWindow *ui; - - QStringList ogrFormats; - - void init(); - - void setIcons(); - void setConnections(); - void OGRFormats(); - -public slots: - void updateStack(QTreeWidgetItem *, QTreeWidgetItem *); - void openElevation(); - void updateMesh(int index); -}; - -#endif // MAINWINDOW_H diff --git a/src/gui/ui/mainwindow.ui b/src/gui/ui/mainwindow.ui deleted file mode 100644 index 8becf6b95..000000000 --- a/src/gui/ui/mainwindow.ui +++ /dev/null @@ -1,857 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 563 - 641 - - - - WindNinja - - - - - - - 1 - - - - WindNinja - - - - - Solver - - - - - Site Information - - - - - Initialization - - - - - Output - - - - - Run - - - - - - - - 4 - - - - - - - Conservation of Mass - - - true - - - - - - - Conservation of Mass && Momentum - - - - - - - Diurnal - - - - - - - Stability - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - Elevation Data - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - Open... - - - Qt::ToolButtonTextBesideIcon - - - - - - - Download... - - - Qt::ToolButtonTextBesideIcon - - - - - - - - - - - Time Zone - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Show all zones - - - - - - - - - - - Vegetation - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - Grass - - - - - Brush - - - - - Trees - - - - - - - - - - - - Mesh Resolution - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - Coarse - - - - - Medium - - - - - Fine - - - - - Custom... - - - - - - - - false - - - - - - - false - - - - meters - - - - - feet - - - - - - - - - - Qt::Vertical - - - - 20 - 257 - - - - - - - - - - - - - - Initialization Method - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - Domain Averaged - - - - - Point - - - - - Weather Model - - - - - - - - - - 0 - - - - - - - - - Wind Height - - - - - - - 500.000000000000000 - - - 20.000000000000000 - - - - - - - - Feet - - - - - Meters - - - - - - - - - - - - Wind Speed Units - - - - - - - - Miles/Hour - - - - - Kilometers/Hour - - - - - Meters/Second - - - - - - - - - - - - Temperature Units - - - - - - - - Fahrenheit - - - - - Celsius - - - - - - - - - - - - Speed - - - - - - - + - - - - - - - - - - - - - - - - - - - Cloud Cover - - - - - - - Air Temp. - - - - - - - Time - - - - - - - - - - Direction - - - - - - - - - - - - - - - - - - - Start Time - - - - - - - - - - End Time - - - - - - - - - - - - - - Source - - - - - - - - - - Download - - - Qt::ToolButtonTextBesideIcon - - - - - - - - - - - Forecast File - - - - - - - - - - Open... - - - Qt::ToolButtonTextBesideIcon - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - Output Format - - - - - - - - - - - - Resolution - - - - - - Use Mesh Resolution - - - true - - - - - - - - - false - - - - - - - false - - - - meters - - - - - feet - - - - - - - - - - - - - Specify creation options - - - - - - - false - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Add Output - - - Qt::ToolButtonTextBesideIcon - - - - - - - - - - - - - Available Processors: 1 - - - - - - - - - Number of Processors - - - - - - - 1 - - - - - - - - - false - - - Open Output Directory - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - 0 - 0 - 563 - 24 - - - - - &File - - - - - &Tools - - - - - &Help - - - - - - - - - - - - initCombo - currentIndexChanged(int) - initStack - setCurrentIndex(int) - - - 468 - 184 - - - 281 - 350 - - - - - createOptCheck - clicked(bool) - createOptList - setEnabled(bool) - - - 281 - 322 - - - 281 - 402 - - - - - diff --git a/src/gui/ui/windninja.qrc b/src/gui/ui/windninja.qrc deleted file mode 100644 index 6ba28ce69..000000000 --- a/src/gui/ui/windninja.qrc +++ /dev/null @@ -1,9 +0,0 @@ - - - icons/scalable/actions/document-new.svg - icons/scalable/actions/document-open.svg - icons/scalable/actions/document-save.svg - icons/scalable/actions/document-save-as.svg - icons/scalable/actions/list-add.svg - - diff --git a/src/gui/vec3f.cpp b/src/gui/vec3f.cpp deleted file mode 100644 index 02869fe39..000000000 --- a/src/gui/vec3f.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: OpenGL 3d vector class - * Author: See below - * - ****************************************************************************** - * - * SEE COPYRIGHT BELOW - * - * 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. - * - *****************************************************************************/ - -/* Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above notice and this permission notice shall be included in all copies - * or substantial portions of the Software. - * - * 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. - */ -/* File for "Terrain" lesson of the OpenGL tutorial on - * www.videotutorialsrock.com - */ - - - -#include - -#include "vec3f.h" - - - -Vec3f::Vec3f() { - -} - -Vec3f::Vec3f(float x, float y, float z) { - v[0] = x; - v[1] = y; - v[2] = z; -} - -float &Vec3f::operator[](int index) { - return v[index]; -} - -float Vec3f::operator[](int index) const { - return v[index]; -} - -Vec3f Vec3f::operator*(float scale) const { - return Vec3f(v[0] * scale, v[1] * scale, v[2] * scale); -} - -Vec3f Vec3f::operator/(float scale) const { - return Vec3f(v[0] / scale, v[1] / scale, v[2] / scale); -} - -Vec3f Vec3f::operator+(const Vec3f &other) const { - return Vec3f(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]); -} - -Vec3f Vec3f::operator-(const Vec3f &other) const { - return Vec3f(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]); -} - -Vec3f Vec3f::operator-() const { - return Vec3f(-v[0], -v[1], -v[2]); -} - -const Vec3f &Vec3f::operator*=(float scale) { - v[0] *= scale; - v[1] *= scale; - v[2] *= scale; - return *this; -} - -const Vec3f &Vec3f::operator/=(float scale) { - v[0] /= scale; - v[1] /= scale; - v[2] /= scale; - return *this; -} - -const Vec3f &Vec3f::operator+=(const Vec3f &other) { - v[0] += other.v[0]; - v[1] += other.v[1]; - v[2] += other.v[2]; - return *this; -} - -const Vec3f &Vec3f::operator-=(const Vec3f &other) { - v[0] -= other.v[0]; - v[1] -= other.v[1]; - v[2] -= other.v[2]; - return *this; -} - -float Vec3f::magnitude() const { - return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); -} - -float Vec3f::magnitudeSquared() const { - return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; -} - -Vec3f Vec3f::normalize() const { - float m = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - return Vec3f(v[0] / m, v[1] / m, v[2] / m); -} - -float Vec3f::dot(const Vec3f &other) const { - return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2]; -} - -Vec3f Vec3f::cross(const Vec3f &other) const { - return Vec3f(v[1] * other.v[2] - v[2] * other.v[1], - v[2] * other.v[0] - v[0] * other.v[2], - v[0] * other.v[1] - v[1] * other.v[0]); -} - -Vec3f operator*(float scale, const Vec3f &v) { - return v * scale; -} - -std::ostream &operator<<(std::ostream &output, const Vec3f &v) { - std::cout << '(' << v[0] << ", " << v[1] << ", " << v[2] << ')'; - return output; -} - - - - - - - - - diff --git a/src/gui/vec3f.h b/src/gui/vec3f.h deleted file mode 100644 index 1beb1838a..000000000 --- a/src/gui/vec3f.h +++ /dev/null @@ -1,91 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: OpenGL 3d vector class - * Author: See below - * - ****************************************************************************** - * - * SEE COPYRIGHT BELOW - * - * 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. - * - *****************************************************************************/ - -/* Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above notice and this permission notice shall be included in all copies - * or substantial portions of the Software. - * - * 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. - */ -/* File for "Terrain" lesson of the OpenGL tutorial on - * www.videotutorialsrock.com - */ - -#ifndef VEC3F_H_INCLUDED -#define VEC3F_H_INCLUDED - -#include -#include - -class Vec3f { - private: - float v[3]; - public: - Vec3f(); - Vec3f(float x, float y, float z); - - float &operator[](int index); - float operator[](int index) const; - - Vec3f operator*(float scale) const; - Vec3f operator/(float scale) const; - Vec3f operator+(const Vec3f &other) const; - Vec3f operator-(const Vec3f &other) const; - Vec3f operator-() const; - - const Vec3f &operator*=(float scale); - const Vec3f &operator/=(float scale); - const Vec3f &operator+=(const Vec3f &other); - const Vec3f &operator-=(const Vec3f &other); - - float magnitude() const; - float magnitudeSquared() const; - Vec3f normalize() const; - float dot(const Vec3f &other) const; - Vec3f cross(const Vec3f &other) const; -}; - -Vec3f operator*(float scale, const Vec3f &v); -std::ostream &operator<<(std::ostream &output, const Vec3f &v); - -#endif diff --git a/src/gui/vtkOutput.cpp b/src/gui/vtkOutput.cpp deleted file mode 100644 index 18c551a9d..000000000 --- a/src/gui/vtkOutput.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/****************************************************************************** - * toggle nerdtree on/off - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: vtk output selection widget - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "vtkOutput.h" - -vtkOutput::vtkOutput(QWidget *parent) : QWidget(parent) -{ - vtkGroupBox = new QGroupBox(tr("Create VTK Files (*.vtk)"), this); - vtkGroupBox->setCheckable(true); - vtkGroupBox->setChecked(false); - - vtkLabel = new QLabel(tr(" Write VTK surface and volume files\n" - " \n" - " Note that *vtk files are for advanced users and \n" - " are rarely used by fire managers/modelers.\n" - ), this); - - //writeVolumeCheckBox = new QCheckBox(tr("Volume File"), this); - //writeSurfaceCheckBox = new QCheckBox(tr("Surface File"), this); - - //vtkLayout = new QVBoxLayout; - //vtkLayout->addWidget(vtkLabel); - //vtkLayout->addWidget(writeVolumeCheckBox); - //vtkLayout->addWidget(writeSurfaceCheckBox); - //vtkLayout->addStretch(); - //vtkLayout->addWidget(vtkWarningLabel); - //vtkGroupBox->setLayout(vtkLayout); - - layout = new QVBoxLayout; - - layout->addWidget(vtkGroupBox); - layout->addWidget(vtkLabel); - layout->addStretch(); - setLayout(layout); - //layout->addStretch(); -} - diff --git a/src/gui/vtkOutput.h b/src/gui/vtkOutput.h deleted file mode 100644 index 417fe1559..000000000 --- a/src/gui/vtkOutput.h +++ /dev/null @@ -1,55 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: vtk output selection widget - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 VTKOUTPUT_H -#define VTKOUTPUT_H - -#include -#include -#include -#include -#include - -class vtkOutput : public QWidget -{ - Q_OBJECT - - public: - vtkOutput(QWidget *parent = 0); - - QGroupBox *vtkGroupBox; - QLabel *vtkLabel; - QCheckBox *writeVolumeCheckBox; - QCheckBox *writeSurfaceCheckBox; - - QVBoxLayout *vtkLayout; - QVBoxLayout *layout; -}; - -#endif /* VTKOUTPUT_H */ diff --git a/src/gui/weatherModel.cpp b/src/gui/weatherModel.cpp deleted file mode 100644 index 15506559a..000000000 --- a/src/gui/weatherModel.cpp +++ /dev/null @@ -1,769 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Client to download and access weather data - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "weatherModel.h" - -/** - * Constructor - * - * @param parent parent widget for the widget - */ -weatherModel::weatherModel(QWidget *parent) : QWidget(parent) -{ - weatherGroupBox = new QGroupBox( tr( "Weather Model Initialization" ), - this ); - weatherGroupBox->setCheckable( true ); - weatherGroupBox->setChecked(false); - - downloadGroupBox = new QGroupBox(tr("Download Weather Data")); - - modelComboBox = new QComboBox(this); - modelComboBox->setDuplicatesEnabled(false); - - hourSpinBox = new QSpinBox(this); - hourSpinBox->setRange(3, 84); - hourSpinBox->setSingleStep(1); - hourSpinBox->setSuffix(" hours"); - hourSpinBox->setValue(3); - hourSpinBox->setAccelerated( true ); - //hourSpinBox->setVisible( false ); - - downloadToolButton = new QToolButton(this); - downloadToolButton->setText(tr("Download data")); - downloadToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - downloadToolButton->setIcon(QIcon(":server_go.png")); - - //QDirModel will be deprecated in 4.7, use QFileSystemModel if upgrading - model = new QDirModel(this); - model->setReadOnly(true); - model->setSorting( QDir::Time ); - - forecastListLabel = new QLabel( this ); - forecastListLabel->setText( "Downloaded forecasts" ); - - treeView = new QTreeView(this); - treeView->setModel(model); - treeView->header()->setStretchLastSection(true); - treeView->setAnimated(true); - treeView->setColumnHidden(1, true); - treeView->setColumnHidden(2, true); - //treeView->setColumnHidden(3, true); - //treeView->setDragDropMode( QAbstractItemView::DragOnly ); - treeView->setAlternatingRowColors( false ); - treeView->setSelectionBehavior(QAbstractItemView::SelectRows); - - timeGroupBox = new QGroupBox(tr("Select specific time steps"), this); - timeGroupBox->setCheckable(true); - timeGroupBox->setChecked(false); - - listView = new QListView(this); - timeModel = new QStringListModel(this); - listView->setModel(timeModel); - // Extended selection is clear on click, unless ctrl or shift is held. - // listView->setSelectionMode(QAbstractItemView::ExtendedSelection); - // Multi selection is click everything on off, no modifiers - listView->setSelectionMode(QAbstractItemView::MultiSelection); - listView->setEditTriggers(QAbstractItemView::NoEditTriggers); - - selectAllTimesButton = new QToolButton(this); - selectAllTimesButton->setText(tr("Select All")); - selectAllTimesButton->setToolTip(tr("Select all forecast times")); - selectAllTimesButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - selectNoTimesButton = new QToolButton(this); - selectNoTimesButton->setText(tr("Select None")); - selectNoTimesButton->setToolTip(tr("Select no forecast times")); - selectNoTimesButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - statusLabel = new QLabel(this); - statusLabel->setText(tr("Ready.")); - - refreshToolButton = new QToolButton(this); - refreshToolButton->setText(tr("Refresh Forecast List")); - refreshToolButton->setIcon(QIcon(":arrow_rotate_clockwise.png")); - refreshToolButton->setToolTip(tr("Refresh the forecast listing.")); - refreshToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - startDateLabel = new QLabel(this); - endDateLabel = new QLabel(this); - - startTime = new QDateTimeEdit(this); - startTime->setDisplayFormat("MM/dd/yyyy HH:00"); - startTime->setCalendarPopup(true); - - stopTime = new QDateTimeEdit(this); - stopTime->setDisplayFormat("MM/dd/yyyy HH:00"); - stopTime->setCalendarPopup(true); - - updatePastcastTimesAndLabels(); - - startDateLabel->setVisible(false); - endDateLabel->setVisible(false); - startTime->setVisible(false); - stopTime->setVisible(false); - - QVBoxLayout *startTimeLayout = new QVBoxLayout; - startTimeLayout->addWidget(startDateLabel); - startTimeLayout->addWidget(startTime); - - QVBoxLayout *stopTimeLayout = new QVBoxLayout; - stopTimeLayout->addWidget(endDateLabel); - stopTimeLayout->addWidget(stopTime); - - QHBoxLayout *dateTimeLayout = new QHBoxLayout; - dateTimeLayout->addLayout(startTimeLayout); - dateTimeLayout->addLayout(stopTimeLayout); - - - connect(downloadToolButton, SIGNAL(clicked()), - this, SLOT(getData())); - connect(refreshToolButton, SIGNAL(clicked()), - this, SLOT(checkForModelData())); - connect(treeView, SIGNAL(pressed(const QModelIndex &)), - this, SLOT(displayForecastTime(const QModelIndex &))); - - //clear the selection on uncheck of group box - connect(weatherGroupBox, SIGNAL(toggled(bool)), - this, SLOT(unselectForecast(bool))); - - //change time for given model - connect(modelComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(setTimeLimits(int))); - connect(modelComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(setComboToolTip(int))); - connect(modelComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(displayArchiveDates(int))); - - connect(selectAllTimesButton, SIGNAL(clicked(bool)), - listView, SLOT(selectAll(void))); - - connect(selectNoTimesButton, SIGNAL(clicked(bool)), - listView, SLOT(clearSelection(void))); - - //layout - downloadLayout = new QHBoxLayout; - downloadLayout->addWidget(modelComboBox); - downloadLayout->addWidget(hourSpinBox); - downloadLayout->addWidget(downloadToolButton); - - downloadGroupBox->setLayout(downloadLayout); - - treeLayout = new QHBoxLayout; - treeLayout->addWidget(treeView); - - timeLayout = new QVBoxLayout; - timeLayout->addWidget(listView); - - selectLayout = new QHBoxLayout; - selectLayout->addStretch(); - selectLayout->addWidget(selectAllTimesButton); - selectLayout->addWidget(selectNoTimesButton); - - timeLayout->addLayout(selectLayout); - - timeGroupBox->setLayout(timeLayout); - - loadLayout = new QHBoxLayout; - loadLayout->addWidget(statusLabel); - loadLayout->addWidget(refreshToolButton); - - weatherLayout = new QVBoxLayout; - weatherLayout->addWidget(downloadGroupBox); - weatherLayout->addLayout(dateTimeLayout); - weatherLayout->addWidget(forecastListLabel); - weatherLayout->addLayout(treeLayout); - weatherLayout->addWidget(timeGroupBox); - weatherLayout->addLayout(loadLayout); - - ninjafoamConflictLabel = new QLabel(tr("The weather model initialization option is not currently available for the\n" - "momentum solver."), this); - ninjafoamConflictLabel->setHidden(true); - - weatherGroupBox->setLayout( weatherLayout ); - - layout = new QVBoxLayout; - layout->addWidget(ninjafoamConflictLabel); - layout->addWidget( weatherGroupBox ); - setLayout(layout); - -#ifdef WITH_NOMADS_SUPPORT - nNomadsCount = 0; - while( apszNomadsKeys[nNomadsCount][0] != NULL ) - nNomadsCount++; - papoNomads = new NomadsWxModel*[nNomadsCount]; - int i = 0; - while( apszNomadsKeys[i][0] != NULL ) - { - papoNomads[i] = new NomadsWxModel( apszNomadsKeys[i][0] ); - i++; - } - CPLDebug( "WINDNINJA", "Loaded %d NOMADS models", nNomadsCount ); -#endif - progressDialog = new QProgressDialog( this ); - progressDialog->setAutoClose( false ); - progressDialog->setAutoReset( false ); - progressDialog->setModal( true ); - - pGlobProg = progressDialog; - - loadModelComboBox(); - - checkForModelData(); -} - -/** - * Destructor. Closes files.` - */ -weatherModel::~weatherModel() -{ -#ifdef WITH_NOMADS_SUPPORT - for( int i = 0; i < nNomadsCount; i++ ) - delete papoNomads[i]; - delete [] papoNomads; -#endif -} - -/** - * Populate the combobox with available models from the host_list.xml - * - * @return false if population fails - */ -void weatherModel::loadModelComboBox() -{ - modelComboBox->addItem("=== Forecasts ==="); - QModelIndex index = modelComboBox->model()->index(modelComboBox->count() - 1, 0); - modelComboBox->model()->setData(index, 0, Qt::UserRole - 1); - - modelComboBox->addItem(QString::fromStdString(ndfd.getForecastIdentifier())); - modelComboBox->addItem(QString::fromStdString(nam.getForecastIdentifier())); - modelComboBox->addItem(QString::fromStdString(rap.getForecastIdentifier())); - modelComboBox->addItem(QString::fromStdString(namAk.getForecastIdentifier())); - modelComboBox->addItem(QString::fromStdString(gfs.getForecastIdentifier())); - -#ifdef WITH_NOMADS_SUPPORT - - for (int i = 0; i < nNomadsCount; i++) - { - QString s = QString::fromStdString(papoNomads[i]->getForecastReadable('-')).toUpper(); - modelComboBox->addItem(s); - } -#endif - - modelComboBox->addItem("=== Pastcasts ==="); - index = modelComboBox->model()->index(modelComboBox->count() - 1, 0); - modelComboBox->model()->setData(index, 0, Qt::UserRole - 1); - modelComboBox->addItem(QString::fromStdString(archhrr.getForecastIdentifier())); - - modelComboBox->setCurrentIndex(1); - -} - -void weatherModel::setTimeLimits( int index ) -{ - if( index == 1 ) - hourSpinBox->setRange( ndfd.getStartHour(), ndfd.getEndHour() ); - else if( index == 2 ) - hourSpinBox->setRange( nam.getStartHour(), nam.getEndHour() ); - else if( index == 3 ) - hourSpinBox->setRange( rap.getStartHour(), rap.getEndHour() ); - else if( index == 4 ) - hourSpinBox->setRange( namAk.getStartHour(), namAk.getEndHour() ); - else if( index == 5 ) - hourSpinBox->setRange( gfs.getStartHour(), gfs.getEndHour() ); - else if (index == modelComboBox->count() - 1) { - return; - } - else - { -#ifdef WITH_NOMADS_SUPPORT - int n = index - 6; - hourSpinBox->setRange( papoNomads[n]->getStartHour(), - papoNomads[n]->getEndHour() ); -#endif - return; - } -} - -static int UpdateProgress( double dfProgress, const char *pszMessage, - void *pProgressArg ) -{ - (void*)pProgressArg; - if( pszMessage ) - pGlobProg->setLabelText( pszMessage ); - pGlobProg->setValue( dfProgress * 100 ); - QCoreApplication::processEvents(); - if( pGlobProg->wasCanceled() ) - return 1; - return 0; -} - -/** - * Retrieve the forecast from the UCAR thredds server based on user input - * - */ -void weatherModel::getData() -{ - statusLabel->setText( "Downloading data..."); - setCursor(Qt::WaitCursor); - int modelChoice = modelComboBox->currentIndex(); - int hours = hourSpinBox->value(); - - //change length to 4 days; - //days = 20; - - wxModelInitialization *model; - - if( inputFile.isEmpty() ) { - statusLabel->setText( "No input dem file specified" ); - setCursor(Qt::ArrowCursor); - return; - } - if( modelChoice == 1 ) - model = new ncepNdfdInitialization( ndfd ); - else if( modelChoice == 2 ) - model = new ncepNamSurfInitialization( nam ); - else if( modelChoice == 3 ) - model = new ncepRapSurfInitialization( rap ); - else if( modelChoice == 4 ) - model = new ncepNamAlaskaSurfInitialization( namAk ); - else if( modelChoice == 5 ) - model = new ncepGfsSurfInitialization( gfs ); - else if ( modelChoice == modelComboBox->count() - 1) { - model = new GCPWxModel(archhrr); - progressDialog->reset(); - progressDialog->setRange( 0, 100 ); - model->SetProgressFunc( (GDALProgressFunc)&UpdateProgress ); - progressDialog->show(); - progressDialog->setCancelButtonText( "Cancel" ); - } - else - { -#ifdef WITH_NOMADS_SUPPORT - model = papoNomads[modelChoice - 6]; - /* - ** Disable progress on 32-bit windows as we segfault. - */ -#if defined(WIN32) && defined(NINJA_32BIT) - model->SetProgressFunc( NULL ); - QCoreApplication::processEvents(); -#else /* defined(WIN32) && defined(NINJA_32BIT) */ - progressDialog->reset(); - progressDialog->setRange( 0, 100 ); - model->SetProgressFunc( (GDALProgressFunc)&UpdateProgress ); - progressDialog->show(); - progressDialog->setCancelButtonText( "Cancel" ); -#endif /* defined(WIN32) && defined(NINJA_32BIT) */ -#endif /* WITH_NOMADS_SUPPORT */ - } - - try { - if(modelChoice != modelComboBox->count() - 1) { - model->fetchForecast( inputFile.toStdString(), hours ); - } - else { - - QDateTime startDT = LocalToUtc(startTime->dateTime()); - QDateTime endDT = LocalToUtc(stopTime->dateTime()); - - if (startDT < minDateTime || endDT > maxDateTime) { - progressDialog->close(); - QMessageBox::warning(this, "Out of Bounds", - QString("Time range must be between %1 and %2.") - .arg(UtcToLocal(minDateTime).toString("yyyy/MM/dd HH:00")) - .arg(UtcToLocal(maxDateTime).toString("yyyy/MM/dd HH:00"))); - setCursor(Qt::ArrowCursor); - return; - } - if (startDT > endDT) { - progressDialog->close(); - QMessageBox::warning(this, "Invalid Range", "The start time must be before the stop time."); - setCursor(Qt::ArrowCursor); - return; - } - if (startDT.daysTo(endDT) > 14) { - progressDialog->close(); - QMessageBox::warning(this, "Invalid Range", "The time range cannot exceed 14 days."); - setCursor(Qt::ArrowCursor); - return; - } - - // Extract start date and time - QDate startQDate = startDT.date(); - QTime startQTime = startDT.time(); - int startYear = startQDate.year(); - int startMonth = startQDate.month(); - int startDay = startQDate.day(); - int startHour = startQTime.hour(); - - // Extract end date and time - QDate endQDate = endDT.date(); - QTime endQTime = endDT.time(); - int endYear = endQDate.year(); - int endMonth = endQDate.month(); - int endDay = endQDate.day(); - int endHour = endQTime.hour(); - - // Convert to boost dates - boost::gregorian::date startDate(startYear, startMonth, startDay); - boost::gregorian::date endDate(endYear, endMonth, endDay); - - auto* forecastModel = dynamic_cast(model); - forecastModel->setDateTime(startDate, endDate, boost::lexical_cast(startHour), boost::lexical_cast(endHour)); - - model->fetchForecast(inputFile.toStdString(), hours); - } - } - catch( badForecastFile &e ) { - progressDialog->close(); - QMessageBox::warning( this, "WindNinja", - QString::fromStdString( e.what() ), - QMessageBox::Ok ); - setCursor(Qt::ArrowCursor); - return; - } - catch( std::runtime_error &e ) { - progressDialog->close(); - QMessageBox::warning( this, "WindNinja", - QString::fromStdString( e.what() ), - QMessageBox::Ok ); - setCursor(Qt::ArrowCursor); - checkForModelData(); - return; - } - -#if !defined(NINJA_32BIT) - if( modelChoice > 5 ) - { - progressDialog->setRange( 0, 100 ); - progressDialog->setValue( 100 ); - progressDialog->setLabelText( "Done" ); - progressDialog->setCancelButtonText( "Close" ); - /* Should we autoclose, or ask user to close */ - progressDialog->close(); - } -#endif /* defined(NINJA_64BIT) */ - - checkForModelData(); - setCursor(Qt::ArrowCursor); - - //connect with thread::finished()? - if( modelChoice < 6 ) - delete model; -} - -/** - * Check the working directory for a weather model data directory - * - */ -void weatherModel::checkForModelData() -{ - QDir wd(cwd); - - QStringList filters; - /* ndfd */ - filters << QString::fromStdString( ndfd.getForecastIdentifier() ) - + "-" + QFileInfo( inputFile ).fileName(); - /* nam suface */ - filters << QString::fromStdString( nam.getForecastIdentifier() ) - + "-" + QFileInfo( inputFile ).fileName(); - /* rap surface */ - filters << QString::fromStdString( rap.getForecastIdentifier() ) - + "-" + QFileInfo( inputFile ).fileName(); - /* nam alaska surface */ - filters << QString::fromStdString( namAk.getForecastIdentifier() ) - + "-" + QFileInfo( inputFile ).fileName(); - /* gfs */ - filters << QString::fromStdString( gfs.getForecastIdentifier() ) - + "-" + QFileInfo( inputFile ).fileName(); - filters << QString::fromStdString( archhrr.getForecastIdentifier() ) - + "-" + QFileInfo( inputFile ).fileName(); - -#ifdef WITH_NOMADS_SUPPORT - int i; - for( i = 0; i < nNomadsCount; i++ ) - { - filters << - (QString::fromStdString(papoNomads[i]->getForecastReadable('-') ) - + "-" + QFileInfo( inputFile ).fileName()).toUpper(); - } - filters << "20*.zip"; -#endif - //filter to see the folder in utc time - filters << "20*T*"; - filters << "*.nc"; - model->setNameFilters(filters); - model->setFilter(QDir::Files | QDir::Dirs); - treeView->setRootIndex(model->index(wd.absolutePath())); - treeView->resizeColumnToContents(0); - statusLabel->setText( "" ); - timeModel->setStringList(QStringList()); - - unselectForecast(false); - // QModelIndex index = treeView->indexBelow( treeView->rootIndex() ); - // treeView->setExpanded( index, true ); - // index = treeView->indexBelow( index ); - // treeView->setExpanded( index, true ); -} - -/** - * Slot to catch any time the dem file changes - * - * @param newFile new dem file name - */ -void weatherModel::setInputFile(QString newFile) -{ - inputFile = newFile; - cwd = QFileInfo(newFile).absolutePath(); - checkForModelData(); -} - -/** - * Display the first forecast time for a given file - * - * @param index index in the tree view. - */ -void weatherModel::displayForecastTime( const QModelIndex &index ) -{ - clearTimes(); - - if(model->fileInfo(index).isDir()==true) - { - treeView->selectionModel()->select(index,QItemSelectionModel::Deselect|QItemSelectionModel::Rows); - } - - QFileInfo fi( model->fileInfo( index ) ); - - if( !fi.exists() ) { - statusLabel->setText( "" ); - return; - } - std::string filename = fi.absoluteFilePath().toStdString(); - wxModelInitialization* model = NULL; - try { - model = wxModelInitializationFactory::makeWxInitialization(filename); - timelist = model->getTimeList(tzString.toStdString()); - } - catch( ... ) { - statusLabel->setText( "" ); - delete model; - return; - } - - delete model; - - blt::local_time_facet* facet; - facet = new blt::local_time_facet(); - std::ostringstream os; - os.imbue(std::locale(std::locale::classic(), facet)); - facet->format("%a %b %d %H:%M %z"); - os << timelist[0]; - QString dateTime = QString::fromStdString( os.str() ); - dateTime.prepend( "First forecast time: " ); - - statusLabel->setText( dateTime ); - - //blt::time_zone_ptr utc = globalTimeZoneDB.time_zone_from_region("UTC"); - QStringList times; - for(int i = 0; i < timelist.size(); i++) { - std::ostringstream os; - os.imbue(std::locale(std::locale::classic(), facet)); - facet->format("%a %b %d %H:%M %z"); - //os << timelist[i].local_time_in(utc); - os << timelist[i]; - times.append(QString::fromStdString(os.str())); - os.clear(); - } - timeModel->setStringList(times); - listView->selectAll(); - listView->setFocus(Qt::OtherFocusReason); -} - -/** - * Slot to recieve a change in timezone from location - * - * @param tz timezone string - */ -void weatherModel::updateTz( QString tz ) -{ - tzString = tz; - checkForModelData(); - updatePastcastTimesAndLabels(); -} - -/** - * Unselect the forecast if the group box is disabled. This is for - * mainWindow check...() fx(s) - * - * @param checked - */ -void weatherModel::unselectForecast( bool checked ) -{ - if( checked ) return; - treeView->selectionModel()->clear(); - clearTimes(); -} -const char * weatherModel::ExpandDescription( const char *pszReadable ) -{ - if( pszReadable == NULL ) - return NULL; - const char *pszDesc = NULL; - const char *pszTmp = NULL; - int i; - char **papszKeys = NULL; - papszKeys = CSLTokenizeString2( pszReadable, "- ", 0 ); - i = 0; - while( papszKeys[i] != NULL ) - { - { - pszTmp = CSLFetchNameValue( (char**)apszWxModelGlossary, papszKeys[i] ); - if( pszTmp != NULL ) - { - if( pszDesc == NULL ) - pszDesc = CPLSPrintf( "%s", pszTmp ); - else - pszDesc = CPLSPrintf( "%s, %s", pszDesc, pszTmp ); - } - } - i++; - } - return pszDesc; -} - -void weatherModel::updatePastcastTimesAndLabels() -{ - minDateTime = QDateTime( QDate(2014, 7, 30), QTime(18, 0), Qt::UTC ); - maxDateTime = QDateTime::currentDateTimeUtc(); - // the max time should actually be 1 minus the hour of the current time, and 59 minutes, not the current time - maxDateTime = maxDateTime.addSecs(-3600); - maxDateTime.setTime( QTime(maxDateTime.time().hour(), 59, 0) ); - - startDateLabel->setText( tr("Earliest PASTCAST Date: %1").arg(UtcToLocal(minDateTime).toString("MM/dd/yyyy")) ); -// endDateLabel->setText( tr("Latest PASTCAST Date: %1").arg(UtcToLocal(maxDateTime).toString("MM/dd/yyyy")) ); - endDateLabel->setText( tr("") ); - - startTime->setDateTime( QDateTime::currentDateTimeUtc() ); - startTime->setTime( QTime(startTime->time().hour(), 0, 0) ); // clean up the time a bit, drop all the min and seconds - startTime->setDateTime( startTime->dateTime().addSecs(-3600) ); // start at the max time, which is 1 minus the hours of the current time, not the current time - startTime->setDateTime( UtcToLocal(startTime->dateTime()) ); // displayed times need to be local times, NOT utc times, so this is stored as a local time, not a UTC time - startTime->setToolTip(tr("Minimum allowed time: %1").arg(UtcToLocal(minDateTime).toString("MM/dd/yyyy HH:00"))); - - stopTime->setDateTime( QDateTime::currentDateTimeUtc() ); - stopTime->setTime( QTime(stopTime->time().hour(), 0, 0) ); // clean up the time a bit, drop all the min and seconds - stopTime->setDateTime( stopTime->dateTime().addSecs(-3600) ); // start at the max time, which is 1 minus the hours of the current time, not the current time - stopTime->setDateTime( UtcToLocal(stopTime->dateTime()) ); // displayed times need to be local times, NOT utc times, so this is stored as a local time, not a UTC time -} - -QDateTime weatherModel::LocalToUtc(QDateTime qat) -{ - boost::local_time::time_zone_ptr timeZone; - timeZone = globalTimeZoneDB.time_zone_from_region(tzString.toStdString()); - - // use the local time constructor, from a date and duration instead of from a ptime (which is a UTC time constructor) - blt::local_date_time new_blt(boost::local_time::not_a_date_time); - new_blt = boost::local_time::local_date_time( - boost::gregorian::date(qat.date().year(), qat.date().month(), qat.date().day()), - boost::posix_time::time_duration(qat.time().hour(),qat.time().minute(),qat.time().second(),qat.time().msec()), - timeZone, - boost::local_time::local_date_time::NOT_DATE_TIME_ON_ERROR - ); - - QDateTime new_qat = QDateTime( - QDate(new_blt.utc_time().date().year(), new_blt.utc_time().date().month(), new_blt.utc_time().date().day()), - QTime(new_blt.utc_time().time_of_day().hours(), new_blt.utc_time().time_of_day().minutes()), - Qt::UTC - ); - - return new_qat; -} - -QDateTime weatherModel::UtcToLocal(QDateTime qat) -{ - boost::local_time::time_zone_ptr timeZone; - timeZone = globalTimeZoneDB.time_zone_from_region(tzString.toStdString()); - - // use the UTC constructor, from a ptime instead of from a date and duration (which is a local time constructor) - blt::local_date_time new_blt(boost::local_time::not_a_date_time); - new_blt = boost::local_time::local_date_time( - boost::posix_time::ptime( - boost::gregorian::date(qat.date().year(), qat.date().month(), qat.date().day()), - boost::posix_time::time_duration(qat.time().hour(),qat.time().minute(),qat.time().second(),qat.time().msec()) - ), - timeZone - ); - - QDateTime new_qat = QDateTime( - QDate(new_blt.local_time().date().year(), new_blt.local_time().date().month(), new_blt.local_time().date().day()), - QTime(new_blt.local_time().time_of_day().hours(), new_blt.local_time().time_of_day().minutes()), - Qt::UTC - ); - - return new_qat; -} - -void weatherModel::setComboToolTip(int) -{ - QString s = modelComboBox->currentText(); - s = ExpandDescription( s.toLocal8Bit().data() ); - modelComboBox->setToolTip( s ); -} - -void weatherModel::displayArchiveDates(int index) -{ - if (index == modelComboBox->count() - 1) { - startDateLabel->setVisible(true); - endDateLabel->setVisible(true); - startTime->setVisible(true); - stopTime->setVisible(true); - hourSpinBox->setVisible(false); - } - else { - startDateLabel->setVisible(false); - endDateLabel->setVisible(false); - startTime->setVisible(false); - stopTime->setVisible(false); - hourSpinBox->setVisible(true); - } -} - -std::vector weatherModel::timeList() { - std::vector tl; - if(!timeGroupBox->isChecked()) { - return tl; - } - QModelIndexList mi = listView->selectionModel()->selectedIndexes(); - for(int i = 0; i < mi.size(); i++) { - tl.push_back(timelist[mi[i].row()]); - } - return tl; -} - -void weatherModel::clearTimes() { - timelist.clear(); - timeModel->setStringList(QStringList()); -} diff --git a/src/gui/weatherModel.h b/src/gui/weatherModel.h deleted file mode 100644 index 78c25be34..000000000 --- a/src/gui/weatherModel.h +++ /dev/null @@ -1,187 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Client to download weather data - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 WEATHER_MODEL_H -#define WEATHER_MODEL_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifndef Q_MOC_RUN -#include "boost/date_time/local_time/local_time.hpp" -#include "boost/date_time/posix_time/posix_time.hpp" -#endif - -#include "ninjaException.h" -#include "wxModelInitializationFactory.h" -#include "nomads_wx_init.h" -#include "gcp_wx_init.h" - -#include "gdal_util.h" -#include "netcdf.h" -#include "ninja_conv.h" - -static QProgressDialog *pGlobProg; -static void *pCancel; - -static const char *apszWxModelGlossary[] = -{ - "UCAR=University Corporation for Atomosperhic Research", - "NOMADS=NOAA Operational Model Archive and Distribution System", - "NDFD=National Digital Forecast Database", - "NAM=North American Mesoscale", - "RAP=Rapid Refresh", - "GFS=Global Forecast System", - "HIRES=High Resolution", - "NEST=Nested", - "ARW=Advanced Research WRF", - "NMM=Non-hydrostatic Mesoscale model", - NULL -}; - -class weatherModel : public QWidget -{ - Q_OBJECT - - public: - weatherModel(QWidget *parent = 0); - ~weatherModel(); - - QString wxModelFileName; - - QGroupBox *weatherGroupBox; - QGroupBox *downloadGroupBox; - QComboBox *modelComboBox; - QSpinBox *daySpinBox; - QSpinBox *hourSpinBox; - QToolButton *downloadToolButton; - - QLabel *ninjafoamConflictLabel; - QLabel *statusLabel; - QToolButton *refreshToolButton; - - QLabel *forecastListLabel; - - QTreeView *treeView; - QDirModel *model; - - QGroupBox *timeGroupBox; - QStringListModel *timeModel; - QListView *listView; - QToolButton *selectAllTimesButton; - QToolButton *selectNoTimesButton; - QDateTimeEdit *startTime; - QDateTimeEdit *stopTime; - QLabel *startDateLabel; - QLabel *endDateLabel; - - QDir cwd; - QString inputFile; - - QProgressDialog *progressDialog; - QLabel *progressLabel; - - QHBoxLayout *downloadLayout; - QHBoxLayout *treeLayout; - QHBoxLayout *listLayout; - QHBoxLayout *loadLayout; - QVBoxLayout *timeLayout; - QHBoxLayout *selectLayout; - QVBoxLayout *weatherLayout; - QVBoxLayout *layout; - - QString tzString; - - std::vector timeList(); - - QDateTime minDateTime; - QDateTime maxDateTime; - - private: - void loadModelComboBox(); - - const char * ExpandDescription( const char *pszReadable ); - - void updatePastcastTimesAndLabels(); - - QDateTime LocalToUtc(QDateTime qat); - QDateTime UtcToLocal(QDateTime qat); - - ncepNamSurfInitialization nam; - ncepNdfdInitialization ndfd; - ncepRapSurfInitialization rap; - ncepNamAlaskaSurfInitialization namAk; - ncepGfsSurfInitialization gfs; - GCPWxModel archhrr; - -#ifdef WITH_NOMADS_SUPPORT - int nNomadsCount; - NomadsWxModel **papoNomads; -#endif - - std::vector timelist; - - private slots: - void getData(); - void displayForecastTime(const QModelIndex &index); - void setInputFile(QString newFile); - void unselectForecast( bool checked ); - void setTimeLimits( int index ); - void setComboToolTip( int index ); - void displayArchiveDates( int index ); - void clearTimes(); - public slots: - void checkForModelData(); - void updateTz( QString tz ); -}; - -#endif /* WEATHER_MODEL_H */ diff --git a/src/gui/windInput.cpp b/src/gui/windInput.cpp deleted file mode 100644 index ee0395377..000000000 --- a/src/gui/windInput.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Handles options for wind inputs - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "windInput.h" - -windInput::windInput(QWidget *parent) : QWidget(parent) -{ - windGroupBox = new QGroupBox( "Domain Average Wind" ); - windGroupBox->setCheckable( true ); - windGroupBox->setChecked(true); - metaWind = new metaWindWidget; - - windTable = new windInputTable; - - scrollArea = new QScrollArea; - scrollArea->setWidget(windTable); - - clearButton = new QToolButton(this); - clearButton->setIcon(QIcon(":cancel.png")); - clearButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); - clearButton->setText("Clear"); - clearButton->setToolTip("Clear all entries"); - - windLayout = new QVBoxLayout; - clearLayout = new QHBoxLayout; - mainLayout = new QVBoxLayout; - - clearLayout->addStretch(); - clearLayout->addWidget(clearButton); - - windLayout->addWidget(metaWind); - windLayout->addLayout(clearLayout); - windLayout->addWidget(scrollArea); - - windGroupBox->setLayout( windLayout ); - - mainLayout->addWidget( windGroupBox ); - setLayout(mainLayout); - - connect(clearButton, SIGNAL(clicked()), windTable, SLOT(clear())); -} diff --git a/src/gui/windInputTable.cpp b/src/gui/windInputTable.cpp deleted file mode 100644 index 74ffde5bb..000000000 --- a/src/gui/windInputTable.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Wind input table for specifying wind speed and direction, as well - * as the diurnal specifics. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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. - * - *****************************************************************************/ - -#include "windInputTable.h" - -windInputTable::windInputTable(QWidget *parent) : QWidget(parent) -{ - nRuns = 64; - diurnalChecked = false; - stabilityChecked = false; - speedLabel = new QLabel(tr("Speed")); - dirLabel = new QLabel(tr("Direction")); - - inputSpeedUnits = new QComboBox; - inputSpeedUnits->addItem(tr("mph"), 0); - inputSpeedUnits->addItem(tr("m/s"), 1); - inputSpeedUnits->addItem(tr("kph"), 2); - inputSpeedUnits->addItem(tr("kts"), 3); - inputSpeedUnits->setEditable(false); - inputSpeedUnits->setCurrentIndex(0); - - speed = new QDoubleSpinBox*[nRuns]; - dir = new QSpinBox*[nRuns]; - - baseLayout = new QGridLayout; - baseLayout->addWidget(speedLabel, 0, 0); - baseLayout->addWidget(dirLabel, 0, 1); - baseLayout->addWidget(inputSpeedUnits, 1, 0); - - setMinimumHeight(480); - - //set up speed and direction input - - for(int i = 0;i < nRuns;i++) - { - speed[i] = new QDoubleSpinBox; - speed[i]->setRange(0.0, 500.0); - speed[i]->setValue(0); - - dir[i] = new QSpinBox; - dir[i]->setRange(0, 360); - speed[i]->setValue(0); - - baseLayout->addWidget(speed[i], i + 2, 0); - baseLayout->addWidget(dir[i], i + 2, 1); - } - - //set up diurnal - timeLabel = new QLabel(tr("Time")); - dateLabel = new QLabel(tr("Date")); - cloudLabel = new QLabel(tr("Cloud Cover(%)")); - airTempLabel = new QLabel(tr("Air Temp."));; - - airTempUnits = new QComboBox; - airTempUnits->addItem(tr("F")); - airTempUnits->addItem(tr("C")); - airTempUnits->setEditable(false); - airTempUnits->setCurrentIndex(0); - - diurnalLayout = new QGridLayout; - diurnalLayout->addWidget(timeLabel, 0, 0); - diurnalLayout->addWidget(dateLabel, 0, 1); - diurnalLayout->addWidget(cloudLabel, 0, 2); - diurnalLayout->addWidget(airTempLabel, 0, 3); - - diurnalLayout->addWidget(airTempUnits, 1, 3); - - time = new QTimeEdit* [nRuns]; - date = new QDateEdit* [nRuns]; - cloudCover = new QSpinBox *[nRuns]; - airTemp = new QSpinBox *[nRuns]; - - for(int i = 0;i < nRuns;i++) - { - time[i] = new QTimeEdit(QTime::currentTime()); - time[i]->setDisplayFormat("HH:mm"); - date[i] = new QDateEdit(QDate::currentDate()); - date[i]->setCalendarPopup(true); - cloudCover[i] = new QSpinBox; - cloudCover[i]->setRange(0, 100); - airTemp[i] = new QSpinBox; - airTemp[i]->setRange(-40, 200); - airTemp[i]->setValue(72); - - diurnalLayout->addWidget(time[i], i + 4, 0); - diurnalLayout->addWidget(date[i], i + 4, 1); - diurnalLayout->addWidget(cloudCover[i], i + 4, 2); - diurnalLayout->addWidget(airTemp[i], i + 4, 3); - } - - enableDiurnalCells(false); - enableStabilityCells(false); - - mainLayout = new QHBoxLayout; - mainLayout->addLayout(baseLayout); - mainLayout->addLayout(diurnalLayout); - - setLayout(mainLayout); -} - -void windInputTable::enableStabilityCells(bool checked) -{ - stabilityChecked = checked; - this->windInputUpdate(); -} - - -void windInputTable::enableDiurnalCells(bool checked) -{ - diurnalChecked = checked; - this->windInputUpdate(); -} - -void windInputTable::windInputUpdate() -{ - if(this->stabilityChecked == true && this->diurnalChecked == false) - { - airTempUnits->setEnabled(false); - for(int i = 0;i < nRuns;i++) - { - time[i]->setEnabled(true); - date[i]->setEnabled(true); - cloudCover[i]->setEnabled(true); - airTemp[i]->setEnabled(false); - } - } - else if(this->stabilityChecked == false && this->diurnalChecked == true) - { - airTempUnits->setEnabled(true); - for(int i = 0;i < nRuns;i++) - { - time[i]->setEnabled(true); - date[i]->setEnabled(true); - cloudCover[i]->setEnabled(true); - airTemp[i]->setEnabled(true); - } - } - else if(this->stabilityChecked == true && this->diurnalChecked == true) - { - airTempUnits->setEnabled(true); - for(int i = 0;i < nRuns;i++) - { - time[i]->setEnabled(true); - date[i]->setEnabled(true); - cloudCover[i]->setEnabled(true); - airTemp[i]->setEnabled(true); - } - } - else if(this->stabilityChecked == false && this->diurnalChecked == false) - { - airTempUnits->setEnabled(false); - for(int i = 0;i < nRuns;i++) - { - time[i]->setEnabled(false); - date[i]->setEnabled(false); - cloudCover[i]->setEnabled(false); - airTemp[i]->setEnabled(false); - } - } -} - -void windInputTable::clear() { - for (int i = 0; i < nRuns; i++) { - speed[i]->setValue(0.0); - dir[i]->setValue(0.0); - cloudCover[i]->setValue(0); - airTemp[i]->setValue(72); - } -} diff --git a/src/gui/windInputTable.h b/src/gui/windInputTable.h deleted file mode 100644 index 9bf68295a..000000000 --- a/src/gui/windInputTable.h +++ /dev/null @@ -1,91 +0,0 @@ -/****************************************************************************** - * - * $Id$ - * - * Project: WindNinja Qt GUI - * Purpose: Wind input table for specifying wind speed and direction, as well - * as the diurnal specifics. - * Author: Kyle Shannon - * - ****************************************************************************** - * - * 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 WINDINPUTTABLE_H -#define WINDINPUTTABLE_H - -#include - - -#include -#include -#include -#include - -#include -#include - -#include -#include - -class windInputTable : public QWidget -{ - Q_OBJECT - public: - windInputTable(QWidget *parent = 0); - - int nRuns; - - //base inputs - QLabel *speedLabel, *dirLabel; - - QComboBox *inputSpeedUnits; - - QDoubleSpinBox **speed; - QSpinBox **dir; - - QGridLayout *baseLayout; - - //diurnal input - QLabel *timeLabel, *dateLabel, *cloudLabel, *airTempLabel; - - QComboBox *airTempUnits; - - QTimeEdit **time; - QDateEdit **date; - QSpinBox **cloudCover, **airTemp; - - public slots: - void enableDiurnalCells(bool checked); - void enableStabilityCells(bool checked); - void clear(); - - public: - QGridLayout *diurnalLayout; - - QHBoxLayout *mainLayout; - -private: - bool diurnalChecked; - bool stabilityChecked; - void windInputUpdate(); -}; - -#endif /* WINDINPUTTABLE_H */ diff --git a/src/hrrr_to_kmz/CMakeLists.txt b/src/hrrr_to_kmz/CMakeLists.txt index a3cf6057f..87b777269 100644 --- a/src/hrrr_to_kmz/CMakeLists.txt +++ b/src/hrrr_to_kmz/CMakeLists.txt @@ -15,7 +15,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.16) include_directories(${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/ninja diff --git a/src/ninja/CMakeLists.txt b/src/ninja/CMakeLists.txt index 0a5eef9b8..333998b5b 100644 --- a/src/ninja/CMakeLists.txt +++ b/src/ninja/CMakeLists.txt @@ -15,12 +15,14 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -#cmake_minimum_required(VERSION 2.6) + +#cmake_minimum_required(VERSION 3.16) if (APPLE) set(CMAKE_MACOSX_RPATH OFF) endif (APPLE) + set(NINJA_INCLUDES ${NETCDF_INCLUDES} ${GDAL_SYSTEM_INCLUDE} ${GDAL_INCLUDE_DIR} ${CURL_INCLUDE_DIRS} @@ -31,9 +33,9 @@ set(NINJA_SOURCES air.cpp ascii_grid.cpp Array2D.cpp Aspect.cpp + callbackFunctions.h cellDiurnal.cpp cli.cpp - dbfopen.cpp domainAverageInitialization.cpp dust.cpp EasyBMP.cpp @@ -83,7 +85,6 @@ set(NINJA_SOURCES air.cpp relief_fetch.cpp Shade.cpp ShapeVector.cpp - shpopen.cpp Slope.cpp solar.cpp solpos.cpp @@ -106,7 +107,11 @@ set(NINJA_SOURCES air.cpp wxModelInitializationFactory.cpp wxStation.cpp windninja.cpp - gcp_wx_init.cpp) + gcp_wx_init.cpp + ninjaTools.h + ninjaTools.cpp + windninja.h + ninjaArmy.h) if(NINJAFOAM) set(NINJA_SOURCES ${NINJA_SOURCES} ninjafoam.cpp @@ -132,31 +137,41 @@ set(LINK_LIBS ${NETCDF_LIBRARIES_C} ${Boost_LIBRARIES}) -if(NINJA_QTGUI) - include_directories(${NINJA_INCLUDES} ${QT_INCLUDES}) - QT4_WRAP_CPP(NINJA_LIB_INCLUDES_MOC ninjaCom.h) - set(NINJA_SOURCES ${NINJA_SOURCES} ${NINJA_LIB_INCLUDES_MOC}) - set(LINK_LIBS ${LINK_LIBS} ${QT_LIBRARIES}) -else(NINJA_QTGUI) - include_directories(${NINJA_INCLUDES}) -endif(NINJA_QTGUI) +include_directories(${NINJA_INCLUDES}) if(WIN32) add_library(ninja STATIC ${NINJA_SOURCES}) add_library(WindNinjadll SHARED ${NINJA_SOURCES}) + target_link_libraries(WindNinjadll PRIVATE shapelib::shp GDAL::GDAL ${LINK_LIBS}) + target_compile_definitions(WindNinjadll + PUBLIC + WIN32_LEAN_AND_MEAN + ) target_link_libraries(WindNinjadll ${LINK_LIBS} $<$:OpenMP::OpenMP_CXX>) else() if(APPLE) add_library(ninja STATIC ${NINJA_SOURCES}) - else() - add_library(ninja SHARED ${NINJA_SOURCES}) # Linux + else() # Linux + add_library(ninja SHARED ${NINJA_SOURCES}) endif() endif() target_link_libraries(ninja ${LINK_LIBS} $<$:OpenMP::OpenMP_CXX>) + +if(MSVC) + target_compile_definitions(ninja + PUBLIC + WIN32_LEAN_AND_MEAN + ) +endif() + +target_include_directories(ninja PUBLIC $ $) + + install(TARGETS ninja DESTINATION lib COMPONENT libs) if(WIN32) install(TARGETS WindNinjadll DESTINATION ${lib_dest}) endif(WIN32) -install(FILES WindNinjaInputs.h ninja.h windninja.h ninja_errors.h DESTINATION include COMPONENT includes) +install(FILES WindNinjaInputs.h ninja.h gdal_util.h callbackFunctions.h windninja.h ninja_errors.h DESTINATION include COMPONENT includes) + diff --git a/src/ninja/KmlVector.cpp b/src/ninja/KmlVector.cpp index b7a95cd04..ecd2a776e 100644 --- a/src/ninja/KmlVector.cpp +++ b/src/ninja/KmlVector.cpp @@ -72,7 +72,7 @@ KmlVector::~KmlVector() splitValue = NULL; } - OCTDestroyCoordinateTransformation( coordTransform ); + OCTDestroyCoordinateTransformation( (OGRCoordinateTransformationH)coordTransform ); } void KmlVector::setSpeedGrid(AsciiGrid &s, velocityUnits::eVelocityUnits units) diff --git a/src/ninja/OutputWriter.cpp b/src/ninja/OutputWriter.cpp index 20dabbf3b..90b64c074 100644 --- a/src/ninja/OutputWriter.cpp +++ b/src/ninja/OutputWriter.cpp @@ -297,7 +297,7 @@ void OutputWriter::_closeOGRFile() { if( NULL != hDataSource ) { - OGR_DS_Destroy( hDataSource ); + GDALClose( hDataSource ); hDataSource = NULL; } } diff --git a/src/ninja/WindNinjaInputs.cpp b/src/ninja/WindNinjaInputs.cpp index dd3e7c626..5705ecdf6 100644 --- a/src/ninja/WindNinjaInputs.cpp +++ b/src/ninja/WindNinjaInputs.cpp @@ -33,6 +33,8 @@ WindNinjaInputs::WindNinjaInputs() : ninjaTime(boost::local_time::not_a_date_time) { //Initialize variables + Com = new ninjaComClass(); + inputsRunNumber = -9999; hSpdMemDs = NULL; hDirMemDs = NULL; hDustMemDs = NULL; @@ -176,6 +178,7 @@ WindNinjaInputs::WindNinjaInputs() WindNinjaInputs::~WindNinjaInputs() { + delete Com; } /** @@ -185,7 +188,179 @@ WindNinjaInputs::~WindNinjaInputs() WindNinjaInputs::WindNinjaInputs(const WindNinjaInputs &rhs) : ninjaTime(boost::local_time::not_a_date_time) { - *this = rhs; + Com = new ninjaComClass(*rhs.Com); + + inputsRunNumber = rhs.inputsRunNumber; + + armySize = rhs.armySize; + hSpdMemDs = rhs.hSpdMemDs; + hDirMemDs = rhs.hDirMemDs; + hDustMemDs = rhs.hDustMemDs; + + vegetation = rhs.vegetation; + + speedInitGridFilename = rhs.speedInitGridFilename; + dirInitGridFilename = rhs.dirInitGridFilename; + initializationMethod = rhs.initializationMethod; + forecastFilename = rhs.forecastFilename; + inputSpeedUnits = rhs.inputSpeedUnits; + outputSpeedUnits = rhs.outputSpeedUnits; + inputSpeed = rhs.inputSpeed; + inputDirection = rhs.inputDirection; + inputWindHeightUnits = rhs.inputWindHeightUnits; + inputWindHeight = rhs.inputWindHeight; + outputWindHeightUnits = rhs.outputWindHeightUnits; + outputWindHeight = rhs.outputWindHeight; + + stations = rhs.stations; + realStations = rhs.realStations; + wxStationFilename = rhs.wxStationFilename; + stationsScratch = rhs.stationsScratch; + stationsOldInput = rhs.stationsOldInput; + stationsOldOutput = rhs.stationsOldOutput; + stationFetch=rhs.stationFetch; + matchWxStations = rhs.matchWxStations; + outer_relax = rhs.outer_relax; + CPLDebug("NINJA", "Setting NINJA_POINT_MATCH_OUT_RELAX to %lf", outer_relax); + diurnalWinds = rhs.diurnalWinds; +#ifdef EMISSIONS + dustFlag = rhs.dustFlag; + dustFilename = rhs.dustFilename; + dustFileOut = rhs.dustFileOut; + geotiffOutFlag = rhs.geotiffOutFlag; + geotiffOutFilename = rhs.geotiffOutFilename; + dustFile = rhs.dustFile; + ustarFile = rhs.ustarFile; +#endif +#ifdef NINJAFOAM + nIterations = rhs.nIterations; + meshCount = rhs.meshCount; + ninjafoamMeshChoice = rhs.ninjafoamMeshChoice; + existingCaseDirectory = rhs.existingCaseDirectory; + stlFile = rhs.stlFile; + foamVelocityGrid = rhs.foamVelocityGrid; + foamAngleGrid = rhs.foamAngleGrid; + writeTurbulence = rhs.writeTurbulence; + colMax_colHeightAGL = rhs.colMax_colHeightAGL; + colMax_colHeightAGL_units = rhs.colMax_colHeightAGL_units; +#endif + outputPointsFilename = rhs.outputPointsFilename; + inputPointsFilename = rhs.inputPointsFilename; + pointsNamesList = rhs.pointsNamesList; + latList = rhs.latList; + lonList = rhs.lonList; + heightList = rhs.heightList; + projYList = rhs.projYList; + projXList = rhs.projXList; + + ninjaTime = rhs.ninjaTime; + if(rhs.ninjaTimeZone.get() == NULL) //If pointer is NULL + ninjaTimeZone.reset(); + else if(rhs.ninjaTimeZone->to_posix_string().empty()) //If pointer is good, but posix string is empty + ninjaTimeZone.reset(); + else //Else it is a good time zone pointer, so set accordingly + ninjaTimeZone = boost::local_time::time_zone_ptr (new boost::local_time::posix_time_zone(rhs.ninjaTimeZone->to_posix_string())); + + airTempUnits = rhs.airTempUnits; + airTemp = rhs.airTemp; + cloudCoverUnits = rhs.cloudCoverUnits; + cloudCover = rhs.cloudCover; + latitude = rhs.latitude; + longitude = rhs.longitude; + numberCPUs = rhs.numberCPUs; + outputBufferClipping = rhs.outputBufferClipping; + writeAtmFile = rhs.writeAtmFile; + googOutFlag = rhs.googOutFlag; + googSpeedScaling = rhs.googSpeedScaling; + googLineWidth = rhs.googLineWidth; + googColor = rhs.googColor; + googVectorScale = rhs.googVectorScale; + googUseConsistentColorScale = rhs.googUseConsistentColorScale; + wxModelGoogOutFlag = rhs.wxModelGoogOutFlag; + wxModelGoogSpeedScaling = rhs.wxModelGoogSpeedScaling; + wxModelGoogLineWidth = rhs.wxModelGoogLineWidth; + shpOutFlag = rhs.shpOutFlag; + + asciiOutFlag = rhs.asciiOutFlag; + asciiAaigridOutFlag = rhs.asciiAaigridOutFlag; + asciiJsonOutFlag = rhs.asciiJsonOutFlag; + asciiUtmOutFlag = rhs.asciiUtmOutFlag; + ascii4326OutFlag = rhs.ascii4326OutFlag; + asciiUvOutFlag = rhs.asciiUvOutFlag; + + wxModelShpOutFlag = rhs.wxModelShpOutFlag; + wxModelAsciiOutFlag = rhs.wxModelAsciiOutFlag; + txtOutFlag = rhs.txtOutFlag; + volVTKOutFlag = rhs.volVTKOutFlag; + kmlFile = rhs.kmlFile; + kmzFile = rhs.kmzFile; + wxModelKmlFile = rhs.wxModelKmlFile; + wxModelKmzFile = rhs.wxModelKmzFile; + kmzResolution = rhs.kmzResolution; + kmzUnits = rhs.kmzUnits; + shpFile = rhs.shpFile; + dbfFile = rhs.dbfFile; + wxModelShpFile = rhs.wxModelShpFile; + wxModelDbfFile = rhs.wxModelDbfFile; + shpResolution = rhs.shpResolution; + shpUnits = rhs.shpUnits; + pdfOutFlag = rhs.pdfOutFlag; + pdfDEMFileName = rhs.pdfDEMFileName; + pdfResolution = rhs.pdfResolution; + pdfLineWidth = rhs.pdfLineWidth; + pdfUnits = rhs.pdfUnits; + pdfFile = rhs.pdfFile; + pdfBaseType = rhs.pdfBaseType; + pdfWidth = rhs.pdfWidth; + pdfHeight = rhs.pdfHeight; + pdfDPI = rhs.pdfDPI; + cldFile = rhs.cldFile; + velFile = rhs.velFile; + wxModelCldFile = rhs.wxModelCldFile; + wxModelVelFile = rhs.wxModelVelFile; + velResolution = rhs.velResolution; + velOutputFileDistanceUnits = rhs.velOutputFileDistanceUnits; + angFile = rhs.angFile; + atmFile = rhs.atmFile; + wxModelAngFile = rhs.wxModelAngFile; + angResolution = rhs.angResolution; + angOutputFileDistanceUnits = rhs.angOutputFileDistanceUnits; + legFile = rhs.legFile; + dateTimeLegFile = rhs.dateTimeLegFile; + wxModelLegFile = rhs.wxModelLegFile; + dateTimewxModelLegFile = rhs.dateTimewxModelLegFile; + volVTKFile = rhs.volVTKFile; + keepOutGridsInMemory = rhs.keepOutGridsInMemory; + customOutputPath = rhs.customOutputPath; + +#ifdef NINJA_SPEED_TESTING + speedDampeningRatio = rhs.speedDampeningRatio; +#endif + + downDragCoeff = rhs.downDragCoeff; + downEntrainmentCoeff = rhs.downEntrainmentCoeff; + upDragCoeff = rhs.upDragCoeff; + upEntrainmentCoeff = rhs.upEntrainmentCoeff; + + stabilityFlag = rhs.stabilityFlag; + alphaStability = rhs.alphaStability; +#ifdef FRICTION_VELOCITY + frictionVelocityFlag = rhs.frictionVelocityFlag; + frictionVelocityCalculationMethod = rhs.frictionVelocityCalculationMethod; + ustarFile = rhs.ustarFile; +#endif + + outputPath = rhs.outputPath; + + //class crap + dem = rhs.dem; + surface = rhs.surface; + +#ifdef _OPENMP + omp_set_nested(false); + omp_set_dynamic(false); +#endif //_OPENMP + } /** @@ -196,7 +371,7 @@ WindNinjaInputs::WindNinjaInputs(const WindNinjaInputs &rhs) * @param rhs WindNinjaInputs object to compare with. * @return true if objects are equal, otherwise false. */ -bool WindNinjaInputs::operator==(const WindNinjaInputs &rhs) +bool WindNinjaInputs::operator==(const WindNinjaInputs &rhs) const { if( inputSpeed == rhs.inputSpeed && inputSpeedUnits == rhs.inputSpeedUnits && @@ -225,15 +400,21 @@ WindNinjaInputs &WindNinjaInputs::operator=(const WindNinjaInputs &rhs) { if(&rhs != this) { - //ninjaCom stuff - Com = NULL; //must be set to null! + delete Com; + Com = new ninjaComClass(); + *Com = *rhs.Com; + + inputsRunNumber = rhs.inputsRunNumber; + armySize = rhs.armySize; hSpdMemDs = rhs.hSpdMemDs; hDirMemDs = rhs.hDirMemDs; hDustMemDs = rhs.hDustMemDs; - + vegetation = rhs.vegetation; + speedInitGridFilename = rhs.speedInitGridFilename; + dirInitGridFilename = rhs.dirInitGridFilename; initializationMethod = rhs.initializationMethod; forecastFilename = rhs.forecastFilename; inputSpeedUnits = rhs.inputSpeedUnits; @@ -245,9 +426,9 @@ WindNinjaInputs &WindNinjaInputs::operator=(const WindNinjaInputs &rhs) inputWindHeight = rhs.inputWindHeight; outputWindHeightUnits = rhs.outputWindHeightUnits; outputWindHeight = rhs.outputWindHeight; - - + stations = rhs.stations; + realStations = rhs.realStations; wxStationFilename = rhs.wxStationFilename; stationsScratch = rhs.stationsScratch; stationsOldInput = rhs.stationsOldInput; @@ -262,6 +443,7 @@ WindNinjaInputs &WindNinjaInputs::operator=(const WindNinjaInputs &rhs) dustFilename = rhs.dustFilename; dustFileOut = rhs.dustFileOut; geotiffOutFlag = rhs.geotiffOutFlag; + geotiffOutFilename = rhs.geotiffOutFilename; dustFile = rhs.dustFile; ustarFile = rhs.ustarFile; #endif @@ -269,6 +451,7 @@ WindNinjaInputs &WindNinjaInputs::operator=(const WindNinjaInputs &rhs) nIterations = rhs.nIterations; meshCount = rhs.meshCount; ninjafoamMeshChoice = rhs.ninjafoamMeshChoice; + existingCaseDirectory = rhs.existingCaseDirectory; stlFile = rhs.stlFile; foamVelocityGrid = rhs.foamVelocityGrid; foamAngleGrid = rhs.foamAngleGrid; @@ -282,6 +465,8 @@ WindNinjaInputs &WindNinjaInputs::operator=(const WindNinjaInputs &rhs) latList = rhs.latList; lonList = rhs.lonList; heightList = rhs.heightList; + projYList = rhs.projYList; + projXList = rhs.projXList; ninjaTime = rhs.ninjaTime; if(rhs.ninjaTimeZone.get() == NULL) //If pointer is NULL @@ -291,7 +476,9 @@ WindNinjaInputs &WindNinjaInputs::operator=(const WindNinjaInputs &rhs) else //Else it is a good time zone pointer, so set accordingly ninjaTimeZone = boost::local_time::time_zone_ptr (new boost::local_time::posix_time_zone(rhs.ninjaTimeZone->to_posix_string())); + airTempUnits = rhs.airTempUnits; airTemp = rhs.airTemp; + cloudCoverUnits = rhs.cloudCoverUnits; cloudCover = rhs.cloudCover; latitude = rhs.latitude; longitude = rhs.longitude; @@ -315,7 +502,7 @@ WindNinjaInputs &WindNinjaInputs::operator=(const WindNinjaInputs &rhs) asciiProjOutFlag = rhs.asciiProjOutFlag; asciiGeogOutFlag = rhs.asciiGeogOutFlag; asciiUvOutFlag = rhs.asciiUvOutFlag; - + wxModelShpOutFlag = rhs.wxModelShpOutFlag; wxModelAsciiOutFlag = rhs.wxModelAsciiOutFlag; txtOutFlag = rhs.txtOutFlag; @@ -355,23 +542,25 @@ WindNinjaInputs &WindNinjaInputs::operator=(const WindNinjaInputs &rhs) angOutputFileDistanceUnits = rhs.angOutputFileDistanceUnits; legFile = rhs.legFile; dateTimeLegFile = rhs.dateTimeLegFile; + wxModelLegFile = rhs.wxModelLegFile; + dateTimewxModelLegFile = rhs.dateTimewxModelLegFile; volVTKFile = rhs.volVTKFile; keepOutGridsInMemory = rhs.keepOutGridsInMemory; customOutputPath = rhs.customOutputPath; - + #ifdef NINJA_SPEED_TESTING speedDampeningRatio = rhs.speedDampeningRatio; #endif downDragCoeff = rhs.downDragCoeff; - downEntrainmentCoeff = rhs.downDragCoeff; + downEntrainmentCoeff = rhs.downEntrainmentCoeff; upDragCoeff = rhs.upDragCoeff; upEntrainmentCoeff = rhs.upEntrainmentCoeff; - + stabilityFlag = rhs.stabilityFlag; alphaStability = rhs.alphaStability; #ifdef FRICTION_VELOCITY - frictionVelocityFlag = rhs.frictionVelocityFlag; + frictionVelocityFlag = rhs.frictionVelocityFlag; frictionVelocityCalculationMethod = rhs.frictionVelocityCalculationMethod; ustarFile = rhs.ustarFile; #endif diff --git a/src/ninja/WindNinjaInputs.h b/src/ninja/WindNinjaInputs.h index 1816f67aa..51a92cec3 100644 --- a/src/ninja/WindNinjaInputs.h +++ b/src/ninja/WindNinjaInputs.h @@ -54,7 +54,7 @@ struct WindNinjaInputs WindNinjaInputs(const WindNinjaInputs &rhs); WindNinjaInputs &operator=(const WindNinjaInputs &rhs); - bool operator==(const WindNinjaInputs &rhs); + bool operator==(const WindNinjaInputs &rhs) const; /*----------------------------------------------------------------------------- @@ -106,13 +106,10 @@ struct WindNinjaInputs * Base input data passed to "simulate_wind()" *-----------------------------------------------------------------------------*/ - - ninjaComClass *Com; //pointer to a com handler for the specific communication type desired - char lastComString[NINJA_MSG_SIZE]; + ninjaComClass *Com; // pointer to a given ninjas[i] level com handler int inputsRunNumber; - ninjaComClass::eNinjaCom inputsComType; - - int armySize; + + int armySize; GDALDatasetH hSpdMemDs; GDALDatasetH hDirMemDs; GDALDatasetH hDustMemDs; diff --git a/src/ninja/ascii_grid.cpp b/src/ninja/ascii_grid.cpp index 5cedcf246..dd4f8d6b1 100644 --- a/src/ninja/ascii_grid.cpp +++ b/src/ninja/ascii_grid.cpp @@ -26,7 +26,6 @@ template<> inline int epsClr() { return 1; } template<> inline short epsClr() { return 1; } - /** * @brief Create an empty grid * Create an empty grid with no header data and no data diff --git a/src/ninja/callbackFunctions.h b/src/ninja/callbackFunctions.h new file mode 100644 index 000000000..c89606275 --- /dev/null +++ b/src/ninja/callbackFunctions.h @@ -0,0 +1,6 @@ +#ifndef CALLBACKFUNCTIONS_H +#define CALLBACKFUNCTIONS_H + +typedef void (*ninjaComMessageHandler)(const char *pszMessage, void *pUser); + +#endif // CALLBACKFUNCTIONS diff --git a/src/ninja/cli.cpp b/src/ninja/cli.cpp index 9446c74bd..0e4e67b92 100644 --- a/src/ninja/cli.cpp +++ b/src/ninja/cli.cpp @@ -648,7 +648,7 @@ int windNinjaCLI(int argc, char* argv[]) OGRPointToLatLon(bbox[1], bbox[0], hDS, "WGS84"); OGRPointToLatLon(bbox[3], bbox[2], hDS, "WGS84"); - OGR_DS_Destroy(hDS); + GDALClose(hDS); //add a buffer bbox[0] += 0.009; //north @@ -1450,21 +1450,10 @@ int windNinjaCLI(int argc, char* argv[]) } //STATION_FETCH - /* - windsim.Com = new ninjaCLIComHandler(); - int r = -1; - windsim.Com->runNumber = &r; - char msg[1024]; - windsim.Com->lastMsg = msg; - */ - //For loop over all ninjas (just 1 ninja unless it's a weather model run)-------------------- for(int i_ = 0; i_ < windsim.getSize(); i_++) { - //Set ninja communication---------------------------------------------------------- - windsim.setNinjaCommunication(i_, i_, ninjaComClass::ninjaCLICom ); - windsim.setNumberCPUs( i_, vm["num_threads"].as() ); //windsim.ninjas[i_].readInputFile(*elevation_file); diff --git a/src/ninja/dbfopen.cpp b/src/ninja/dbfopen.cpp deleted file mode 100644 index 453f2bb19..000000000 --- a/src/ninja/dbfopen.cpp +++ /dev/null @@ -1,1495 +0,0 @@ -/****************************************************************************** - * $Id$ - * - * Project: Shapelib - * Purpose: Implementation of .dbf access API documented in dbf_api.html. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 1999, Frank Warmerdam - * - * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This - * option is discussed in more detail in shapelib.html. - * - * -- - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * 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. - ****************************************************************************** - * - * $Log: dbfopen.c,v $ - * Revision 1.48 2003/03/10 14:51:27 warmerda - * DBFWrite* calls now return FALSE if they have to truncate - * - * Revision 1.47 2002/11/20 03:32:22 warmerda - * Ensure field name in DBFGetFieldIndex() is properly terminated. - * - * Revision 1.46 2002/10/09 13:10:21 warmerda - * Added check that width is positive. - * - * Revision 1.45 2002/09/29 00:00:08 warmerda - * added FTLogical and logical attribute read/write calls - * - * Revision 1.44 2002/05/07 13:46:11 warmerda - * Added DBFWriteAttributeDirectly(). - * - * Revision 1.43 2002/02/13 19:39:21 warmerda - * Fix casting issues in DBFCloneEmpty(). - * - * Revision 1.42 2002/01/15 14:36:07 warmerda - * updated email address - * - * Revision 1.41 2002/01/15 14:31:49 warmerda - * compute rather than copying nHeaderLength in DBFCloneEmpty() - * - * Revision 1.40 2002/01/09 04:32:35 warmerda - * fixed to read correct amount of header - * - * Revision 1.39 2001/12/11 22:41:03 warmerda - * improve io related error checking when reading header - * - * Revision 1.38 2001/11/28 16:07:31 warmerda - * Cleanup to avoid compiler warnings as suggested by Richard Hash. - * - * Revision 1.37 2001/07/04 05:18:09 warmerda - * do last fix properly - * - * Revision 1.36 2001/07/04 05:16:09 warmerda - * fixed fieldname comparison in DBFGetFieldIndex - * - * Revision 1.35 2001/06/22 02:10:06 warmerda - * fixed NULL shape support with help from Jim Matthews - * - * Revision 1.33 2001/05/31 19:20:13 warmerda - * added DBFGetFieldIndex() - * - * Revision 1.32 2001/05/31 18:15:40 warmerda - * Added support for NULL fields in DBF files - * - * Revision 1.31 2001/05/23 13:36:52 warmerda - * added use of SHPAPI_CALL - * - * Revision 1.30 2000/12/05 14:43:38 warmerda - * DBReadAttribute() white space trimming bug fix - * - * Revision 1.29 2000/10/05 14:36:44 warmerda - * fix bug with writing very wide numeric fields - * - * Revision 1.28 2000/09/25 14:18:07 warmerda - * Added some casts of strlen() return result to fix warnings on some - * systems, as submitted by Daniel. - * - * Revision 1.27 2000/09/25 14:15:51 warmerda - * added DBFGetNativeFieldType() - * - * Revision 1.26 2000/07/07 13:39:45 warmerda - * removed unused variables, and added system include files - * - * Revision 1.25 2000/05/29 18:19:13 warmerda - * avoid use of uchar, and adding casting fix - * - * Revision 1.24 2000/05/23 13:38:27 warmerda - * Added error checks on return results of fread() and fseek(). - * - * Revision 1.23 2000/05/23 13:25:49 warmerda - * Avoid crashing if field or record are out of range in dbfread*attribute(). - * - * Revision 1.22 1999/12/15 13:47:24 warmerda - * Added stdlib.h to ensure that atof() is prototyped. - * - * Revision 1.21 1999/12/13 17:25:46 warmerda - * Added support for upper case .DBF extention. - * - * Revision 1.20 1999/11/30 16:32:11 warmerda - * Use atof() instead of sscanf(). - * - * Revision 1.19 1999/11/05 14:12:04 warmerda - * updated license terms - * - * Revision 1.18 1999/07/27 00:53:28 warmerda - * ensure that whole old field value clear on write of string - * - * Revision 1.1 1999/07/05 18:58:07 warmerda - * New - * - * Revision 1.17 1999/06/11 19:14:12 warmerda - * Fixed some memory leaks. - * - * Revision 1.16 1999/06/11 19:04:11 warmerda - * Remoted some unused variables. - * - * Revision 1.15 1999/05/11 03:19:28 warmerda - * added new Tuple api, and improved extension handling - add from candrsn - * - * Revision 1.14 1999/05/04 15:01:48 warmerda - * Added 'F' support. - * - * Revision 1.13 1999/03/23 17:38:59 warmerda - * DBFAddField() now actually does return the new field number, or -1 if - * it fails. - * - * Revision 1.12 1999/03/06 02:54:46 warmerda - * Added logic to convert shapefile name to dbf filename in DBFOpen() - * for convenience. - * - * Revision 1.11 1998/12/31 15:30:34 warmerda - * Improved the interchangability of numeric and string attributes. Add - * white space trimming option for attributes. - * - * Revision 1.10 1998/12/03 16:36:44 warmerda - * Use r+b instead of rb+ for binary access. - * - * Revision 1.9 1998/12/03 15:34:23 warmerda - * Updated copyright message. - * - * Revision 1.8 1997/12/04 15:40:15 warmerda - * Added newline character after field definitions. - * - * Revision 1.7 1997/03/06 14:02:10 warmerda - * Ensure bUpdated is initialized. - * - * Revision 1.6 1996/02/12 04:54:41 warmerda - * Ensure that DBFWriteAttribute() returns TRUE if it succeeds. - * - * Revision 1.5 1995/10/21 03:15:12 warmerda - * Changed to use binary file access, and ensure that the - * field name field is zero filled, and limited to 10 chars. - * - * Revision 1.4 1995/08/24 18:10:42 warmerda - * Added use of SfRealloc() to avoid pre-ANSI realloc() functions such - * as on the Sun. - * - * Revision 1.3 1995/08/04 03:15:16 warmerda - * Fixed up header. - * - * Revision 1.2 1995/08/04 03:14:43 warmerda - * Added header. - */ - -static char rcsid[] = - "$Id$"; - -#include "shapefil.h" - -#include -#include -#include -#include - -#ifndef FALSE -# define FALSE 0 -# define TRUE 1 -#endif - -static int nStringFieldLen = 0; -static char * pszStringField = NULL; - -/************************************************************************/ -/* SfRealloc() */ -/* */ -/* A realloc cover function that will access a NULL pointer as */ -/* a valid input. */ -/************************************************************************/ - -static void * SfRealloc( void * pMem, int nNewSize ) - -{ - if( pMem == NULL ) - return( (void *) malloc(nNewSize) ); - else - return( (void *) realloc(pMem,nNewSize) ); -} - -/************************************************************************/ -/* DBFWriteHeader() */ -/* */ -/* This is called to write out the file header, and field */ -/* descriptions before writing any actual data records. This */ -/* also computes all the DBFDataSet field offset/size/decimals */ -/* and so forth values. */ -/************************************************************************/ - -static void DBFWriteHeader(DBFHandle psDBF) - -{ - unsigned char abyHeader[XBASE_FLDHDR_SZ]; - int i; - - if( !psDBF->bNoHeader ) - return; - - psDBF->bNoHeader = FALSE; - -/* -------------------------------------------------------------------- */ -/* Initialize the file header information. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) - abyHeader[i] = 0; - - abyHeader[0] = 0x03; /* memo field? - just copying */ - - /* date updated on close, record count preset at zero */ - - abyHeader[8] = psDBF->nHeaderLength % 256; - abyHeader[9] = psDBF->nHeaderLength / 256; - - abyHeader[10] = psDBF->nRecordLength % 256; - abyHeader[11] = psDBF->nRecordLength / 256; - -/* -------------------------------------------------------------------- */ -/* Write the initial 32 byte file header, and all the field */ -/* descriptions. */ -/* -------------------------------------------------------------------- */ - fseek( psDBF->fp, 0, 0 ); - fwrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp ); - fwrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, psDBF->fp ); - -/* -------------------------------------------------------------------- */ -/* Write out the newline character if there is room for it. */ -/* -------------------------------------------------------------------- */ - if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) - { - char cNewline; - - cNewline = 0x0d; - fwrite( &cNewline, 1, 1, psDBF->fp ); - } -} - -/************************************************************************/ -/* DBFFlushRecord() */ -/* */ -/* Write out the current record if there is one. */ -/************************************************************************/ - -static void DBFFlushRecord( DBFHandle psDBF ) - -{ - int nRecordOffset; - - if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 ) - { - psDBF->bCurrentRecordModified = FALSE; - - nRecordOffset = psDBF->nRecordLength * psDBF->nCurrentRecord - + psDBF->nHeaderLength; - - fseek( psDBF->fp, nRecordOffset, 0 ); - fwrite( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); - } -} - -/************************************************************************/ -/* DBFOpen() */ -/* */ -/* Open a .dbf file. */ -/************************************************************************/ - -DBFHandle SHPAPI_CALL -DBFOpen( const char * pszFilename, const char * pszAccess ) - -{ - DBFHandle psDBF; - unsigned char *pabyBuf; - int nFields, nHeadLen, nRecLen, iField, i; - char *pszBasename, *pszFullname; - -/* -------------------------------------------------------------------- */ -/* We only allow the access strings "rb" and "r+". */ -/* -------------------------------------------------------------------- */ - if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 - && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0 - && strcmp(pszAccess,"r+b") != 0 ) - return( NULL ); - - if( strcmp(pszAccess,"r") == 0 ) - pszAccess = "rb"; - - if( strcmp(pszAccess,"r+") == 0 ) - pszAccess = "rb+"; - -/* -------------------------------------------------------------------- */ -/* Compute the base (layer) name. If there is any extension */ -/* on the passed in filename we will strip it off. */ -/* -------------------------------------------------------------------- */ - pszBasename = (char *) malloc(strlen(pszFilename)+5); - strcpy( pszBasename, pszFilename ); - for( i = strlen(pszBasename)-1; - i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' - && pszBasename[i] != '\\'; - i-- ) {} - - if( pszBasename[i] == '.' ) - pszBasename[i] = '\0'; - - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.dbf", pszBasename ); - - psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) ); - psDBF->fp = fopen( pszFullname, pszAccess ); - - if( psDBF->fp == NULL ) - { - sprintf( pszFullname, "%s.DBF", pszBasename ); - psDBF->fp = fopen(pszFullname, pszAccess ); - } - - free( pszBasename ); - free( pszFullname ); - - if( psDBF->fp == NULL ) - { - free( psDBF ); - return( NULL ); - } - - psDBF->bNoHeader = FALSE; - psDBF->nCurrentRecord = -1; - psDBF->bCurrentRecordModified = FALSE; - -/* -------------------------------------------------------------------- */ -/* Read Table Header info */ -/* -------------------------------------------------------------------- */ - pabyBuf = (unsigned char *) malloc(500); - if( fread( pabyBuf, 32, 1, psDBF->fp ) != 1 ) - { - fclose( psDBF->fp ); - free( pabyBuf ); - free( psDBF ); - return NULL; - } - - psDBF->nRecords = - pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; - - psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; - psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11]*256; - - psDBF->nFields = nFields = (nHeadLen - 32) / 32; - - psDBF->pszCurrentRecord = (char *) malloc(nRecLen); - -/* -------------------------------------------------------------------- */ -/* Read in Field Definitions */ -/* -------------------------------------------------------------------- */ - - pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen); - psDBF->pszHeader = (char *) pabyBuf; - - fseek( psDBF->fp, 32, 0 ); - if( fread( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 ) - { - fclose( psDBF->fp ); - free( pabyBuf ); - free( psDBF ); - return NULL; - } - - psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields); - psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields); - psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields); - psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields); - - for( iField = 0; iField < nFields; iField++ ) - { - unsigned char *pabyFInfo; - - pabyFInfo = pabyBuf+iField*32; - - if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) - { - psDBF->panFieldSize[iField] = pabyFInfo[16]; - psDBF->panFieldDecimals[iField] = pabyFInfo[17]; - } - else - { - psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256; - psDBF->panFieldDecimals[iField] = 0; - } - - psDBF->pachFieldType[iField] = (char) pabyFInfo[11]; - if( iField == 0 ) - psDBF->panFieldOffset[iField] = 1; - else - psDBF->panFieldOffset[iField] = - psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1]; - } - - return( psDBF ); -} - -/************************************************************************/ -/* DBFClose() */ -/************************************************************************/ - -void SHPAPI_CALL -DBFClose(DBFHandle psDBF) -{ -/* -------------------------------------------------------------------- */ -/* Write out header if not already written. */ -/* -------------------------------------------------------------------- */ - if( psDBF->bNoHeader ) - DBFWriteHeader( psDBF ); - - DBFFlushRecord( psDBF ); - -/* -------------------------------------------------------------------- */ -/* Update last access date, and number of records if we have */ -/* write access. */ -/* -------------------------------------------------------------------- */ - if( psDBF->bUpdated ) - { - unsigned char abyFileHeader[32]; - - fseek( psDBF->fp, 0, 0 ); - fread( abyFileHeader, 32, 1, psDBF->fp ); - - abyFileHeader[1] = 95; /* YY */ - abyFileHeader[2] = 7; /* MM */ - abyFileHeader[3] = 26; /* DD */ - - abyFileHeader[4] = psDBF->nRecords % 256; - abyFileHeader[5] = (psDBF->nRecords/256) % 256; - abyFileHeader[6] = (psDBF->nRecords/(256*256)) % 256; - abyFileHeader[7] = (psDBF->nRecords/(256*256*256)) % 256; - - fseek( psDBF->fp, 0, 0 ); - fwrite( abyFileHeader, 32, 1, psDBF->fp ); - } - -/* -------------------------------------------------------------------- */ -/* Close, and free resources. */ -/* -------------------------------------------------------------------- */ - fclose( psDBF->fp ); - - if( psDBF->panFieldOffset != NULL ) - { - free( psDBF->panFieldOffset ); - free( psDBF->panFieldSize ); - free( psDBF->panFieldDecimals ); - free( psDBF->pachFieldType ); - } - - free( psDBF->pszHeader ); - free( psDBF->pszCurrentRecord ); - - free( psDBF ); - - if( pszStringField != NULL ) - { - free( pszStringField ); - pszStringField = NULL; - nStringFieldLen = 0; - } -} - -/************************************************************************/ -/* DBFCreate() */ -/* */ -/* Create a new .dbf file. */ -/************************************************************************/ - -DBFHandle SHPAPI_CALL -DBFCreate( const char * pszFilename ) - -{ - DBFHandle psDBF; - FILE *fp; - char *pszFullname, *pszBasename; - int i; - -/* -------------------------------------------------------------------- */ -/* Compute the base (layer) name. If there is any extension */ -/* on the passed in filename we will strip it off. */ -/* -------------------------------------------------------------------- */ - pszBasename = (char *) malloc(strlen(pszFilename)+5); - strcpy( pszBasename, pszFilename ); - for( i = strlen(pszBasename)-1; - i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' - && pszBasename[i] != '\\'; - i-- ) {} - - if( pszBasename[i] == '.' ) - pszBasename[i] = '\0'; - - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.dbf", pszBasename ); - free( pszBasename ); - -/* -------------------------------------------------------------------- */ -/* Create the file. */ -/* -------------------------------------------------------------------- */ - fp = fopen( pszFullname, "wb" ); - if( fp == NULL ) - return( NULL ); - - fputc( 0, fp ); - fclose( fp ); - - fp = fopen( pszFullname, "rb+" ); - if( fp == NULL ) - return( NULL ); - - free( pszFullname ); - -/* -------------------------------------------------------------------- */ -/* Create the info structure. */ -/* -------------------------------------------------------------------- */ - psDBF = (DBFHandle) malloc(sizeof(DBFInfo)); - - psDBF->fp = fp; - psDBF->nRecords = 0; - psDBF->nFields = 0; - psDBF->nRecordLength = 1; - psDBF->nHeaderLength = 33; - - psDBF->panFieldOffset = NULL; - psDBF->panFieldSize = NULL; - psDBF->panFieldDecimals = NULL; - psDBF->pachFieldType = NULL; - psDBF->pszHeader = NULL; - - psDBF->nCurrentRecord = -1; - psDBF->bCurrentRecordModified = FALSE; - psDBF->pszCurrentRecord = NULL; - - psDBF->bNoHeader = TRUE; - - return( psDBF ); -} - -/************************************************************************/ -/* DBFAddField() */ -/* */ -/* Add a field to a newly created .dbf file before any records */ -/* are written. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFAddField(DBFHandle psDBF, const char * pszFieldName, - DBFFieldType eType, int nWidth, int nDecimals ) - -{ - char *pszFInfo; - int i; - -/* -------------------------------------------------------------------- */ -/* Do some checking to ensure we can add records to this file. */ -/* -------------------------------------------------------------------- */ - if( psDBF->nRecords > 0 ) - return( -1 ); - - if( !psDBF->bNoHeader ) - return( -1 ); - - if( eType != FTDouble && nDecimals != 0 ) - return( -1 ); - - if( nWidth < 1 ) - return -1; - -/* -------------------------------------------------------------------- */ -/* SfRealloc all the arrays larger to hold the additional field */ -/* information. */ -/* -------------------------------------------------------------------- */ - psDBF->nFields++; - - psDBF->panFieldOffset = (int *) - SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); - - psDBF->panFieldSize = (int *) - SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); - - psDBF->panFieldDecimals = (int *) - SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); - - psDBF->pachFieldType = (char *) - SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); - -/* -------------------------------------------------------------------- */ -/* Assign the new field information fields. */ -/* -------------------------------------------------------------------- */ - psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength; - psDBF->nRecordLength += nWidth; - psDBF->panFieldSize[psDBF->nFields-1] = nWidth; - psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals; - - if( eType == FTLogical ) - psDBF->pachFieldType[psDBF->nFields-1] = 'L'; - else if( eType == FTString ) - psDBF->pachFieldType[psDBF->nFields-1] = 'C'; - else - psDBF->pachFieldType[psDBF->nFields-1] = 'N'; - -/* -------------------------------------------------------------------- */ -/* Extend the required header information. */ -/* -------------------------------------------------------------------- */ - psDBF->nHeaderLength += 32; - psDBF->bUpdated = FALSE; - - psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); - - pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1); - - for( i = 0; i < 32; i++ ) - pszFInfo[i] = '\0'; - - if( (int) strlen(pszFieldName) < 10 ) - strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); - else - strncpy( pszFInfo, pszFieldName, 10); - - pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1]; - - if( eType == FTString ) - { - pszFInfo[16] = nWidth % 256; - pszFInfo[17] = nWidth / 256; - } - else - { - pszFInfo[16] = nWidth; - pszFInfo[17] = nDecimals; - } - -/* -------------------------------------------------------------------- */ -/* Make the current record buffer appropriately larger. */ -/* -------------------------------------------------------------------- */ - psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, - psDBF->nRecordLength); - - return( psDBF->nFields-1 ); -} - -/************************************************************************/ -/* DBFReadAttribute() */ -/* */ -/* Read one of the attribute fields of a record. */ -/************************************************************************/ - -static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, - char chReqType ) - -{ - int nRecordOffset; - unsigned char *pabyRec; - void *pReturnField = NULL; - - static double dDoubleField; - -/* -------------------------------------------------------------------- */ -/* Verify selection. */ -/* -------------------------------------------------------------------- */ - if( hEntity < 0 || hEntity >= psDBF->nRecords ) - return( NULL ); - - if( iField < 0 || iField >= psDBF->nFields ) - return( NULL ); - -/* -------------------------------------------------------------------- */ -/* Have we read the record? */ -/* -------------------------------------------------------------------- */ - if( psDBF->nCurrentRecord != hEntity ) - { - DBFFlushRecord( psDBF ); - - nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; - - if( fseek( psDBF->fp, nRecordOffset, 0 ) != 0 ) - { - fprintf( stderr, "fseek(%d) failed on DBF file.\n", - nRecordOffset ); - return NULL; - } - - if( fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, - 1, psDBF->fp ) != 1 ) - { - fprintf( stderr, "fread(%d) failed on DBF file.\n", - psDBF->nRecordLength ); - return NULL; - } - - psDBF->nCurrentRecord = hEntity; - } - - pabyRec = (unsigned char *) psDBF->pszCurrentRecord; - -/* -------------------------------------------------------------------- */ -/* Ensure our field buffer is large enough to hold this buffer. */ -/* -------------------------------------------------------------------- */ - if( psDBF->panFieldSize[iField]+1 > nStringFieldLen ) - { - nStringFieldLen = psDBF->panFieldSize[iField]*2 + 10; - pszStringField = (char *) SfRealloc(pszStringField,nStringFieldLen); - } - -/* -------------------------------------------------------------------- */ -/* Extract the requested field. */ -/* -------------------------------------------------------------------- */ - strncpy( pszStringField, - ((const char *) pabyRec) + psDBF->panFieldOffset[iField], - psDBF->panFieldSize[iField] ); - pszStringField[psDBF->panFieldSize[iField]] = '\0'; - - pReturnField = pszStringField; - -/* -------------------------------------------------------------------- */ -/* Decode the field. */ -/* -------------------------------------------------------------------- */ - if( chReqType == 'N' ) - { - dDoubleField = atof(pszStringField); - - pReturnField = &dDoubleField; - } - -/* -------------------------------------------------------------------- */ -/* Should we trim white space off the string attribute value? */ -/* -------------------------------------------------------------------- */ -#ifdef TRIM_DBF_WHITESPACE - else - { - char *pchSrc, *pchDst; - - pchDst = pchSrc = pszStringField; - while( *pchSrc == ' ' ) - pchSrc++; - - while( *pchSrc != '\0' ) - *(pchDst++) = *(pchSrc++); - *pchDst = '\0'; - - while( pchDst != pszStringField && *(--pchDst) == ' ' ) - *pchDst = '\0'; - } -#endif - - return( pReturnField ); -} - -/************************************************************************/ -/* DBFReadIntAttribute() */ -/* */ -/* Read an integer attribute. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) - -{ - double *pdValue; - - pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); - - if( pdValue == NULL ) - return 0; - else - return( (int) *pdValue ); -} - -/************************************************************************/ -/* DBFReadDoubleAttribute() */ -/* */ -/* Read a double attribute. */ -/************************************************************************/ - -double SHPAPI_CALL -DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) - -{ - double *pdValue; - - pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); - - if( pdValue == NULL ) - return 0.0; - else - return( *pdValue ); -} - -/************************************************************************/ -/* DBFReadStringAttribute() */ -/* */ -/* Read a string attribute. */ -/************************************************************************/ - -const char SHPAPI_CALL1(*) -DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField ) - -{ - return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) ); -} - -/************************************************************************/ -/* DBFReadLogicalAttribute() */ -/* */ -/* Read a logical attribute. */ -/************************************************************************/ - -const char SHPAPI_CALL1(*) -DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField ) - -{ - return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) ); -} - -/************************************************************************/ -/* DBFIsAttributeNULL() */ -/* */ -/* Return TRUE if value for field is NULL. */ -/* */ -/* Contributed by Jim Matthews. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) - -{ - const char *pszValue; - - pszValue = DBFReadStringAttribute( psDBF, iRecord, iField ); - - switch(psDBF->pachFieldType[iField]) - { - case 'N': - case 'F': - /* NULL numeric fields have value "****************" */ - return pszValue[0] == '*'; - - case 'D': - /* NULL date fields have value "00000000" */ - return strncmp(pszValue,"00000000",8) == 0; - - case 'L': - /* NULL boolean fields have value "?" */ - return pszValue[0] == '?'; - - default: - /* empty string fields are considered NULL */ - return strlen(pszValue) == 0; - } -} - -/************************************************************************/ -/* DBFGetFieldCount() */ -/* */ -/* Return the number of fields in this table. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFGetFieldCount( DBFHandle psDBF ) - -{ - return( psDBF->nFields ); -} - -/************************************************************************/ -/* DBFGetRecordCount() */ -/* */ -/* Return the number of records in this table. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFGetRecordCount( DBFHandle psDBF ) - -{ - return( psDBF->nRecords ); -} - -/************************************************************************/ -/* DBFGetFieldInfo() */ -/* */ -/* Return any requested information about the field. */ -/************************************************************************/ - -DBFFieldType SHPAPI_CALL -DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, - int * pnWidth, int * pnDecimals ) - -{ - if( iField < 0 || iField >= psDBF->nFields ) - return( FTInvalid ); - - if( pnWidth != NULL ) - *pnWidth = psDBF->panFieldSize[iField]; - - if( pnDecimals != NULL ) - *pnDecimals = psDBF->panFieldDecimals[iField]; - - if( pszFieldName != NULL ) - { - int i; - - strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 ); - pszFieldName[11] = '\0'; - for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- ) - pszFieldName[i] = '\0'; - } - - if ( psDBF->pachFieldType[iField] == 'L' ) - return( FTLogical); - - else if( psDBF->pachFieldType[iField] == 'N' - || psDBF->pachFieldType[iField] == 'F' - || psDBF->pachFieldType[iField] == 'D' ) - { - if( psDBF->panFieldDecimals[iField] > 0 ) - return( FTDouble ); - else - return( FTInteger ); - } - else - { - return( FTString ); - } -} - -/************************************************************************/ -/* DBFWriteAttribute() */ -/* */ -/* Write an attribute record to the file. */ -/************************************************************************/ - -static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, - void * pValue ) - -{ - int nRecordOffset, i, j, nRetResult = TRUE; - unsigned char *pabyRec; - char szSField[400], szFormat[20]; - -/* -------------------------------------------------------------------- */ -/* Is this a valid record? */ -/* -------------------------------------------------------------------- */ - if( hEntity < 0 || hEntity > psDBF->nRecords ) - return( FALSE ); - - if( psDBF->bNoHeader ) - DBFWriteHeader(psDBF); - -/* -------------------------------------------------------------------- */ -/* Is this a brand new record? */ -/* -------------------------------------------------------------------- */ - if( hEntity == psDBF->nRecords ) - { - DBFFlushRecord( psDBF ); - - psDBF->nRecords++; - for( i = 0; i < psDBF->nRecordLength; i++ ) - psDBF->pszCurrentRecord[i] = ' '; - - psDBF->nCurrentRecord = hEntity; - } - -/* -------------------------------------------------------------------- */ -/* Is this an existing record, but different than the last one */ -/* we accessed? */ -/* -------------------------------------------------------------------- */ - if( psDBF->nCurrentRecord != hEntity ) - { - DBFFlushRecord( psDBF ); - - nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; - - fseek( psDBF->fp, nRecordOffset, 0 ); - fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); - - psDBF->nCurrentRecord = hEntity; - } - - pabyRec = (unsigned char *) psDBF->pszCurrentRecord; - - psDBF->bCurrentRecordModified = TRUE; - psDBF->bUpdated = TRUE; - -/* -------------------------------------------------------------------- */ -/* Translate NULL value to valid DBF file representation. */ -/* */ -/* Contributed by Jim Matthews. */ -/* -------------------------------------------------------------------- */ - if( pValue == NULL ) - { - switch(psDBF->pachFieldType[iField]) - { - case 'N': - case 'F': - /* NULL numeric fields have value "****************" */ - memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '*', - psDBF->panFieldSize[iField] ); - break; - - case 'D': - /* NULL date fields have value "00000000" */ - memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '0', - psDBF->panFieldSize[iField] ); - break; - - case 'L': - /* NULL boolean fields have value "?" */ - memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '?', - psDBF->panFieldSize[iField] ); - break; - - default: - /* empty string fields are considered NULL */ - memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '\0', - psDBF->panFieldSize[iField] ); - break; - } - return TRUE; - } - -/* -------------------------------------------------------------------- */ -/* Assign all the record fields. */ -/* -------------------------------------------------------------------- */ - switch( psDBF->pachFieldType[iField] ) - { - case 'D': - case 'N': - case 'F': - if( psDBF->panFieldDecimals[iField] == 0 ) - { - int nWidth = psDBF->panFieldSize[iField]; - - if( sizeof(szSField)-2 < nWidth ) - nWidth = sizeof(szSField)-2; - - sprintf( szFormat, "%%%dd", nWidth ); - sprintf(szSField, szFormat, (int) *((double *) pValue) ); - if( (int)strlen(szSField) > psDBF->panFieldSize[iField] ) - { - szSField[psDBF->panFieldSize[iField]] = '\0'; - nRetResult = FALSE; - } - - strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), - szSField, strlen(szSField) ); - } - else - { - int nWidth = psDBF->panFieldSize[iField]; - - if( sizeof(szSField)-2 < nWidth ) - nWidth = sizeof(szSField)-2; - - sprintf( szFormat, "%%%d.%df", - nWidth, psDBF->panFieldDecimals[iField] ); - sprintf(szSField, szFormat, *((double *) pValue) ); - if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) - { - szSField[psDBF->panFieldSize[iField]] = '\0'; - nRetResult = FALSE; - } - strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), - szSField, strlen(szSField) ); - } - break; - - case 'L': - if (psDBF->panFieldSize[iField] >= 1 && - (*(char*)pValue == 'F' || *(char*)pValue == 'T')) - *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue; - break; - - default: - if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] ) - { - j = psDBF->panFieldSize[iField]; - nRetResult = FALSE; - } - else - { - memset( pabyRec+psDBF->panFieldOffset[iField], ' ', - psDBF->panFieldSize[iField] ); - j = strlen((char *) pValue); - } - - strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), - (char *) pValue, j ); - break; - } - - return( nRetResult ); -} - -/************************************************************************/ -/* DBFWriteAttributeDirectly() */ -/* */ -/* Write an attribute record to the file, but without any */ -/* reformatting based on type. The provided buffer is written */ -/* as is to the field position in the record. */ -/************************************************************************/ - -int DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, - void * pValue ) - -{ - int nRecordOffset, i, j; - unsigned char *pabyRec; - -/* -------------------------------------------------------------------- */ -/* Is this a valid record? */ -/* -------------------------------------------------------------------- */ - if( hEntity < 0 || hEntity > psDBF->nRecords ) - return( FALSE ); - - if( psDBF->bNoHeader ) - DBFWriteHeader(psDBF); - -/* -------------------------------------------------------------------- */ -/* Is this a brand new record? */ -/* -------------------------------------------------------------------- */ - if( hEntity == psDBF->nRecords ) - { - DBFFlushRecord( psDBF ); - - psDBF->nRecords++; - for( i = 0; i < psDBF->nRecordLength; i++ ) - psDBF->pszCurrentRecord[i] = ' '; - - psDBF->nCurrentRecord = hEntity; - } - -/* -------------------------------------------------------------------- */ -/* Is this an existing record, but different than the last one */ -/* we accessed? */ -/* -------------------------------------------------------------------- */ - if( psDBF->nCurrentRecord != hEntity ) - { - DBFFlushRecord( psDBF ); - - nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; - - fseek( psDBF->fp, nRecordOffset, 0 ); - fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); - - psDBF->nCurrentRecord = hEntity; - } - - pabyRec = (unsigned char *) psDBF->pszCurrentRecord; - -/* -------------------------------------------------------------------- */ -/* Assign all the record fields. */ -/* -------------------------------------------------------------------- */ - if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] ) - j = psDBF->panFieldSize[iField]; - else - { - memset( pabyRec+psDBF->panFieldOffset[iField], ' ', - psDBF->panFieldSize[iField] ); - j = strlen((char *) pValue); - } - - strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), - (char *) pValue, j ); - - psDBF->bCurrentRecordModified = TRUE; - psDBF->bUpdated = TRUE; - - return( TRUE ); -} - -/************************************************************************/ -/* DBFWriteDoubleAttribute() */ -/* */ -/* Write a double attribute. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, - double dValue ) - -{ - return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); -} - -/************************************************************************/ -/* DBFWriteIntegerAttribute() */ -/* */ -/* Write a integer attribute. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, - int nValue ) - -{ - double dValue = nValue; - - return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); -} - -/************************************************************************/ -/* DBFWriteStringAttribute() */ -/* */ -/* Write a string attribute. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, - const char * pszValue ) - -{ - return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) ); -} - -/************************************************************************/ -/* DBFWriteNULLAttribute() */ -/* */ -/* Write a string attribute. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField ) - -{ - return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) ); -} - -/************************************************************************/ -/* DBFWriteLogicalAttribute() */ -/* */ -/* Write a logical attribute. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField, - const char lValue) - -{ - return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) ); -} - -/************************************************************************/ -/* DBFWriteTuple() */ -/* */ -/* Write an attribute record to the file. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ) - -{ - int nRecordOffset, i; - unsigned char *pabyRec; - -/* -------------------------------------------------------------------- */ -/* Is this a valid record? */ -/* -------------------------------------------------------------------- */ - if( hEntity < 0 || hEntity > psDBF->nRecords ) - return( FALSE ); - - if( psDBF->bNoHeader ) - DBFWriteHeader(psDBF); - -/* -------------------------------------------------------------------- */ -/* Is this a brand new record? */ -/* -------------------------------------------------------------------- */ - if( hEntity == psDBF->nRecords ) - { - DBFFlushRecord( psDBF ); - - psDBF->nRecords++; - for( i = 0; i < psDBF->nRecordLength; i++ ) - psDBF->pszCurrentRecord[i] = ' '; - - psDBF->nCurrentRecord = hEntity; - } - -/* -------------------------------------------------------------------- */ -/* Is this an existing record, but different than the last one */ -/* we accessed? */ -/* -------------------------------------------------------------------- */ - if( psDBF->nCurrentRecord != hEntity ) - { - DBFFlushRecord( psDBF ); - - nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; - - fseek( psDBF->fp, nRecordOffset, 0 ); - fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); - - psDBF->nCurrentRecord = hEntity; - } - - pabyRec = (unsigned char *) psDBF->pszCurrentRecord; - - memcpy ( pabyRec, pRawTuple, psDBF->nRecordLength ); - - psDBF->bCurrentRecordModified = TRUE; - psDBF->bUpdated = TRUE; - - return( TRUE ); -} - -/************************************************************************/ -/* DBFReadTuple() */ -/* */ -/* Read one of the attribute fields of a record. */ -/************************************************************************/ - -const char SHPAPI_CALL1(*) -DBFReadTuple(DBFHandle psDBF, int hEntity ) - -{ - int nRecordOffset; - unsigned char *pabyRec; - static char *pReturnTuple = NULL; - - static int nTupleLen = 0; - -/* -------------------------------------------------------------------- */ -/* Have we read the record? */ -/* -------------------------------------------------------------------- */ - if( hEntity < 0 || hEntity >= psDBF->nRecords ) - return( NULL ); - - if( psDBF->nCurrentRecord != hEntity ) - { - DBFFlushRecord( psDBF ); - - nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; - - fseek( psDBF->fp, nRecordOffset, 0 ); - fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); - - psDBF->nCurrentRecord = hEntity; - } - - pabyRec = (unsigned char *) psDBF->pszCurrentRecord; - - if ( nTupleLen < psDBF->nRecordLength) { - nTupleLen = psDBF->nRecordLength; - pReturnTuple = (char *) SfRealloc(pReturnTuple, psDBF->nRecordLength); - } - - memcpy ( pReturnTuple, pabyRec, psDBF->nRecordLength ); - - return( pReturnTuple ); -} - -/************************************************************************/ -/* DBFCloneEmpty() */ -/* */ -/* Read one of the attribute fields of a record. */ -/************************************************************************/ - -DBFHandle SHPAPI_CALL -DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) -{ - DBFHandle newDBF; - - newDBF = DBFCreate ( pszFilename ); - if ( newDBF == NULL ) return ( NULL ); - - newDBF->pszHeader = (char *) malloc ( 32 * psDBF->nFields ); - memcpy ( newDBF->pszHeader, psDBF->pszHeader, 32 * psDBF->nFields ); - - newDBF->nFields = psDBF->nFields; - newDBF->nRecordLength = psDBF->nRecordLength; - newDBF->nHeaderLength = 32 * (psDBF->nFields+1); - - newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); - memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); - newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields ); - memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); - newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields ); - memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); - newDBF->pachFieldType = (char *) malloc ( sizeof(int) * psDBF->nFields ); - memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(int) * psDBF->nFields ); - - newDBF->bNoHeader = TRUE; - newDBF->bUpdated = TRUE; - - DBFWriteHeader ( newDBF ); - DBFClose ( newDBF ); - - newDBF = DBFOpen ( pszFilename, "rb+" ); - - return ( newDBF ); -} - -/************************************************************************/ -/* DBFGetNativeFieldType() */ -/* */ -/* Return the DBase field type for the specified field. */ -/* */ -/* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */ -/* 'N' (Numeric, with or without decimal), */ -/* 'L' (Logical), */ -/* 'M' (Memo: 10 digits .DBT block ptr) */ -/************************************************************************/ - -char SHPAPI_CALL -DBFGetNativeFieldType( DBFHandle psDBF, int iField ) - -{ - if( iField >=0 && iField < psDBF->nFields ) - return psDBF->pachFieldType[iField]; - - return ' '; -} - -/************************************************************************/ -/* str_to_upper() */ -/************************************************************************/ - -static void str_to_upper (char *string) -{ - int len; - short i = -1; - - len = strlen (string); - - while (++i < len) - if (isalpha(string[i]) && islower(string[i])) - string[i] = toupper ((int)string[i]); -} - -/************************************************************************/ -/* DBFGetFieldIndex() */ -/* */ -/* Get the index number for a field in a .dbf file. */ -/* */ -/* Contributed by Jim Matthews. */ -/************************************************************************/ - -int SHPAPI_CALL -DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) - -{ - char name[12], name1[12], name2[12]; - int i; - - strncpy(name1, pszFieldName,11); - name1[11] = '\0'; - str_to_upper(name1); - - for( i = 0; i < DBFGetFieldCount(psDBF); i++ ) - { - DBFGetFieldInfo( psDBF, i, name, NULL, NULL ); - strncpy(name2,name,11); - str_to_upper(name2); - - if(!strncmp(name1,name2,10)) - return(i); - } - return(-1); -} diff --git a/src/ninja/dust.cpp b/src/ninja/dust.cpp index b42496b87..0dd81c6b4 100644 --- a/src/ninja/dust.cpp +++ b/src/ninja/dust.cpp @@ -158,7 +158,7 @@ void Dust::MakeGrid(WindNinjaInputs &input, AsciiGrid &grid) hMemDS = NULL; } - OGR_DS_Destroy(hOGRDS); + GDALClose(hOGRDS); } #endif diff --git a/src/ninja/gdal_util.cpp b/src/ninja/gdal_util.cpp index 56084ffb5..ee6e8f631 100644 --- a/src/ninja/gdal_util.cpp +++ b/src/ninja/gdal_util.cpp @@ -861,47 +861,46 @@ bool GDALPointToLatLon( double &x, double &y, GDALDataset *poSrcDS, bool OGRPointToLatLon(double &x, double &y, OGRDataSourceH hDS, const char *datum) { - char *pszPrj = NULL; + char *pszPrj = NULL; - OGRSpatialReference *poSrcSRS; - OGRSpatialReference oSourceSRS, oTargetSRS; - OGRCoordinateTransformation *poCT; - - if (hDS == NULL) { - return false; - } - - OGRLayer *poLayer; + const OGRSpatialReference *poSrcSRS; + OGRSpatialReference oSourceSRS, oTargetSRS; + OGRCoordinateTransformation *poCT; - poLayer = (OGRLayer *)OGR_DS_GetLayer(hDS, 0); - poLayer->ResetReading(); + if (hDS == NULL) { + return false; + } - poSrcSRS = poLayer->GetSpatialRef(); - if (poSrcSRS == NULL) { - return false; - } + OGRLayer *poLayer; - oTargetSRS.SetWellKnownGeogCS(datum); + poLayer = (OGRLayer *)OGR_DS_GetLayer(hDS, 0); + poLayer->ResetReading(); -#ifdef GDAL_COMPUTE_VERSION -#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,0,0) - poSrcSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); - oTargetSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); -#endif /* GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,0,0) */ -#endif /* GDAL_COMPUTE_VERSION */ + poSrcSRS = poLayer->GetSpatialRef(); + if (poSrcSRS == NULL) { + return false; + } + oSourceSRS = *poSrcSRS; + oTargetSRS.SetWellKnownGeogCS(datum); - poCT = OGRCreateCoordinateTransformation(poSrcSRS, &oTargetSRS); + #ifdef GDAL_COMPUTE_VERSION + #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,0,0) + oSourceSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + oTargetSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + #endif /* GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,0,0) */ + #endif /* GDAL_COMPUTE_VERSION */ + poCT = OGRCreateCoordinateTransformation(&oSourceSRS, &oTargetSRS); - if (poCT == NULL) { - return false; - } + if (poCT == NULL) { + return false; + } - if (!poCT->Transform(1, &x, &y)) { + if (!poCT->Transform(1, &x, &y)) { + OGRCoordinateTransformation::DestroyCT(poCT); + return false; + } OGRCoordinateTransformation::DestroyCT(poCT); - return false; - } - OGRCoordinateTransformation::DestroyCT(poCT); - return true; + return true; } /** @@ -1167,7 +1166,7 @@ std::string FetchTimeZone( double dfX, double dfY, const char *pszWkt ) } OGR_F_Destroy( hFeature ); OGR_G_DestroyGeometry( hGeometry ); - OGR_DS_Destroy( hDS ); + GDALClose( hDS ); return oTimeZone; } /** @@ -1238,7 +1237,7 @@ int NinjaOGRContain(const char *pszWkt, const char *pszFile, } } OGR_G_DestroyGeometry( hTestGeometry ); - OGR_DS_Destroy( hDS ); + GDALClose( hDS ); return bContains; } diff --git a/src/ninja/genericSurfInitialization.cpp b/src/ninja/genericSurfInitialization.cpp index aafa04655..edd0a5066 100644 --- a/src/ninja/genericSurfInitialization.cpp +++ b/src/ninja/genericSurfInitialization.cpp @@ -207,7 +207,7 @@ void genericSurfInitialization::checkForValidData() else { noDataValueExists = true; - noDataIsNan = cplIsNan(dfNoData); + noDataIsNan = std::isnan(dfNoData); } //set the data @@ -221,7 +221,7 @@ void genericSurfInitialization::checkForValidData() { if(noDataIsNan) { - if(cplIsNan(padfScanline[k])) + if(std::isnan(padfScanline[k])) throw badForecastFile("Forecast file contains no_data values."); }else { @@ -472,28 +472,28 @@ void genericSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, if( varList[i] == "Temperature_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, airGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue(-9999.0); airGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "V-component_of_wind_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, vGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue(-9999.0); vGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "U-component_of_wind_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, uGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue(-9999.0); uGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "Total_cloud_cover" ) { GDAL2AsciiGrid( wrpDS, bandNum, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue(-9999.0); cloudGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/ncepGfsSurfInitialization.cpp b/src/ninja/ncepGfsSurfInitialization.cpp index 57eb353e5..f12a861c6 100644 --- a/src/ninja/ncepGfsSurfInitialization.cpp +++ b/src/ninja/ncepGfsSurfInitialization.cpp @@ -217,7 +217,7 @@ void ncepGfsSurfInitialization::checkForValidData() else { noDataValueExists = true; - noDataIsNan = cplIsNan(dfNoData); + noDataIsNan = std::isnan(dfNoData); } //set the data @@ -231,7 +231,7 @@ void ncepGfsSurfInitialization::checkForValidData() { if(noDataIsNan) { - if(cplIsNan(padfScanline[k])) + if(std::isnan(padfScanline[k])) throw badForecastFile("Forecast file contains no_data values."); }else { @@ -557,7 +557,7 @@ void ncepGfsSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, if( varList[i] == "Temperature_height_above_ground" ) { tempBandNum = (bandNum * 3) - 2; // adjust for height dimension (3) GDAL2AsciiGrid( wrpDS, tempBandNum, airGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue(-9999.0); airGrid.replaceNan( -9999.0 ); } @@ -565,7 +565,7 @@ void ncepGfsSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, else if( varList[i] == "v-component_of_wind_height_above_ground" ) { vBandNum = (bandNum * 3) - 2; // adjust for height dimension (3) GDAL2AsciiGrid( wrpDS, vBandNum, vGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue(-9999.0); vGrid.replaceNan( -9999.0 ); } @@ -573,7 +573,7 @@ void ncepGfsSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, else if( varList[i] == "u-component_of_wind_height_above_ground" ) { uBandNum = (bandNum * 3) - 2; //adjust for height dimension (3) GDAL2AsciiGrid( wrpDS, uBandNum, uGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue(-9999.0); uGrid.replaceNan( -9999.0 ); //TEST FOR SOUTHERN HEMISPHERE WARP ISSUE. @@ -584,7 +584,7 @@ void ncepGfsSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, } else if( varList[i] == "Total_cloud_cover_convective_cloud" ) { GDAL2AsciiGrid( wrpDS, bandNum, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue(-9999.0); cloudGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/ncepHrrrSurfInitialization.cpp b/src/ninja/ncepHrrrSurfInitialization.cpp index ccc57ce60..401b7d16e 100644 --- a/src/ninja/ncepHrrrSurfInitialization.cpp +++ b/src/ninja/ncepHrrrSurfInitialization.cpp @@ -374,7 +374,7 @@ void ncepHrrrSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, if( varList[i] == "2t" ) { GDAL2AsciiGrid( wrpDS, i+1, airGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue( -9999.0 ); airGrid.replaceNan( -9999.0 ); } @@ -385,21 +385,21 @@ void ncepHrrrSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, } else if( varList[i] == "10v" ) { GDAL2AsciiGrid( wrpDS, i+1, vGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue( -9999.0 ); vGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "10u" ) { GDAL2AsciiGrid( wrpDS, i+1, uGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue( -9999.0 ); uGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "tcc" ) { GDAL2AsciiGrid( wrpDS, i+1, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue( -9999.0 ); cloudGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/ncepNamAlaskaSurfInitialization.cpp b/src/ninja/ncepNamAlaskaSurfInitialization.cpp index 1d44a47d8..69bea3588 100644 --- a/src/ninja/ncepNamAlaskaSurfInitialization.cpp +++ b/src/ninja/ncepNamAlaskaSurfInitialization.cpp @@ -200,7 +200,7 @@ void ncepNamAlaskaSurfInitialization::checkForValidData() else { noDataValueExists = true; - noDataIsNan = cplIsNan(dfNoData); + noDataIsNan = std::isnan(dfNoData); } //set the data @@ -214,7 +214,7 @@ void ncepNamAlaskaSurfInitialization::checkForValidData() { if(noDataIsNan) { - if(cplIsNan(padfScanline[k])) + if(std::isnan(padfScanline[k])) throw badForecastFile("Forecast file contains no_data values."); }else { @@ -522,28 +522,28 @@ void ncepNamAlaskaSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, } if( varList[i] == "Temperature_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, airGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue(-9999.0); airGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "v-component_of_wind_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, vGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue(-9999.0); vGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "u-component_of_wind_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, uGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue(-9999.0); uGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "Total_cloud_cover_entire_atmosphere_single_layer" ) { GDAL2AsciiGrid( wrpDS, bandNum, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue(-9999.0); cloudGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/ncepNamGrib2SurfInitialization.cpp b/src/ninja/ncepNamGrib2SurfInitialization.cpp index 187292563..be787c84c 100644 --- a/src/ninja/ncepNamGrib2SurfInitialization.cpp +++ b/src/ninja/ncepNamGrib2SurfInitialization.cpp @@ -290,28 +290,28 @@ void ncepNamGrib2SurfInitialization::setSurfaceGrids( WindNinjaInputs &input, for( unsigned int i = 0; i < varList.size(); i++ ) { if( varList[i] == "2t" ) { GDAL2AsciiGrid( wrpDS, i+1, airGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue( -9999.0 ); airGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "10v" ) { GDAL2AsciiGrid( wrpDS, i+1, vGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue( -9999.0 ); vGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "10u" ) { GDAL2AsciiGrid( wrpDS, i+1, uGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue( -9999.0 ); uGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "tcc" ) { GDAL2AsciiGrid( wrpDS, i+1, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue( -9999.0 ); cloudGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/ncepNamSurfInitialization.cpp b/src/ninja/ncepNamSurfInitialization.cpp index 4f7f2b24b..d2dd76ffd 100644 --- a/src/ninja/ncepNamSurfInitialization.cpp +++ b/src/ninja/ncepNamSurfInitialization.cpp @@ -200,7 +200,7 @@ void ncepNamSurfInitialization::checkForValidData() else { noDataValueExists = true; - noDataIsNan = cplIsNan(dfNoData); + noDataIsNan = std::isnan(dfNoData); } //set the data @@ -214,7 +214,7 @@ void ncepNamSurfInitialization::checkForValidData() { if(noDataIsNan) { - if(cplIsNan(padfScanline[k])) + if(std::isnan(padfScanline[k])) throw badForecastFile("Forecast file contains no_data values."); }else { @@ -526,28 +526,28 @@ void ncepNamSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, if( varList[i] == "Temperature_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, airGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue(-9999.0); airGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "v-component_of_wind_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, vGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue(-9999.0); vGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "u-component_of_wind_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, uGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue(-9999.0); uGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "Total_cloud_cover_entire_atmosphere_single_layer" ) { GDAL2AsciiGrid( wrpDS, bandNum, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue(-9999.0); cloudGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/ncepNdfdInitialization.cpp b/src/ninja/ncepNdfdInitialization.cpp index a722f4908..3db4fdb03 100644 --- a/src/ninja/ncepNdfdInitialization.cpp +++ b/src/ninja/ncepNdfdInitialization.cpp @@ -244,7 +244,7 @@ void ncepNdfdInitialization::checkForValidData() else { noDataValueExists = true; - noDataIsNan = cplIsNan(dfNoData); + noDataIsNan = std::isnan(dfNoData); } //set the data @@ -258,7 +258,7 @@ void ncepNdfdInitialization::checkForValidData() { if(noDataIsNan) { - if(cplIsNan(padfScanline[k])) + if(std::isnan(padfScanline[k])) throw badForecastFile("Forecast file contains no_data values."); }else { @@ -682,7 +682,7 @@ void ncepNdfdInitialization::setSurfaceGrids( WindNinjaInputs &input, // GDAL2AsciiGrid( wrpDS, bandNumTempLuck/2, airGrid ); // } // } - //if( cplIsNan( dfNoData ) ) { + //if( std::isnan( dfNoData ) ) { //airHighGrid.set_noDataValue(-9999.0); //airHighGrid.replaceNan( -9999.0 ); //airLowGrid.set_noDataValue(-9999.0); @@ -721,7 +721,7 @@ void ncepNdfdInitialization::setSurfaceGrids( WindNinjaInputs &input, // } ///* fix no data in the air high, low, and regular grid */ - //if( cplIsNan( dfNoData ) ) { + //if( std::isnan( dfNoData ) ) { //airHighGrid.set_noDataValue(-9999.0); //airHighGrid.replaceNan( -9999.0 ); //airLowGrid.set_noDataValue(-9999.0); @@ -734,7 +734,7 @@ void ncepNdfdInitialization::setSurfaceGrids( WindNinjaInputs &input, if(varList[i] == "Total_cloud_cover_entire_atmosphere_single_layer_layer") { GDAL2AsciiGrid( wrpDS, bandNum, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue(-9999.0); cloudGrid.replaceNan( -9999.0 ); } @@ -743,7 +743,7 @@ void ncepNdfdInitialization::setSurfaceGrids( WindNinjaInputs &input, }else if(varList[i] == "Wind_direction_from_which_blowing_height_above_ground") { GDAL2AsciiGrid( wrpDS, bandNum, directionGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { directionGrid.set_noDataValue(-9999.0); directionGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/ncepRapSurfInitialization.cpp b/src/ninja/ncepRapSurfInitialization.cpp index 19b5348a6..950f049a9 100644 --- a/src/ninja/ncepRapSurfInitialization.cpp +++ b/src/ninja/ncepRapSurfInitialization.cpp @@ -202,7 +202,7 @@ void ncepRapSurfInitialization::checkForValidData() else { noDataValueExists = true; - noDataIsNan = cplIsNan(dfNoData); + noDataIsNan = std::isnan(dfNoData); } //set the data @@ -221,7 +221,7 @@ void ncepRapSurfInitialization::checkForValidData() { if(noDataIsNan) { - if(cplIsNan(padfScanline[k])) + if(std::isnan(padfScanline[k])) throw badForecastFile("Forecast file contains no_data values."); }else { @@ -504,28 +504,28 @@ void ncepRapSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, if( varList[i] == "Temperature_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, airGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue(-9999.0); airGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "v-component_of_wind_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, vGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue(-9999.0); vGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "u-component_of_wind_height_above_ground" ) { GDAL2AsciiGrid( wrpDS, bandNum, uGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue(-9999.0); uGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "Geopotential_height_cloud_tops" ) { GDAL2AsciiGrid( wrpDS, bandNum, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue(-9999.0); cloudGrid.replaceNan( -99999.0 ); } diff --git a/src/ninja/ninja.cpp b/src/ninja/ninja.cpp index 76ae7ec4a..a29443ee1 100644 --- a/src/ninja/ninja.cpp +++ b/src/ninja/ninja.cpp @@ -76,13 +76,6 @@ ninja::ninja() nMaxMatchingIters = atoi( CPLGetConfigOption( "NINJA_POINT_MAX_MATCH_ITERS", "150" ) ); CPLDebug( "NINJA", "Maximum match iterations set to: %d", nMaxMatchingIters ); - - //ninjaCom stuff - input.lastComString[0] = '\0'; - input.inputsRunNumber = 0; - input.inputsComType = ninjaComClass::ninjaDefaultCom; - input.Com = new ninjaDefaultComHandler(); - } /**Ninja destructor @@ -90,8 +83,7 @@ ninja::ninja() */ ninja::~ninja() { - deleteDynamicMemory(); - delete input.Com; + deleteDynamicMemory(); } /** @@ -125,12 +117,6 @@ ninja::ninja(const ninja &rhs) , mesh(rhs.mesh) , input(rhs.input) { - input.Com = NULL; //must be set to null! - set_ninjaCommunication(rhs.get_inputsRunNumber(), rhs.get_inputsComType()); - strcpy( input.lastComString, rhs.get_lastComString() ); - input.Com->fpLog = rhs.get_ComLogFp(); - - cancel = rhs.cancel; alphaH = rhs.alphaH; isNullRun = rhs.isNullRun; @@ -261,7 +247,9 @@ bool ninja::simulate_wind() checkCancel(); input.Com->ninjaCom(ninjaComClass::ninjaNone, "Reading elevation file..."); - + +//throw std::runtime_error("I WANT CHOCOLATE!!! Yum."); + readInputFile(); set_position(); @@ -3670,46 +3658,16 @@ void ninja::set_DEM(const double* dem, const int nXSize, const int nYSize, input.dem.readFromMemory(dem, nXSize, nYSize, geoRef, prj); } -int ninja::get_inputsRunNumber() const -{ - return input.inputsRunNumber; -} - -ninjaComClass::eNinjaCom ninja::get_inputsComType() const -{ - return input.inputsComType; -} - -char * ninja::get_lastComString() const -{ - return (char*)input.lastComString; -} - -FILE * ninja::get_ComLogFp() const -{ - return input.Com->fpLog; -} -ninjaComClass * ninja::get_Com() const -{ - return input.Com; -} - -#ifdef NINJA_GUI -int ninja::get_ComNumRuns() const +void ninja::set_ninjaCommunication(const ninjaComClass* Com) { - return input.Com->nRuns; + *input.Com = *Com; + input.Com->printRunNumber = true; } -void ninja::set_ComNumRuns( int nRuns ) +void ninja::set_ninjaComRunNumber(int RunNumber) { - input.Com->nRuns = nRuns; -} - -#endif //NINJA_GUI - -double ninja::get_progressWeight() -{ - return input.Com->progressWeight; + input.inputsRunNumber = RunNumber; + input.Com->runNumber = input.inputsRunNumber; } void ninja::set_progressWeight(double progressWeight) @@ -3849,7 +3807,9 @@ WindNinjaInputs::eNinjafoamMeshChoice ninja::get_eNinjafoamMeshChoice(std::strin return WindNinjaInputs::fine; } else{ - throw std::logic_error("Problem with mesh type string in ninja::get_eMeshType()."); + throw std::invalid_argument( "Invalid input '" + meshChoice + + "' in ninja::get_eMeshType()" + + "\nchoices are: 'coarse', 'medium', or 'fine'" ); } } @@ -4523,7 +4483,8 @@ void ninja::set_meshResChoice( std::string choice ) } else throw std::invalid_argument( "Invalid input '" + choice + - "' in ninja::set_meshResChoice" ); + "' in ninja::set_meshResChoice()" + + "\nchoices are: 'coarse', 'medium', or 'fine'" ); } void ninja::set_meshResChoice( const Mesh::eMeshChoice choice ) { @@ -5393,33 +5354,6 @@ double ninja::getFuelBedDepth(int fuelModel) return (depthInFeet * 0.3048); } -void ninja::set_ninjaCommunication(int RunNumber, ninjaComClass::eNinjaCom comType) -{ - input.inputsComType = comType; - - if(input.Com) - delete input.Com; - - if(comType == ninjaComClass::ninjaDefaultCom) - input.Com = new ninjaDefaultComHandler(); - else if(comType == ninjaComClass::ninjaQuietCom) - input.Com = new ninjaQuietComHandler(); - else if(comType == ninjaComClass::ninjaLoggingCom) - input.Com = new ninjaLoggingComHandler(); - else if(comType == ninjaComClass::ninjaGUICom) - input.Com = new ninjaGUIComHandler(); - else if(comType == ninjaComClass::WFDSSCom) - input.Com = new ninjaWFDSSComHandler(); - else if(comType == ninjaComClass::ninjaCLICom) - input.Com = new ninjaCLIComHandler(); - else - input.Com = new ninjaDefaultComHandler(); - - input.inputsRunNumber = RunNumber; - input.Com->runNumber = &input.inputsRunNumber; - input.Com->lastMsg = input.lastComString; -} - void ninja::checkInputs() { //check for invalid characters in DEM name diff --git a/src/ninja/ninja.h b/src/ninja/ninja.h index 3f70cf9ba..2a7288468 100644 --- a/src/ninja/ninja.h +++ b/src/ninja/ninja.h @@ -119,6 +119,8 @@ //#define NINJA_DEBUG //#define NINJA_DEBUG_VERBOSE +#include "callbackFunctions.h" + class ninja { public: @@ -163,17 +165,10 @@ class ninja /*----------------------------------------------------------------------------- * ninjaCom section *-----------------------------------------------------------------------------*/ - int get_inputsRunNumber() const; - ninjaComClass::eNinjaCom get_inputsComType() const; - char * get_lastComString() const; - FILE * get_ComLogFp() const; //returns the Com Log file pointer - ninjaComClass * get_Com() const; //returns the Com object -#ifdef NINJA_GUI - int get_ComNumRuns() const; - void set_ComNumRuns( int nRuns ); -#endif //NINJA-GUI + void set_ninjaCommunication(const ninjaComClass* Com); + void set_ninjaComRunNumber(int RunNumber); void set_progressWeight(double progressWeight); //For foam+diurnal simulations - double get_progressWeight(); + /************************************************************* kyle's fx's for importing several file types through GDAL function lives in readInputFile.cpp for now. @@ -366,7 +361,6 @@ class ninja double getFuelBedDepth(int fuelModel); - void set_ninjaCommunication(int RunNumber, ninjaComClass::eNinjaCom comType); void checkInputs(); void dumpMemory(); diff --git a/src/ninja/ninjaArmy.cpp b/src/ninja/ninjaArmy.cpp index b40b5ff21..88ef05a5e 100644 --- a/src/ninja/ninjaArmy.cpp +++ b/src/ninja/ninjaArmy.cpp @@ -35,6 +35,10 @@ ninjaArmy::ninjaArmy() : writeFarsiteAtmFile(false) { + Com = new ninjaComClass(); + Com->runNumber = 9999; + Com->printRunNumber = false; + // ninjas.push_back(new ninja()); initLocalData(); } @@ -46,6 +50,8 @@ ninjaArmy::ninjaArmy() */ ninjaArmy::ninjaArmy(const ninjaArmy& A) { + Com = new ninjaComClass(*A.Com); + writeFarsiteAtmFile = A.writeFarsiteAtmFile; ninjas = A.ninjas; copyLocalData( A ); @@ -62,6 +68,7 @@ ninjaArmy::~ninjaArmy() delete ninjas[0]; } destoryLocalData(); + delete Com; } /** @@ -74,6 +81,10 @@ ninjaArmy& ninjaArmy::operator= (ninjaArmy const& A) { if(&A != this) { + delete Com; + Com = new ninjaComClass(); + *Com = *A.Com; + writeFarsiteAtmFile = A.writeFarsiteAtmFile; ninjas = A.ninjas; copyLocalData( A ); @@ -93,6 +104,16 @@ int ninjaArmy::getSize() void ninjaArmy::makeDomainAverageArmy( int nSize, bool momentumFlag ) { +//Com->ninjaCom(ninjaComClass::ninjaFailure, "forcing an error message in ninjaArmy::makeDomainAverageArmy."); +//throw std::runtime_error("forcing an error message in ninjaArmy::makeDomainAverageArmy."); +Com->ninjaCom(ninjaComClass::ninjaNone, "running ninjaArmy::makeDomainAverageArmy."); + + if( nSize < 1 ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid input numNinjas '%d' in ninjaArmy::makeDomainAverageArmy()", nSize); + throw std::runtime_error(CPLSPrintf("Invalid input numNinjas '%d' in ninjaArmy::makeDomainAverageArmy()", nSize)); + } + int i; for( i=0; i < ninjas.size();i ++) delete ninjas[i]; @@ -107,6 +128,8 @@ void ninjaArmy::makeDomainAverageArmy( int nSize, bool momentumFlag ) #else ninjas[i] = new ninja(); #endif //NINJAFOAM + + setNinjaCommunication( i, i ); } } @@ -121,6 +144,16 @@ void ninjaArmy::makePointArmy(std::vector timeList, string timeZone, string stationFileName, string demFile, bool matchPoints, bool momentumFlag) { +//Com->ninjaCom(ninjaComClass::ninjaFailure, "forcing an error message in ninjaArmy::makePointArmy."); +//throw std::runtime_error("forcing an error message in ninjaArmy::makePointArmy."); +Com->ninjaCom(ninjaComClass::ninjaNone, "running ninjaArmy::makePointArmy."); + + if( timeList.size() == 0 ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid 'empty' input timeList in ninjaArmy::makePointArmy()"); + throw std::runtime_error("Invalid 'empty' input timeList in ninjaArmy::makePointArmy()"); + } + vector stationList; boost::posix_time::ptime noTime; //interpolate raw data to actual time steps @@ -135,12 +168,14 @@ void ninjaArmy::makePointArmy(std::vector timeList, { stationList = pointInitialization::interpolateFromDisk(demFile, timeList, timeZone); } - + ninjas.resize(timeList.size()); for(unsigned int i=0; i(), momentumFlag); } -/** - * @brief Fetches a DEM using a point. - * - * @param adfPoint a x,y point in WGS 84 longitude, latitude - * @param adfBuff length of a buffer in the x and y directions - * @param units Units of buffer. - * @param dfCellSize Cell size of DEM. - * @param pszDstFile Destination file. - * @param papszOptions Options for fetching DEM. - * @param fetchType Type of DEM to fetch. - * - * - */ -int ninjaArmy::fetchDEMPoint(double * adfPoint,double *adfBuff, const char* units, double dfCellSize, const char * pszDstFile, const char* fetchType, char ** papszOptions){ - if (pszDstFile == NULL) - { - return NINJA_E_INVALID; - } - SURF_FETCH_E retval = SURF_FETCH_E_NONE; - SurfaceFetch * fetcher; - if (strcmp(fetchType, "srtm") == 0){ - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::SRTM_STR,""); - } - #ifdef HAVE_GMTED - else if (strcmp(fetchType, "gmted") == 0){ - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::WORLD_GMTED_STR,""); - } - #endif - else if (strcmp(fetchType, "relief") == 0){ - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::RELIEF_STR,""); - } - else{ - delete fetcher; - return NINJA_E_INVALID; - } - lengthUnits::eLengthUnits ninjaUnits = lengthUnits::getUnit(std::string(units)); - int result = fetcher->FetchPoint(adfPoint, adfBuff, ninjaUnits, dfCellSize, pszDstFile, papszOptions); - if (result != 0) - { - delete fetcher; - return NINJA_E_INVALID; - } - delete fetcher; - return NINJA_SUCCESS; -} -/** - * @brief Fetches a DEM using bounding box. - * - * @param boundsBox Bounding box in the form of north, east, south, west. - * @param fileName Name of DEM file. - * @param resolution Resolution of DEM file. - * @param fetchType Type of DEM file to fetch. - * - */ -int ninjaArmy::fetchDEMBBox(double *boundsBox, const char *fileName, double resolution, const char* fetchType) -{ - SURF_FETCH_E retval = SURF_FETCH_E_NONE; - SurfaceFetch * fetcher; - if (strcmp(fetchType, "srtm") == 0){ - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::SRTM_STR,""); - } - #ifdef HAVE_GMTED - else if (strcmp(fetchType, "gmted") == 0){ - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::WORLD_GMTED_STR,""); - } - #endif - else if (strcmp(fetchType, "relief") == 0){ - fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::RELIEF_STR,""); - } - if (fetcher == NULL) { - return NINJA_E_INVALID; - } - - double northBound = boundsBox[0]; - double eastBound = boundsBox[1]; - double southBound = boundsBox[2]; - double westBound = boundsBox[3]; - int result = fetcher->FetchBoundingBox(boundsBox, resolution, fileName, NULL); - delete fetcher; - if (result != 0) - { - return NINJA_E_INVALID; - } - return NINJA_SUCCESS; - -} - -/** - * @brief Fetches a forecast file from UCAR/THREDDS server. - * - * @param wx_model_type Type of weather model. - * @param numNinjas Number of ninjas. - * @param elevation_file Name of elevation file. - * - * @return Name of forecast file. - */ -const char* ninjaArmy::fetchForecast(const char* wx_model_type, unsigned int numNinjas, const char* elevation_file) -{ - wxModelInitialization *model; - try - { - model = wxModelInitializationFactory::makeWxInitializationFromId(wx_model_type); - std::string forecastFileName = model->fetchForecast(elevation_file, numNinjas-2); - delete model; - char* cstr = new char[forecastFileName.length() + 1]; - std::strcpy(cstr, forecastFileName.c_str()); - return cstr; - } - catch(armyException &e) - { - return "exception"; - } -} /** * @brief Makes an army (array) of ninjas for a weather forecast run. * @@ -344,6 +266,16 @@ const char* ninjaArmy::fetchForecast(const char* wx_model_type, unsigned int num */ void ninjaArmy::makeWeatherModelArmy(std::string forecastFilename, std::string timeZone, std::vector times, bool momentumFlag) { +//Com->ninjaCom(ninjaComClass::ninjaFailure, "forcing an error message in ninjaArmy::makeWeatherModelArmy."); +//throw std::runtime_error("forcing an error message in ninjaArmy::makeWeatherModelArmy."); +Com->ninjaCom(ninjaComClass::ninjaNone, "running ninjaArmy::makeWeatherModelArmy."); + + if( times.size() == 0 ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid 'empty' input times in ninjaArmy::makeWeatherModelArmy()"); + throw std::runtime_error("Invalid 'empty' input times in ninjaArmy::makeWeatherModelArmy()"); + } + wxModelInitialization* model; tz = timeZone; @@ -352,8 +284,8 @@ void ninjaArmy::makeWeatherModelArmy(std::string forecastFilename, std::string t if( strstr( forecastFilename.c_str(), ".csv" ) ){ FILE *fcastList = VSIFOpen( forecastFilename.c_str(), "r" ); if(fcastList == NULL){ - throw std::runtime_error(std::string("Forecast list ") + forecastFilename.c_str() + - std::string(" cannot be opened.")); + Com->ninjaCom(ninjaComClass::ninjaFailure, "Forecast list %s cannot be opened.", forecastFilename.c_str()); + throw std::runtime_error(std::string("Forecast list ") + forecastFilename.c_str() + std::string(" cannot be opened.")); } while(1){ const char* f = CPLReadLine(fcastList); @@ -380,6 +312,8 @@ void ninjaArmy::makeWeatherModelArmy(std::string forecastFilename, std::string t #else ninjas[i] = new ninja(); #endif //NINJAFOAM + + setNinjaCommunication( i, i ); } std::vector timeList = model->getTimeList(timeZone); @@ -405,7 +339,7 @@ void ninjaArmy::makeWeatherModelArmy(std::string forecastFilename, std::string t } catch(armyException &e) { - std::cout << "Bad forecast file, exiting" << endl; + Com->ninjaCom(ninjaComClass::ninjaFailure, "Bad forecast file, exiting"); throw; } std::vector timeList = model->getTimeList(timeZone); @@ -426,6 +360,8 @@ void ninjaArmy::makeWeatherModelArmy(std::string forecastFilename, std::string t #else ninjas[i] = new ninja(); #endif + + setNinjaCommunication( i, i ); } @@ -469,22 +405,20 @@ bool ninjaArmy::startRuns(int numProcessors) //check for duplicate runs before we start the simulations //this is mostly for batch domain avg runs in the GUI and the API - try{ - if(ninjas.size() > 1){ - for(unsigned int i=0; iinput == ninjas[j]->input && - ninjas[i]->get_initializationMethod() == WindNinjaInputs::domainAverageInitializationFlag){ - throw std::runtime_error("Multiple runs were requested with the same input parameters."); - } + if(ninjas.size() > 1) + { + for(unsigned int i=0; iinput == ninjas[j]->input && ninjas[i]->get_initializationMethod() == WindNinjaInputs::domainAverageInitializationFlag) + { + ninjas[j]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Multiple runs were requested with the same input parameters."); + status = false; + throw std::runtime_error("Multiple runs were requested with the same input parameters."); } } } - }catch (exception& e) - { - std::cout << "Exception caught: " << e.what() << endl; - status = false; - throw; } #ifdef NINJAFOAM @@ -500,6 +434,7 @@ bool ninjaArmy::startRuns(int numProcessors) CPLSetConfigOption("TEMP", CPLGetDirname(ninjas[0]->input.dem.fileName.c_str())); int status = NinjaFoam::GenerateFoamDirectory(ninjas[0]->input.dem.fileName); if(status != 0){ + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Error generating the NINJAFOAM directory."); throw std::runtime_error("Error generating the NINJAFOAM directory."); } } @@ -634,6 +569,13 @@ bool ninjaArmy::startRuns(int numProcessors) CPLSetConfigOption( "GDAL_PAM_ENABLED", "ON" ); } + // prep a clean set of kmz output filenames, to be filled before ninjas[i] gets deleted after each run + // stationKmlfilenames is an exception, it is filled by appending the ninjas[0] set of station files, + // which are shared across runs. Appends within a single run don't mess up the ordering like they do across runs. + kmzFilenames.resize(ninjas.size()); + stationKmlFilenames.clear(); + wxModelKmzFilenames.resize(ninjas.size()); + if(ninjas.size() == 1) { //set number of threads for the run @@ -694,25 +636,27 @@ bool ninjaArmy::startRuns(int numProcessors) if(writeFarsiteAtmFile) writeFarsiteAtmosphereFile(); + //setup the run kmz filenames, for C-API calls + setCurrentRunKmzFilenames(0); + }catch (bad_alloc& e) { - std::cout << "Exception bad_alloc caught: " << e.what() << endl; - std::cout << "WindNinja appears to have run out of memory." << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception bad_alloc caught: %s\nWindNinja appears to have run out of memory.", e.what()); status = false; throw; }catch (cancelledByUser& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaNone, "Exception caught: %s", e.what()); status = false; throw; }catch (exception& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); status = false; throw; }catch (...) { - std::cout << "Exception caught: Cannot determine exception type." << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); status = false; throw; } @@ -783,6 +727,9 @@ bool ninjaArmy::startRuns(int numProcessors) ninjas[i]->get_AngFileName(), ninjas[i]->get_CldFileName() ); } + //setup the run kmz filenames, for C-API calls + setCurrentRunKmzFilenames(i); + //delete all but ninjas[0] (ninjas[0] is used to set the output path in the GUI) //need to keep the ninjas for now, if doing a consistent color scale set of outputs if( i != 0 && ninjas[0]->input.googUseConsistentColorScale == false ) @@ -793,21 +740,24 @@ bool ninjaArmy::startRuns(int numProcessors) }catch (bad_alloc& e) { - std::cout << "Exception bad_alloc caught: " << e.what() << endl; - std::cout << "WindNinja appears to have run out of memory." << endl; + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception bad_alloc caught: %s\nWindNinja appears to have run out of memory.", e.what()); status = false; + throw; }catch (cancelledByUser& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaNone, "Exception caught: %s", e.what()); status = false; + throw; }catch (exception& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); status = false; + throw; }catch (...) { - std::cout << "Exception caught: Cannot determine exception type." << endl; + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); status = false; + throw; } } try{ @@ -817,23 +767,22 @@ bool ninjaArmy::startRuns(int numProcessors) }catch (bad_alloc& e) { - std::cout << "Exception bad_alloc caught: " << e.what() << endl; - std::cout << "WindNinja appears to have run out of memory." << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception bad_alloc caught: %s\nWindNinja appears to have run out of memory.", e.what()); status = false; throw; }catch (cancelledByUser& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaNone, "Exception caught: %s", e.what()); status = false; throw; }catch (exception& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); status = false; throw; }catch (...) { - std::cout << "Exception caught: Cannot determine exception type." << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); status = false; throw; } @@ -905,6 +854,9 @@ bool ninjaArmy::startRuns(int numProcessors) ninjas[i]->get_AngFileName(), ninjas[i]->get_CldFileName() ); } + //setup the run kmz filenames, for C-API calls + setCurrentRunKmzFilenames(i); + //delete all but ninjas[0] (ninjas[0] is used to set the output path in the GUI) //need to keep the ninjas for now, if doing a consistent color scale set of outputs if( i != 0 && ninjas[0]->input.googUseConsistentColorScale == false ) @@ -915,6 +867,7 @@ bool ninjaArmy::startRuns(int numProcessors) }catch (bad_alloc& e) { + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception bad_alloc caught: %s\nWindNinja appears to have run out of memory.", e.what()); #ifdef _OPENMP anErrors[omp_get_thread_num()] = STD_BAD_ALLOC_EXC; asMessages[omp_get_thread_num()] = "Exception bad_alloc caught:"; @@ -926,6 +879,7 @@ bool ninjaArmy::startRuns(int numProcessors) #endif }catch (logic_error& e) { + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception logic_error caught: %s", e.what()); #ifdef _OPENMP anErrors[omp_get_thread_num()] = STD_LOGIC_EXC; asMessages[omp_get_thread_num()] = "Exception logic_error caught:"; @@ -937,9 +891,10 @@ bool ninjaArmy::startRuns(int numProcessors) #endif }catch (cancelledByUser& e) { + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaNone, "Exception canceled by user caught: %s", e.what()); #ifdef _OPENMP anErrors[omp_get_thread_num()] = NINJA_CANCEL_USER_EXC; - asMessages[omp_get_thread_num()] = "Exception cacneled by user caught:"; + asMessages[omp_get_thread_num()] = "Exception canceled by user caught:"; asMessages[omp_get_thread_num()] + e.what(); asMessages[omp_get_thread_num()] += "\n"; status = false; @@ -948,6 +903,7 @@ bool ninjaArmy::startRuns(int numProcessors) #endif }catch (badForecastFile& e) { + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception badForecastFile caught: %s", e.what()); #ifdef _OPENMP anErrors[omp_get_thread_num()] = NINJA_BAD_FORECAST_EXC; asMessages[omp_get_thread_num()] = "Exception badForecastFile caught:"; @@ -959,6 +915,7 @@ bool ninjaArmy::startRuns(int numProcessors) #endif }catch (exception& e) { + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); #ifdef _OPENMP anErrors[omp_get_thread_num()] = STD_EXC; asMessages[omp_get_thread_num()] = "Exception caught:"; @@ -970,6 +927,7 @@ bool ninjaArmy::startRuns(int numProcessors) #endif }catch (...) { + ninjas[i]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); #ifdef _OPENMP anErrors[omp_get_thread_num()] = STD_UNKNOWN_EXC; asMessages[omp_get_thread_num()] = "Unknown Exception caught:"; @@ -990,23 +948,22 @@ bool ninjaArmy::startRuns(int numProcessors) }catch (bad_alloc& e) { - std::cout << "Exception bad_alloc caught: " << e.what() << endl; - std::cout << "WindNinja appears to have run out of memory." << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception bad_alloc caught: %s\nWindNinja appears to have run out of memory.", e.what()); status = false; throw; }catch (cancelledByUser& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaNone, "Exception caught: %s", e.what()); status = false; throw; }catch (exception& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); status = false; throw; }catch (...) { - std::cout << "Exception caught: Cannot determine exception type." << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); status = false; throw; } @@ -1157,23 +1114,22 @@ bool ninjaArmy::startRuns(int numProcessors) } }catch (bad_alloc& e) { - std::cout << "Exception bad_alloc caught: " << e.what() << endl; - std::cout << "WindNinja appears to have run out of memory." << endl; + ninjas[ninjas.size()-1]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception bad_alloc caught: %s\nWindNinja appears to have run out of memory.", e.what()); status = false; throw; }catch (cancelledByUser& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[ninjas.size()-1]->input.Com->ninjaCom(ninjaComClass::ninjaNone, "Exception caught: %s", e.what()); status = false; throw; }catch (exception& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[ninjas.size()-1]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); status = false; throw; }catch (...) { - std::cout << "Exception caught: Cannot determine exception type." << endl; + ninjas[ninjas.size()-1]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); status = false; throw; } @@ -1208,26 +1164,25 @@ bool ninjaArmy::startFirstRun() } catch (bad_alloc& e) { - std::cout << "Exception bad_alloc caught: " << e.what() << endl; - std::cout << "WindNinja appears to have run out of memory." << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception bad_alloc caught: %s\nWindNinja appears to have run out of memory.", e.what()); status = false; throw; } catch (cancelledByUser& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaNone, "Exception caught: %s", e.what()); status = false; throw; } catch (exception& e) { - std::cout << "Exception caught: " << e.what() << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); status = false; throw; } catch (...) { - std::cout << "Exception caught: Cannot determine exception type." << endl; + ninjas[0]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); status = false; throw; } @@ -1257,9 +1212,12 @@ void ninjaArmy::writeFarsiteAtmosphereFile() std::string fileroot( CPLGetBasename(ninjas[0]->get_VelFileName().c_str()) ); int stringPos = fileroot.find_last_of('_'); if(stringPos > 0) + { fileroot.erase(stringPos); - else + } else + { throw std::runtime_error("Problem writing FARSITE atmosphere file. The ninja ASCII velocity filename appears to be malformed."); + } //Form atm filename std::string filename( CPLFormFilename(filePath.c_str(), fileroot.c_str(), "atm") ); @@ -1297,61 +1255,270 @@ void ninjaArmy::setAtmFlags() } /*----------------------------------------------------------------------------- - * Ninja Communication Methods + * C-API makeArmy function calls *-----------------------------------------------------------------------------*/ -int ninjaArmy::setNinjaCommunication( const int nIndex, const int RunNumber, - const ninjaComClass::eNinjaCom comType, - char ** papszOptions ) +int ninjaArmy::NinjaMakeDomainAverageArmy( int numNinjas, bool momentumFlag, const double * speedList, const char * speedUnits, const double * directionList, const int * yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const char * timeZone, const double * airTempList, const char * airTempUnits, const double * cloudCoverList, const char * cloudCoverUnits, char ** papszOptions ) { - IF_VALID_INDEX_TRY( nIndex, ninjas, - ninjas[ nIndex ]->set_ninjaCommunication( RunNumber, comType ) ); + try + { + +#ifndef NINJAFOAM + if(momentumFlag == true) + { + throw std::runtime_error("momentumFlag cannot be set to true. WindNinja was not compiled with mass and momentum support."); + } +#endif + + if( numNinjas < 1 ) + { + throw std::runtime_error(CPLSPrintf("Invalid input numNinjas '%d' in ninjaArmy::NinjaMakeDomainAverageArmy()", numNinjas)); + } + + //Get the number of elements in the arrays +/* size_t length1 = sizeof(speedList) / sizeof(speedList[0]); + size_t length2 = sizeof(directionList) / sizeof(directionList[0]); */ +// size_t length1 = sizeof(yearList) / sizeof(yearList[0]); +// size_t length2 = sizeof(monthList) / sizeof(monthList[0]); +// size_t length3 = sizeof(dayList) / sizeof(dayList[0]); +// size_t length4 = sizeof(hourList) / sizeof(hourList[0]); +// size_t length5 = sizeof(minuteList) / sizeof(minuteList[0]); +// size_t length6 = sizeof(airTempList) / sizeof(airTempList[0]); +// size_t length7 = sizeof(cloudCoverList) / sizeof(cloudCoverList[0]); +// +// if(!(length1 == length2 == length3 == length4 == length5 == length6 == length7)) +// { +// throw std::runtime_error("yearList, monthList, dayList, hourList, minuteList, airTempList, and cloudCoverList must be the same length!"); +// + + makeDomainAverageArmy( numNinjas, momentumFlag ); + + int retval = NINJA_E_INVALID; + for(int i=0; ininjaCom(ninjaComClass::ninjaFailure, "ninjaArmy::setInputSpeed() called in ninjaArmy::NinjaMakeDomainAverageArmy() for ninja '%d' failed.", i); + return retval; + } + + retval = setInputDirection( i, directionList[i] ); + if( retval != NINJA_SUCCESS ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "ninjaArmy::setInputDirection() called in ninjaArmy::NinjaMakeDomainAverageArmy() for ninja '%d' failed.", i); + return retval; + } + + retval = setDateTime( i, yearList[i], monthList[i], dayList[i], hourList[i], minuteList[i], 0, timeZone ); + if( retval != NINJA_SUCCESS ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "ninjaArmy::setDateTime() called in ninjaArmy::NinjaMakeDomainAverageArmy() for ninja '%d' failed.", i); + return retval; + } + + retval = setUniAirTemp( i, airTempList[i], std::string( airTempUnits ) ); + if( retval != NINJA_SUCCESS ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "ninjaArmy::setUniAirTemp() called in ninjaArmy::NinjaMakeDomainAverageArmy() for ninja '%d' failed.", i); + return retval; + } + + retval = setUniCloudCover( i, cloudCoverList[i], std::string( cloudCoverUnits ) ); + if( retval != NINJA_SUCCESS ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "ninjaArmy::setUniCloudCover() called in ninjaArmy::NinjaMakeDomainAverageArmy() for ninja '%d' failed.", i); + return retval; + } + } + } + catch( armyException & e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception & e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } + + return NINJA_SUCCESS; } -int ninjaArmy::setNinjaCommunication( const int nIndex, std::string comType, - char ** papszOptions ) +int ninjaArmy::NinjaMakePointArmy( int * yearList, int * monthList, int * dayList, int * hourList, int * minuteList, int timeListSize, char * timeZone, const char ** stationFileNames, int numStationFiles, char * elevationFile, bool matchPointsFlag, bool momentumFlag, char ** papzOptions ) { - int retval = NINJA_E_INVALID; - IF_VALID_INDEX( nIndex, ninjas ) + try { - std::transform( comType.begin(), comType.end(), comType.begin(), ::tolower ); - if( comType == "ninjaCLICom" || comType == "cli" ) + if(momentumFlag == true) { - ninjas[ nIndex ]->set_ninjaCommunication - ( nIndex, ninjaComClass::ninjaCLICom ); - retval = NINJA_SUCCESS; + throw std::runtime_error("The momentum solver is not available for use with Point Initialization runs."); } - else if( comType == "ninjaQuietCom" || comType == "quiet" ) + + if( timeListSize < 1 ) { - ninjas[ nIndex ]->set_ninjaCommunication - ( nIndex, ninjaComClass::ninjaQuietCom ); - retval = NINJA_SUCCESS; + throw std::runtime_error(CPLSPrintf("Invalid input timeListSize '%d' in ninjaArmy::NinjaMakePointArmy()", timeListSize)); } - else + if( numStationFiles < 1 ) { - retval = NINJA_E_INVALID; + throw std::runtime_error(CPLSPrintf("Invalid input numStationFiles '%d' in ninjaArmy::NinjaMakePointArmy()", numStationFiles)); + } + + wxStation::SetStationFormat(wxStation::newFormat); + + std::vector timeList; + for(size_t i=0; i < timeListSize; i++) + { + 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))); } + + std::vector sFiles; + for (int i = 0; i < numStationFiles; i++) + { + sFiles.emplace_back(stationFileNames[i]); + } + pointInitialization::storeFileNames(sFiles); + + makePointArmy( timeList, std::string(timeZone), sFiles[0], std::string(elevationFile), matchPointsFlag, momentumFlag ); } - return retval; + catch( armyException & e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception & e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } + + return NINJA_SUCCESS; } -#ifdef NINJA_GUI -int ninjaArmy::setNinjaComNumRuns( const int nIndex, const int RunNumber, - char ** papszOptions ) +int ninjaArmy::NinjaMakeWeatherModelArmy( const char * forecastFilename, const char * timeZone, const char** inputTimeList, int size, bool momentumFlag, char ** papszOptions ) { - IF_VALID_INDEX_TRY( nIndex, ninjas, - ninjas[ nIndex ]->set_ComNumRuns( RunNumber ) ); + try + { + +#ifndef NINJAFOAM + if(momentumFlag == true) + { + throw std::runtime_error("momentumFlag cannot be set to true. WindNinja was not compiled with mass and momentum support."); + } +#endif + + if( size < 1 ) + { + throw std::runtime_error(CPLSPrintf("Invalid input size '%d' in ninjaArmy::NinjaMakeWeatherModelArmy()", size)); + } + + wxModelInitialization *model = wxModelInitializationFactory::makeWxInitialization(std::string(forecastFilename)); + std::vector fullTimeList = model->getTimeList(std::string(timeZone)); + std::vector timeList; + + for(int i = 0; i < fullTimeList.size(); i++) + { + for(int j = 0; j < size; j++) + { + std::string time1 = fullTimeList[i].to_string(); + std::string time2(inputTimeList[j]); + if(time1 == time2) + { + timeList.push_back(fullTimeList[i]); + } + } + } + + makeWeatherModelArmy( std::string( forecastFilename ), std::string( timeZone ), timeList, momentumFlag ); + } + catch( armyException & e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception & e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } + + return NINJA_SUCCESS; +} + +/*----------------------------------------------------------------------------- + * Ninja Communication Methods + *-----------------------------------------------------------------------------*/ + +int ninjaArmy::setNinjaComMessageHandler( ninjaComMessageHandler pMsgHandler, void *pUser, + char ** papszOptions ) +{ + try + { + Com->set_messageHandler(pMsgHandler, pUser); + } + catch( ... ) + { + std::cerr << "CRITICAL: ninjaArmy level ninjaComMessageHandler not set. Messages will NOT be delivered." << std::endl; + return NINJA_E_INVALID; + } + return NINJA_SUCCESS; +} + +int ninjaArmy::setNinjaMultiComStream( FILE* stream, + char ** papszOptions ) +{ + try + { + Com->multiStream = stream; + } + catch( ... ) + { + std::cerr << "ERROR: ninjaArmy level ninjaCom multiStream FILE pointer not set." << std::endl; + return NINJA_E_INVALID; + } + return NINJA_SUCCESS; } -ninjaComClass * ninjaArmy::getNinjaCom( const int nIndex, char ** papszOptions ) +int ninjaArmy::setNinjaCommunication( const int nIndex, const int RunNumber, + char ** papszOptions ) { + int retval = NINJA_E_INVALID; IF_VALID_INDEX( nIndex, ninjas ) { - return ninjas[ nIndex ]->get_Com(); + try + { + ninjas[ nIndex ]->set_ninjaCommunication( Com ); + ninjas[ nIndex ]->set_ninjaComRunNumber( RunNumber ); + retval = NINJA_SUCCESS; + } + catch( std::exception &e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s, Failed to set ninjas[%d] level ninjaCom", e.what(), RunNumber); + retval = NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Failed to set ninjas[%d] level ninjaCom", RunNumber); + retval = NINJA_E_INVALID; + } } - return NULL; //if not valid index + return retval; } -#endif //NINJA-GUI /*----------------------------------------------------------------------------- * Ninja Speed Testing Methods @@ -1611,6 +1778,11 @@ int ninjaArmy::setInitializationMethod( const int nIndex, #endif else { +#ifdef NINJAFOAM + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid input initialization_method '%s' in ninjaArmy::setInitializationMethod()\nchoices are: 'domain_average', 'domainAverage', 'domainaverageinitializationflag', 'domain',\n'point', 'pointinitializationflag', 'wxmodel', 'wxmodelinitializationflag', 'griddedInitialization'", method.c_str()); +#else + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid input initialization_method '%s' in ninjaArmy::setInitializationMethod()\nchoices are: 'domain_average', 'domainAverage', 'domainaverageinitializationflag', 'domain',\n'point', 'pointinitializationflag', 'wxmodel', 'wxmodelinitializationflag', 'griddedInitialization', 'foamDomainAverageInitialization'", method.c_str()); +#endif retval = NINJA_E_INVALID; } } @@ -1664,8 +1836,15 @@ int ninjaArmy::setInputWindHeight( const int nIndex, const double height, ninjas[ nIndex ]->set_inputWindHeight( height, lengthUnits::getUnit( units ) ); retval = NINJA_SUCCESS; } - catch( std::logic_error &e ) + /*catch( std::range_error &e ) + { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + retval = NINJA_E_INVALID; + }*/ + //catch( std::logic_error &e ) + catch( std::exception &e ) { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); retval = NINJA_E_INVALID; } } @@ -1696,14 +1875,30 @@ int ninjaArmy::setOutputWindHeight( const int nIndex, const double height, ninjas[ nIndex ]->set_outputWindHeight( height, lengthUnits::getUnit( units ) ); retval = NINJA_SUCCESS; } - catch( std::logic_error &e ) + /*catch( std::range_error &e ) { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + retval = NINJA_E_INVALID; + }*/ + //catch( std::logic_error &e ) + catch( std::exception &e ) + { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); retval = NINJA_E_INVALID; } } return retval; } +//int ninjaArmy::setOutputWindHeight( const int nIndex, const double height, +// std::string units, char ** papszOptions ) +//{ +// //Parse units so it contains only lowercase letters +// std::transform( units.begin(), units.end(), units.begin(), ::tolower ); +// +// IF_VALID_INDEX_TRY( nIndex, ninjas, ninjas[ nIndex ]->set_outputWindHeight( height, lengthUnits::getUnit( units ) ) ); +//} + int ninjaArmy::setOutputSpeedUnits( const int nIndex, const velocityUnits::eVelocityUnits units, char ** papszOptions ) { @@ -1724,6 +1919,7 @@ int ninjaArmy::setOutputSpeedUnits( const int nIndex, std::string units, char ** } catch( std::logic_error &e ) { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); retval = NINJA_E_INVALID; } } @@ -1849,6 +2045,7 @@ int ninjaArmy::setUniVegetation( const int nIndex, std::string vegetation, } else { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid input vegation '%s' in ninjaArmy::setUniVegetation()\nchoices are: 'grass', 'g', 'brush', 'b', 'trees', 't'", vegetation.c_str()); retval = NINJA_E_INVALID; } } @@ -1863,8 +2060,39 @@ int ninjaArmy::setUniVegetation( const int nIndex, char ** papszOptions ) int ninjaArmy::setMeshResolutionChoice( const int nIndex, const std::string choice, char ** papszOptions ) { +#ifndef NINJAFOAM IF_VALID_INDEX_TRY( nIndex, ninjas, ninjas[ nIndex ]->set_meshResChoice( choice ) ); +#else + int retval = NINJA_E_INVALID; + IF_VALID_INDEX( nIndex, ninjas ) + { + try + { + if( ninjas[ nIndex ]->identify() == "ninja" ) + { + ninjas[ nIndex ]->set_meshResChoice( choice ); + retval = NINJA_SUCCESS; + } else if( ninjas[ nIndex ]->identify() == "ninjafoam" ) + { + ninjas[ nIndex ]->set_MeshCount( ninja::get_eNinjafoamMeshChoice(choice) ); + retval = NINJA_SUCCESS; + } + else + { + throw std::invalid_argument( "invalid ninja->identify() '" + choice + + "' in ninjaArmy::setMeshResolutionChoice()" + + "\nshould be: 'ninja' or 'ninjafoam'" ); + } + } + catch( std::logic_error &e ) + { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + retval = NINJA_E_INVALID; + } + } + return retval; +#endif } int ninjaArmy::setMeshResolutionChoice( const int nIndex, const Mesh::eMeshChoice choice, @@ -1896,6 +2124,7 @@ int ninjaArmy::setMeshResolution( const int nIndex, const double resolution, } catch( std::logic_error &e ) { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); retval = NINJA_E_INVALID; } } @@ -2148,6 +2377,7 @@ int ninjaArmy::setGoogResolution( const int nIndex, const double resolution, } catch( std::logic_error &e ) { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); retval = NINJA_E_INVALID; } } @@ -2180,8 +2410,8 @@ int ninjaArmy::setGoogSpeedScaling } else { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid speed scale '%s' in ninjaArmy::setGoogSpeedScaling()\nchoices are: 'equal_color', 'color', 'equal_interval', 'interval'", scaling.c_str()); retval = NINJA_E_INVALID; - } } return retval; @@ -2222,6 +2452,7 @@ int ninjaArmy::setShpResolution( const int nIndex, const double resolution, } catch( std::logic_error &e ) { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); retval = NINJA_E_INVALID; } } @@ -2276,6 +2507,7 @@ int ninjaArmy::setAsciiResolution( const int nIndex, const double resolution, } catch( std::logic_error &e ) { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); retval = NINJA_E_INVALID; } } @@ -2327,6 +2559,7 @@ int ninjaArmy::setPDFResolution( const int nIndex, const double resolution, } catch( std::logic_error &e ) { + ninjas[ nIndex ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); retval = NINJA_E_INVALID; } } @@ -2359,6 +2592,17 @@ std::string ninjaArmy::getOutputPath( const int nIndex, char ** papszOptions ) } return std::string(""); } + +int ninjaArmy::getRunKmzFilenames( std::vector& kmzFilenamesStr, std::vector& stationKmlFilenamesStr, + std::vector& wxModelKmzFilenamesStr, char ** papszOptions ) +{ + kmzFilenamesStr = kmzFilenames; + stationKmlFilenamesStr = stationKmlFilenames; + wxModelKmzFilenamesStr = wxModelKmzFilenames; + + return NINJA_SUCCESS; +} + /** * @brief Reset the army in able to reinitialize needed parameters * @@ -2384,6 +2628,43 @@ void ninjaArmy::cancelAndReset() reset(); } +void ninjaArmy::setCurrentRunKmzFilenames(int runNumber) +{ + kmzFilenames[runNumber] = ninjas[runNumber]->input.kmzFile; + + // assume all the other stations across all the other stations storage, are the exact same list as that of the first station + // SHOULD be true, seems like the idea of the storage was to make sure each station had access to the same copy of data, so a form of SHARED storage + // still, it's one of the quirkiest code setups that I've seen in a while + if(runNumber == 0) + { + if(ninjas[runNumber]->input.stations.size() == 0) + { + stationKmlFilenames.push_back( "" ); + } else + { + if(ninjas[runNumber]->input.stations[runNumber].stationKmlNames.size() == 0) + { + stationKmlFilenames.push_back( "" ); + } else + { + for(int j = 0; j < ninjas[runNumber]->input.stations[runNumber].stationKmlNames.size(); j++) + { + stationKmlFilenames.push_back( ninjas[runNumber]->input.stations[runNumber].stationKmlNames[j] ); + } + } + } + } + + // oh, this one is set to "!set" for non-wxModel runs, the storage of this filename always exists for each ninjas[i] + if(ninjas[runNumber]->input.wxModelKmzFile == "!set") + { + wxModelKmzFilenames[runNumber] = ""; + } else + { + wxModelKmzFilenames[runNumber] = ninjas[runNumber]->input.wxModelKmzFile; + } +} + void ninjaArmy::initLocalData(void) { const char *pszTmp = NULL; diff --git a/src/ninja/ninjaArmy.h b/src/ninja/ninjaArmy.h index 402e8aaea..869f83d19 100644 --- a/src/ninja/ninjaArmy.h +++ b/src/ninja/ninjaArmy.h @@ -73,7 +73,12 @@ extern boost::local_time::tz_database globalTimeZoneDB; if( i >= 0 && i < iterable.size() ) #define CHECK_VALID_INDEX(i,iterable) \ - if( i < 0 || i >= iterable.size() ) throw std::runtime_error("invalid index"); + if( i < 0 || i >= iterable.size() ) \ + { \ + std::cout << "here1" << std::endl; \ + ninjas[ 0 ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: invalid index %d", i); \ + throw std::runtime_error("invalid index"); \ + } \ /* * * Macro IF_VALID_INDEX_DO is a boiler plate for most of the ninjaArmy functions. @@ -82,7 +87,8 @@ extern boost::local_time::tz_database globalTimeZoneDB; * 'func' is located inside a try-catch statement block so upon a thrown exception * it is handled and NINJA_E_INVALID is returned. Otherwise, NINJA_SUCCESS is returned. * */ -#ifdef C_API +//#ifdef C_API +#ifndef C_API #define IF_VALID_INDEX_TRY( i, iterable, func ) \ if( i >= 0 && i < iterable.size() ) \ { \ @@ -90,24 +96,39 @@ extern boost::local_time::tz_database globalTimeZoneDB; { \ func; \ } \ + catch( exception& e ) \ + { \ + std::cout << "here2a" << std::endl; \ + ninjas[ i ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); \ + return NINJA_E_INVALID; \ + } \ catch( ... ) \ { \ + std::cout << "here2b" << std::endl; \ + ninjas[ i ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); \ return NINJA_E_INVALID; \ } \ return NINJA_SUCCESS; \ } \ + std::cout << "here3a" << std::endl; \ + ninjas[ 0 ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: invalid index %d", i); \ return NINJA_E_INVALID; #else #define IF_VALID_INDEX_TRY( i, iterable, func ) \ if( i >= 0 && i < iterable.size() ) \ { \ func; \ - return NINJA_SUCCESS; \ + return NINJA_SUCCESS; \ } \ + std::cout << "here3b" << std::endl; \ + ninjas[ 0 ]->input.Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: invalid index %d", i); \ return NINJA_E_INVALID; #endif -//#include "ninjaCom.h" +#include "callbackFunctions.h" + +#include "ninjaCom.h" + /** * Class used for doing multiple WindNinja runs. */ @@ -121,7 +142,7 @@ class ninjaArmy ninjaArmy& operator= (ninjaArmy const& A); - //ninjaComClass *Com; + ninjaComClass *Com; // pointer to the ninjaArmy level com handler enum eWxModelType{ ncepNdfd, @@ -138,17 +159,21 @@ class ninjaArmy std::string demFile,bool matchPoints, bool momentumFlag ); void makeWeatherModelArmy(std::string forecastFilename, std::string timeZone, bool momentumFlag); - void makeWeatherModelArmy(std::string forecastFilename, std::string timeZone, std::vector times, bool momentumFlag); + void makeWeatherModelArmy(std::string forecastFilename, std::string timeZone, std::vector timeList, bool momentumFlag); std::vector toBoostLocal(std::vector in, std::string timeZone); - int fetchDEMPoint(double * adfPoint, double *adfBuff, const char* units, double dfCellSize, const char * pszDstFile, const char* fetchType, char ** papszOptions); - int fetchDEMBBox(double *boundsBox, const char *fileName, double resolution, const char* fetchType); - const char* fetchForecast(const char* wx_model_type, unsigned int forecastDuration, const char* elevation_file); void set_writeFarsiteAtmFile(bool flag); bool startRuns(int numProcessors); bool startFirstRun(); int ninjaInitialize(); + /*----------------------------------------------------------------------------- + * C-API makeArmy function calls + *-----------------------------------------------------------------------------*/ + int NinjaMakeDomainAverageArmy( int numNinjas, bool momentumFlag, const double * speedList, const char * speedUnits, const double * directionList, const int * yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const char * timeZone, const double * airTempList, const char * airTempUnits, const double * cloudCoverList, const char * cloudCoverUnits, char ** papszOptions=NULL ); + int NinjaMakePointArmy( int * yearList, int * monthList, int * dayList, int * hourList, int * minuteList, int timeListSize, char * timeZone, const char ** stationFileNames, int numStationFiles, char * elevationFile, bool matchPointsFlag, bool momentumFlag, char ** papzOptions=NULL ); + int NinjaMakeWeatherModelArmy( const char * forecastFilename, const char * timeZone, const char** inputTimeList, int size, bool momentumFlag, char ** papzOptions=NULL ); + /** * \brief Return the number of ninjas in the army * @@ -161,38 +186,36 @@ class ninjaArmy /*----------------------------------------------------------------------------- * Ninja Communication Methods *-----------------------------------------------------------------------------*/ + /** - * \brief Initialize the ninja communication of a ninja + * \brief Set a ninjaComMessageHandler callback function to the ninjaArmy level ninjaCom * - * \param nIndex index of a ninja - * \param RunNumber number of runs - * \param comType type of communication + * \param pMsgHandler A pointer to a ninjaComMessageHandler callback function. + * \param pUser A pointer to the object or context associated with the callback function. * \return errval Returns NINJA_SUCCESS upon success */ - int setNinjaCommunication( const int nIndex, const int RunNumber, - const ninjaComClass::eNinjaCom comType, - char ** papszOptions = NULL ); + int setNinjaComMessageHandler( ninjaComMessageHandler pMsgHandler, void *pUser, + char ** papszOptions = NULL); - int setNinjaCommunication( const int nIndex, std::string comType, - char ** papszOptions = NULL); -#ifdef NINJA_GUI /** - * \brief Set the number of runs for a ninjaCom + * \brief Set a ninjaCom multi-stream FILE handle to the ninjaArmy level ninjaCom * - * \param nIndex index of a ninja - * \param RunNumber number of runs + * \param stream A pointer to a multi-stream FILE handle/stream. * \return errval Returns NINJA_SUCCESS upon success */ - int setNinjaComNumRuns( const int nIndex, const int RunNumber, - char ** papszOptions=NULL ); + int setNinjaMultiComStream( FILE* stream, + char ** papszOptions = NULL); + /** - * \brief Returns the ninjaCom for a ninja + * \brief Set the ninjaCom of a ninja, using the ninjaArmy level ninjaCom + * and set the ninja and ninjaCom runNumber of a ninja * * \param nIndex index of a ninja - * \return com the ninjaComClass of a ninja + * \param RunNumber the specific ninja/simulation run number + * \return errval Returns NINJA_SUCCESS upon success */ - ninjaComClass * getNinjaCom( const int nIndex, char ** papszOptions=NULL ); -#endif //NINJA_GUI + int setNinjaCommunication( const int nIndex, const int RunNumber, + char ** papszOptions = NULL); /*----------------------------------------------------------------------------- * Ninja speed testing Methods @@ -1334,13 +1357,30 @@ class ninjaArmy * \return path String of the path, which is empty if no output is set */ std::string getOutputPath( const int nIndex, char ** papszOptions=NULL ); + + /** + * \brief Returns the output kmz filenames of each ninja, as well as the station kml filenames + * and the weather model filenames of each ninja if they were created for the run. + * + * \param kmzFilenames The output kmz filenames of each ninja, to be filled. + * \param stationKmlFilenames The station kml filenames SHARED across each ninja, to be filled. Runs without station kml file output use "" for the station kml filenames. + * \param weatherModelKmzFilenames The weather model kmz filenames of each ninja, to be filled. Runs without weather model kmz file output use "" for the weather model kmz filenames. + * \return errval Returns NINJA_SUCCESS upon success. + */ + int getRunKmzFilenames( std::vector& kmzFilenamesStr, std::vector& stationKmlFilenamesStr, + std::vector& wxModelKmzFilenamesStr, char ** papszOptions=NULL ); + /*----------------------------------------------------------------------------- * Termination Section *-----------------------------------------------------------------------------*/ void reset(); void cancel(); void cancelAndReset(); - + + std::vector kmzFilenames; + std::vector stationKmlFilenames; + std::vector wxModelKmzFilenames; + GDALDatasetH hSpdMemDS; //in-memory dataset for GTiff output writer GDALDatasetH hDirMemDS; //in-memory dataset for GTiff output writer GDALDatasetH hDustMemDS; //in-memory dataset for GTiff output writer @@ -1354,6 +1394,8 @@ class ninjaArmy void writeFarsiteAtmosphereFile(); void setAtmFlags(); + void setCurrentRunKmzFilenames(int runNumber); + /* ** This function initializes various data for the lifetime of the ** ninjaArmy. This should be used for various tasks, such as downloading diff --git a/src/ninja/ninjaCom.cpp b/src/ninja/ninjaCom.cpp index 05110e75a..6de27593e 100644 --- a/src/ninja/ninjaCom.cpp +++ b/src/ninja/ninjaCom.cpp @@ -30,38 +30,125 @@ #include "ninjaCom.h" /** -* Constructor for ninjaComClass. -* @return +* @brief Default constructor. +* */ ninjaComClass::ninjaComClass() { - fpLog = stdout; - lastMsg = NULL; - runNumber = NULL; - comType = NULL; + printRunNumber = true; + runNumber = -9999; + + printMaxErrors = false; errorCount = 0; nMaxErrors = 10; - printSolverProgress = true; + progressWeight = 1.0; -#ifdef NINJA_GUI - progressMultiplier = 0; - nRuns = 0; - runProgress = 0; -#endif + printSolverProgress = true; + printLastMsg = false; + lastMsg[0] = '\0'; + printToMsgHandler = false; + pMsgHandler = nullptr; + pMsgUser = nullptr; + + printLogFile = false; + fpLog = stdout; + fpErr = stderr; + multiStream = NULL; } /** -* Destructor for ninjaComClass. -* @return +* @brief Destructor. +* */ ninjaComClass::~ninjaComClass() { } +/** +* @brief Copy constructor. +* +* @param a ninjaComClass object to be copied. +*/ +ninjaComClass::ninjaComClass(const ninjaComClass& A) +{ + printRunNumber = A.printRunNumber; + runNumber = A.runNumber; + + printMaxErrors = A.printMaxErrors; + errorCount = A.errorCount; + nMaxErrors = A.nMaxErrors; + + progressWeight = A.progressWeight; + + printSolverProgress = A.printSolverProgress; + + printLastMsg = A.printLastMsg; + strcpy( lastMsg, A.lastMsg ); + + printToMsgHandler = A.printToMsgHandler; + pMsgHandler = A.pMsgHandler; + pMsgUser = A.pMsgUser; + + printLogFile = A.printLogFile; + fpLog = A.fpLog; + fpErr = A.fpErr; + multiStream = A.multiStream; +} + +/** +* @brief Equals operator. +* +* @param a ninjaComClass object to set equal to. +* @return A reference to a ninjaComClass object equal to the one on the right-hand side. +*/ +ninjaComClass& ninjaComClass::operator=(const ninjaComClass &A) +{ + if(&A != this) + { + printRunNumber = A.printRunNumber; + runNumber = A.runNumber; + + printMaxErrors = A.printMaxErrors; + errorCount = A.errorCount; + nMaxErrors = A.nMaxErrors; + + progressWeight = A.progressWeight; + + printSolverProgress = A.printSolverProgress; + + printLastMsg = A.printLastMsg; + strcpy( lastMsg, A.lastMsg ); + + printToMsgHandler = A.printToMsgHandler; + pMsgHandler = A.pMsgHandler; + pMsgUser = A.pMsgUser; + + printLogFile = A.printLogFile; + fpLog = A.fpLog; + fpErr = A.fpErr; + multiStream = A.multiStream; + } + return *this; +} + +void ninjaComClass::set_messageHandler(ninjaComMessageHandler pMessageHandler, void *pUser) +{ + if( pMessageHandler == NULL || pUser == NULL ) + { + fprintf(stderr, "CRITICAL: ninjaComClass::set_messageHandler(), input ninjaComMessageHandler pMsgHandler and pMsgUser are NULL\n"); + fflush(stderr); + return; + } + + printToMsgHandler = true; + pMsgHandler = pMessageHandler; + pMsgUser = pUser; +} + /** * Function to turn off solver progress messages. * Note that "matching" progress messages will still @@ -73,473 +160,246 @@ void ninjaComClass::noSolverProgress() } /** -* Communication function used to pass messages from the program to different ninjaComHandler() functions. -* @param eMsg Type of message to be passed. See msgType for available types. -* @param Message to be passed, using string formatting (like a printf() statement). +* Communication function used to pass messages from the program to the ninjaComDispatchMessage() function, +* parsing the printf() %f, %d, %s, etc style syntax of the message into a fully built string. +* @param eMsgType Type of message to be passed. See msgType for available types. +* @param fmt Message to be passed, using string formatting (like a printf() statement). */ -void ninjaComClass::ninjaCom(msgType eMsg, const char *fmt, ...) +void ninjaComClass::ninjaCom(msgType eMsgType, const char *fmt, ...) { va_list args; // Expand the error message va_start(args, fmt); - ninjaComV(eMsg, fmt, args); + ninjaComV(eMsgType, fmt, args); va_end(args); } /** * This is an intermediate function that parses the args...? -* @param eMsg Type of message to be passed. See msgType for available types. +* @param eMsgType Type of message to be passed. See msgType for available types. * @param fmt Message to be passed, using string formatting (like a printf() statement). * @param args Arguments list for fmt. */ -void ninjaComClass::ninjaComV(msgType eMsg, const char *fmt, va_list args) +void ninjaComClass::ninjaComV(msgType eMsgType, const char *fmt, va_list args) { char ninjaMsg[NINJA_MSG_SIZE] = ""; vsnprintf(ninjaMsg, NINJA_MSG_SIZE-2, fmt, args); - ninjaComHandler(eMsg, ninjaMsg); + ninjaComDispatchMessage(eMsgType, ninjaMsg); } -//void ninjaComClass::initializeNinjaCom(char *LastMsg, int* RunNumber, eNinjaCom* ComType) -//{ -// lastMsg = LastMsg; -// runNumber = RunNumber; -// comType = ComType; -//} - - -//********************************************************************** -// ninjaDefaultComHandler() -//********************************************************************** /** -* Communication handler for "Default" WindNinja simulations. Prints everything to stdout. Normally used for command line type runs. -* @param eMsg Type of message to be passed. See msgType for available types. -* @param ninjaComMsg Message to be printed. Comes from ninjaComV and ninjaCom. +* Communication dispatcher for WindNinja simulations. Takes an input message and prints/passes it. +* The place the message is printed or passed to (or if the message is ignored) depends on the input message type, +* whether a ninjaComMessageHandler and/or a multi-stream FILE and/or various FILE streams are set, +* and other pre-set ninjaCom settings +* +* @param eMsgType Type of message to be passed. See msgType for available types. +* @param ninjaComMsg Message to be printed. Comes from ninjaComV and ninjaCom. */ -void ninjaDefaultComHandler::ninjaComHandler(msgType eMsg, const char *ninjaComMsg) +void ninjaComClass::ninjaComDispatchMessage(msgType eMsgType, const char *ninjaComMsg) { - fpLog = stdout; //print to standard out - bool printRunNum = true; //flag to determine if thread number should be printed at beginning of message + char msg[NINJA_MSG_SIZE]; // Declare a character array to store the result of sprintf, for printing + char runPartMsg[10]; // this SHOULD be enough for the "Run %d:" part of the msg - if(printRunNum == true) //if run number should be printed at beginning of message + if( printToMsgHandler == false || multiStream != NULL ) { - if (eMsg==ninjaFailure || eMsg==ninjaFatal) + if( runNumber == -9999 ) { - errorCount++; - if (errorCount > nMaxErrors && nMaxErrors > 0) - { - if(errorCount == nMaxErrors+1) - { - fprintf(fpLog, "Run %d: More than %d errors have been reported. " - "No more will be reported from now on.\n", - *runNumber, nMaxErrors); - } - return; - } - } - - if(eMsg == ninjaNone) //None - fprintf(fpLog, "Run %d: %s\n", *runNumber, ninjaComMsg); -#ifdef NINJA_DEBUG - else if(eMsg == ninjaDebug) //Debug - fprintf(fpLog, "Run %d: %s\n", *runNumber, ninjaComMsg); -#endif //NINJA_DEBUG - else if(eMsg == ninjaSolverProgress) //Solver progress (%complete) - { if(printSolverProgress){ - if(atoi(ninjaComMsg) > 99){ - fprintf(fpLog, "Run %d (solver): 99%% complete\n", *runNumber); - } - else - fprintf(fpLog, "Run %d (solver): %d%% complete\n", *runNumber, atoi(ninjaComMsg)); - } - } - else if(eMsg == ninjaOuterIterProgress) //Solver progress for outer matching iterations (%complete) - fprintf(fpLog, "Run %d (matching): %d%% complete\n", *runNumber, atoi(ninjaComMsg)); - else if(eMsg == ninjaWarning) //Warnings - fprintf(fpLog, "\nRun %d (warning): %s\n", *runNumber, ninjaComMsg); - else if(eMsg == ninjaFailure) //Failures (ie errors) - fprintf(fpLog, "\nRun %d (ERROR): %s\n", *runNumber, ninjaComMsg); - else if(eMsg == ninjaFatal) //Failures (probably fatal) - fprintf(fpLog, "\nRun %d (ERROR): %s\n", *runNumber, ninjaComMsg); - - }else{ //if run number should NOT be printed at beginning of message - - if (eMsg==ninjaFailure || eMsg==ninjaFatal) - { - errorCount++; - if (errorCount > nMaxErrors && nMaxErrors > 0) - { - if(errorCount == nMaxErrors+1) - { - fprintf(fpLog, "More than %d errors have been reported. " - "No more will be reported from now on.\n", - nMaxErrors); - } - return; - } + return; } + } - if(eMsg == ninjaNone) //None - fprintf(fpLog, "%s\n", ninjaComMsg); -#ifdef NINJA_DEBUG - else if(eMsg == ninjaDebug) //Debug - fprintf(fpLog, "%s\n", ninjaComMsg); -#endif //NINJA_DEBUG - else if(eMsg == ninjaSolverProgress) //Solver progress (%complete) + if( printLogFile == true ) + { + fpLog = fopen("ninja.log", "w+"); + if( fpLog == NULL ) { - if(printSolverProgress) - fprintf(fpLog, "Solver: %d%% complete\n", atoi(ninjaComMsg)); + return; } - else if(eMsg == ninjaOuterIterProgress) //Solver progress for outer matching iterations (%complete) - fprintf(fpLog, "Solver (matching): %d%% complete\n", atoi(ninjaComMsg)); - else if(eMsg == ninjaWarning) //Warnings - fprintf(fpLog, "\nWarning: %s\n", ninjaComMsg); - else if(eMsg == ninjaFailure) //Failures (ie errors) - fprintf(fpLog, "\nERROR: %s\n", ninjaComMsg); - else if(eMsg == ninjaFatal) //Failures (probably fatal) - fprintf(fpLog, "\nERROR: %s\n", ninjaComMsg); } -} + // for now, just send fpErr/stderr to stdout + //fpErr = stderr; + fpErr = stdout; -//********************************************************************** -// ninjaQuietComHandler() -//********************************************************************** -/** -* Communication handler for "Quiet" WindNinja simulations. Only prints ninjaFailure and ninjaFatal messages. Prints everything to stdout. -* @param eMsg Type of message to be passed. See msgType for available types. -* @param ninjaComMsg Message to be printed. Comes from ninjaComV and ninjaCom. -*/ -void ninjaQuietComHandler::ninjaComHandler(msgType eMsg, const char *ninjaComMsg) -{ - if(eMsg==ninjaFailure || eMsg==ninjaFatal) + // for now, always assume printRunNumber has been already been set ahead of time, where the default value is always true + + // for now, always assume printMaxErrors has been already been set ahead of time, where the default value is always false, to print all errors + + runPartMsg[0] = '\0'; + if( printRunNumber == true ) { - fpLog = stdout; //print to standard out + sprintf( runPartMsg, "Run %d: ", runNumber ); + } - if (eMsg==ninjaFailure || eMsg==ninjaFatal) + if( printMaxErrors == true ) + { + if( eMsgType == ninjaFailure || eMsgType == ninjaFatal ) { errorCount++; - if (errorCount > nMaxErrors && nMaxErrors > 0) + if( errorCount > nMaxErrors && nMaxErrors > 0 ) { - if(errorCount == nMaxErrors+1) + if( errorCount == nMaxErrors+1 ) { - fprintf(fpLog, "More than %d errors have been reported. " - "No more will be reported from now on.\n", - nMaxErrors); + sprintf( msg, "%sMore than %d errors have been reported. " + "No more will be reported from now on.\n", + runPartMsg, nMaxErrors ); + + fprintf(fpErr, "%s", msg); + + if( multiStream != NULL ) + { + fprintf(multiStream, "%s", msg); + fflush(multiStream); + } + + if( printToMsgHandler == true ) + { + pMsgHandler(msg, pMsgUser); + } } return; } - } - - if(eMsg == ninjaFailure) //Failures (ie errors) - fprintf(fpLog, "\nERROR: %s\n", ninjaComMsg); - else if(eMsg == ninjaFatal) //Failures (probably fatal) - fprintf(fpLog, "\nERROR: %s\n", ninjaComMsg); - } -} + } // if( eMsgType == ninjaFailure || eMsgType == ninjaFatal ) + } // if( printMaxErrors == true ) + if(eMsgType == ninjaNone) //None + { + sprintf( msg, "%s%s\n", runPartMsg, ninjaComMsg ); -//********************************************************************** -// ninjaLoggingComHandler() -//********************************************************************** -/** -* Communication handler that prints everything out to a file called ninja.log. -* @param eMsg Type of message to be passed. See msgType for available types. -* @param ninjaComMsg Message to be printed. Comes from ninjaComV and ninjaCom. -*/ -void ninjaLoggingComHandler::ninjaComHandler(msgType eMsg, const char *ninjaComMsg) -{ + fprintf(fpLog, "%s", msg); - fpLog = fopen("ninja.log", "w+"); - if(fpLog==NULL) - return; + if( multiStream != NULL ) + { + fprintf(multiStream, "%s", msg); + fflush(multiStream); + } - if(eMsg == ninjaNone) //None - fprintf(fpLog, "%s\n", ninjaComMsg); -#ifdef NINJA_DEBUG - else if(eMsg == ninjaDebug) //Debug - fprintf(fpLog, "%s\n", ninjaComMsg); -#endif //NINJA_DEBUG - else if(eMsg == ninjaSolverProgress) //Solver progress (%complete) - { - if(printSolverProgress) - fprintf(fpLog, "Solver: %d%% complete\n", atoi(ninjaComMsg)); + if( printToMsgHandler == true ) + { + pMsgHandler(msg, pMsgUser); + } } - else if(eMsg == ninjaOuterIterProgress) //Solver progress for outer matching iterations (%complete) - fprintf(fpLog, "Solver (matching): %d%% complete\n", atoi(ninjaComMsg)); - else if(eMsg == ninjaWarning) //Warnings - fprintf(fpLog, "\nWarning: %s\n", ninjaComMsg); - else if(eMsg == ninjaFailure) //Failures (ie errors) - fprintf(fpLog, "\nERROR: %s\n", ninjaComMsg); - else if(eMsg == ninjaFatal) //Failures (probably fatal) - fprintf(fpLog, "\nERROR: %s\n", ninjaComMsg); -} - -#ifdef NINJA_GUI -//********************************************************************** -// ninjaGUIComHandler() -//********************************************************************** -/** -* Constructor for ninjaGUIComHandler. -* @return -*/ -ninjaGUIComHandler::ninjaGUIComHandler() : ninjaComClass() -{ - verbose = true; -} - -/** -* Destructor for ninjaGUIComHandler. -* @return -*/ -ninjaGUIComHandler::~ninjaGUIComHandler() -{ - if(progressMultiplier) + #ifdef NINJA_DEBUG + else if(eMsgType == ninjaDebug) //Debug { - delete[] progressMultiplier; - progressMultiplier = 0; - } -} - -/** -* Communication handler for "GUI" WindNinja simulations. -* @param eMsg Type of message to be passed. See msgType for available types. -* @param ninjaComMsg Message to be printed. Comes from ninjaComV and ninjaCom. -*/ -void ninjaGUIComHandler::ninjaComHandler(msgType eMsg, const char *ninjaComMsg) -{ - QString s; - //char* lastMsg; //pointer to last message, points to char in WindNinjaInputs class - //int* runNumber; //pointer to run number, points to int in WindNinjaInputs class - int nThreads = 1; - /* Trouble */ - if( runNumber == NULL ) - return; -#ifdef _OPENMP - nThreads = omp_get_num_threads(); -#endif - QCoreApplication::processEvents(); + sprintf( msg, "%s%s\n", runPartMsg, ninjaComMsg); - if(progressMultiplier == 0) - { - progressMultiplier = new int[nRuns]; - int nFullChunks = nRuns / nThreads; + fprintf(fpLog, "%s", msg); - for(int i = 0;i < nFullChunks;i++) + if( multiStream != NULL ) { - for(int j = 1;j <= nThreads;j++) - progressMultiplier[i * j] = nThreads; + fprintf(multiStream, "%s", msg); + fflush(multiStream); } - int nDone = nFullChunks * nThreads; - int nLeft = nRuns - nDone; - - for(int i = 0;i < nLeft;i++) - progressMultiplier[nDone + i] = nLeft; + if( printToMsgHandler == true ) + { + pMsgHandler(msg, pMsgUser); + } } - - if(*runNumber % nThreads == 0 || nRuns == 1) + #endif //NINJA_DEBUG + else if(eMsgType == ninjaSolverProgress) //Solver progress (%complete) { - if(eMsg == ninjaSolverProgress) + if(printSolverProgress) { - if(printSolverProgress) + sprintf( msg, "%sSolver: %d%% complete\n", runPartMsg, atoi(ninjaComMsg)); + + fprintf(fpLog, "%s", msg); + + if( multiStream != NULL ) { - for(int ix=0;ixsearch for explicitly ~line 565 - emit sendProgress(*runNumber,atoi(ninjaComMsg)); + pMsgHandler(msg, pMsgUser); } } } - if (eMsg==ninjaFailure || eMsg==ninjaFatal) + else if(eMsgType == ninjaOuterIterProgress) //Solver progress for outer matching iterations (%complete) { - errorCount++; - if (errorCount > nMaxErrors && nMaxErrors > 0) + sprintf( msg, "%sSolver (matching): %d%% complete\n", runPartMsg, atoi(ninjaComMsg)); + + fprintf(fpLog, "%s", msg); + + if( multiStream != NULL ) { - if(errorCount == nMaxErrors+1) - { - fprintf(fpLog, "Run %d: More than %d errors have been reported. " - "No more will be reported from now on.\n", - *runNumber, nMaxErrors); - } - //return; + fprintf(multiStream, "%s", msg); + fflush(multiStream); } - } - if(eMsg == ninjaNone) //None - { - fprintf(fpLog, "Run %d: %s\n", *runNumber, ninjaComMsg); - s = "Run " + QString::number(*runNumber) + ": " + ninjaComMsg; - //QMetaObject::invokeMethod((QObject*)this, "sendMessage", - // Qt::QueuedConnection, - // Q_ARG(QString*, &s), - // Q_ARG(QColor, Qt::black)); - emit sendMessage(s); - } - else if(eMsg == ninjaDebug) - { //Debug - fprintf(fpLog, "Run %d: %s\n", *runNumber, ninjaComMsg); - s = "Run " + QString::number(*runNumber) + ": " + ninjaComMsg; - emit sendMessage(s); - } - else if(eMsg == ninjaSolverProgress) //Solver progress (%complete) - { - if(printSolverProgress) + if( printToMsgHandler == true ) { - fprintf(fpLog, "Run %d (solver): %d%% complete\n", *runNumber, atoi(ninjaComMsg)); - s = "Run " + QString::number(*runNumber) + " (solver): " + ninjaComMsg + "% done."; - emit sendProgress(*runNumber,atoi(ninjaComMsg)); //Update the progress bar - emit sendMessage(s); + pMsgHandler(msg, pMsgUser); } } - else if(eMsg == ninjaOuterIterProgress) //Solver progress (%complete) - { - fprintf(fpLog, "Run %d (solver): %d%% complete\n", *runNumber, atoi(ninjaComMsg)); - s = "Run " + QString::number(*runNumber) + " (solver): " + ninjaComMsg + "% done."; - emit sendProgress(*runNumber,atoi(ninjaComMsg)); //update the progress bar - emit sendMessage(s); - } - else if(eMsg == ninjaWarning) //Warnings + else if(eMsgType == ninjaWarning) //Warnings { - fprintf(fpLog, "\nRun %d (warning): %s\n", *runNumber, ninjaComMsg); - s = "Run " + QString::number(*runNumber) + "(warning): " + ninjaComMsg; - emit sendMessage(s); - } - else if(eMsg == ninjaFailure) - { //Failures (ie errors) - fprintf(fpLog, "\nRun %d (ERROR): %s\n", *runNumber, ninjaComMsg); - s = "Run " + QString::number(*runNumber) + "(ERROR): " + ninjaComMsg; - emit sendMessage(s); - } - else if(eMsg == ninjaFatal) - { //Failures (probably fatal) - fprintf(fpLog, "\nRun %d (ERROR): %s\n", *runNumber, ninjaComMsg); - s = "Run " + QString::number(*runNumber) + "ERROR): " + ninjaComMsg; - emit sendMessage(s); - } -} -#else -//********************************************************************** -// ninjaGUIComHandler() for jason -//********************************************************************** -void ninjaGUIComHandler::ninjaComHandler(msgType eMsg, const char *ninjaComMsg) -{ + sprintf( msg, "%sWarning: %s\n", runPartMsg, ninjaComMsg); -} -#endif // NINJA_GUI - -//********************************************************************** -// ninjaWFDSSComHandler() -//********************************************************************** -/** -* Communication handler for a "WFDSS" WindNinja simulation. -* @param eMsg Type of message to be passed. See msgType for available types. -* @param ninjaComMsg Message to be printed. Comes from ninjaComV and ninjaCom. -*/ -void ninjaWFDSSComHandler::ninjaComHandler(msgType eMsg, const char *ninjaComMsg) -{ - //If message is a Failure or Fatal type, write the string to the NinjaComString which - //can then be read from the ninja class using lastComString - if(eMsg==ninjaFailure || eMsg==ninjaFatal) - strcpy(lastMsg,ninjaComMsg); //lastMsg points to string in WindNinjaInputs class (which is inherited by the ninja class) -} + fprintf(fpLog, "%s", msg); + if( multiStream != NULL ) + { + fprintf(multiStream, "%s", msg); + fflush(multiStream); + } -//********************************************************************** -// ninjaCLIComHandler() -//********************************************************************** -/** -* Communication handler for a "CLI" WindNinja simulation. -* @param eMsg Type of message to be passed. See msgType for available types. -* @param ninjaComMsg Message to be printed. Comes from ninjaComV and ninjaCom. -*/ -void ninjaCLIComHandler::ninjaComHandler(msgType eMsg, const char *ninjaComMsg) -{ - fpLog = stdout; - bool printRunNum = true; - if(printRunNum == true) { - if (eMsg==ninjaFailure || eMsg==ninjaFatal) { - errorCount++; - if (errorCount > nMaxErrors && nMaxErrors > 0) { - if(errorCount == nMaxErrors+1) { - fprintf(stderr, "Run %d: More than %d errors have been reported. " - "No more will be reported from now on.\n", - *runNumber, nMaxErrors); - } - return; - } + if( printToMsgHandler == true ) + { + pMsgHandler(msg, pMsgUser); } + } else if(eMsgType == ninjaFailure) //Failures (ie errors) + { + sprintf( msg, "%sERROR: %s\n", runPartMsg, ninjaComMsg); - if(eMsg == ninjaNone) - fprintf(fpLog, "Run %d: %s\n", *runNumber, ninjaComMsg); + fprintf(fpErr, "%s", msg); - else if(eMsg == ninjaSolverProgress) + if( multiStream != NULL ) { - if(printSolverProgress) - fprintf(fpLog, "Run %d (solver): %d%% complete\n", - *runNumber, atoi(ninjaComMsg)); + fprintf(multiStream, "%s", msg); + fflush(multiStream); } - else if(eMsg == ninjaOuterIterProgress) //Solver progress for outer matching iterations (%complete) - fprintf(fpLog, "Run %d (matching): %d%% complete\n", - *runNumber, atoi(ninjaComMsg)); - else if(eMsg == ninjaWarning) - fprintf(fpLog, "\nRun %d (warning): %s\n", - *runNumber, ninjaComMsg); - else if(eMsg == ninjaFailure) - fprintf(stderr, "\nRun %d (ERROR): %s\n", - *runNumber, ninjaComMsg); - else if(eMsg == ninjaFatal) - fprintf(stderr, "\nRun %d (ERROR): %s\n", - *runNumber, ninjaComMsg); + if( printToMsgHandler == true ) + { + pMsgHandler(msg, pMsgUser); + } } - else { - if (eMsg==ninjaFailure || eMsg==ninjaFatal) { - errorCount++; - if (errorCount > nMaxErrors && nMaxErrors > 0) { - if(errorCount == nMaxErrors+1) { - fprintf(stderr, "More than %d errors have been reported. " - "No more will be reported from now on.\n", - nMaxErrors); - } - return; - } + else if(eMsgType == ninjaFatal) //Failures (probably fatal) + { + sprintf( msg, "%sERROR: %s\n", runPartMsg, ninjaComMsg); + + fprintf(fpErr, "%s", msg); + + if( multiStream != NULL ) + { + fprintf(multiStream, "%s", msg); + fflush(multiStream); } - if(eMsg == ninjaNone) - fprintf(fpLog, "%s\n", ninjaComMsg); - else if(eMsg == ninjaSolverProgress) + if( printToMsgHandler == true ) { - if(printSolverProgress) - fprintf(fpLog, "Solver: %d%% complete\n", atoi(ninjaComMsg)); + pMsgHandler(msg, pMsgUser); } - else if(eMsg == ninjaOuterIterProgress) //Solver progress for outer matching iterations (%complete) - fprintf(fpLog, "Solver (matching): %d%% complete\n", atoi(ninjaComMsg)); - else if(eMsg == ninjaWarning) - fprintf(fpLog, "\nWarning: %s\n", ninjaComMsg); - else if(eMsg == ninjaFailure) - fprintf(stderr, "\nERROR: %s\n", ninjaComMsg); - else if(eMsg == ninjaFatal) - fprintf(stderr, "\nERROR: %s\n", ninjaComMsg); - fflush(stdout); } + + if( printLastMsg == true ) + { + // If message is a Failure or Fatal type, write the string to the lastMsg storage + // which can then be read from ninjaCom using ninja::get_lastComString() + if( eMsgType == ninjaFailure || eMsgType == ninjaFatal ) + { + strcpy(lastMsg, ninjaComMsg); // stores the raw message in lastMsg, without any ninjaCom message processing + } + } // if( printLastMsg == true ) + + fflush(stdout); + fflush(stderr); } + diff --git a/src/ninja/ninjaCom.h b/src/ninja/ninjaCom.h index 7707e5c28..f9fe4125d 100644 --- a/src/ninja/ninjaCom.h +++ b/src/ninja/ninjaCom.h @@ -38,34 +38,19 @@ #include #include -#ifdef _OPENMP -#include "omp.h" -#endif - -#ifdef NINJA_GUI -#include -#include -#include -#include -#endif - #define NINJA_MSG_SIZE 1000 -class ninjaComClass //virtual base class -#ifdef NINJA_GUI - : public QObject -#endif +#include "callbackFunctions.h" + +class ninjaComClass { public: + ninjaComClass(); - virtual ~ninjaComClass(); - double progressWeight; + ~ninjaComClass(); -#ifdef NINJA_GUI - int *runProgress; - int nRuns; - int *progressMultiplier; -#endif + ninjaComClass(const ninjaComClass& A); + ninjaComClass& operator=(const ninjaComClass &A); typedef enum { @@ -78,92 +63,42 @@ class ninjaComClass //virtual base class ninjaFatal } msgType; - typedef enum - { - ninjaDefaultCom, - ninjaQuietCom, - ninjaLoggingCom, - ninjaGUICom, - WFDSSCom, - ninjaCLICom - } eNinjaCom; - - char* lastMsg; //pointer to last message, points to char in WindNinjaInputs class - int* runNumber; //pointer to run number, points to int in WindNinjaInputs class - eNinjaCom* comType; //pointer to communication type, should point to eNinjaCom in WindNinjaInputs class - FILE* fpLog; + bool printRunNumber; // flag to determine if thread number should be printed at beginning of messages + int runNumber; // run number of the simulation. Can turn this back into a pointer to the value in the WindNinjaInputs class, if the values start to differ - int errorCount; //running error count - int nMaxErrors; //max number of errors to report - bool printSolverProgress; //flag specifying where normal solver progress should be printed (matching will still be printed) + bool printMaxErrors; // flag to determine whether to keep printing error messages past when errorCount exceeds nMaxErrors + int errorCount; // running error count + int nMaxErrors; // max number of errors to report - //methods + double progressWeight; // storage managed and used by diurnal simulations - void noSolverProgress(); + bool printSolverProgress; // flag specifying whether normal solver progress should be printed (matching progress from point initialization runs will still be printed) - void ninjaCom(msgType eMsg, const char *fmt, ...); - void ninjaComV(msgType, const char *, va_list); - //void initializeNinjaCom(char *LastMsg, int* RunNumber, eNinjaCom* ComType); + bool printLastMsg; + char lastMsg[NINJA_MSG_SIZE]; // storage of the last message - //pure virtual function that must be overridden in derived classes - virtual void ninjaComHandler(msgType eMsg, const char *ninjaComMsg) = 0; - -}; + bool printToMsgHandler; + ninjaComMessageHandler pMsgHandler; // A pointer to a user defined ninjaComMessageHandler callback function. If defined, ninjaCom sends messages to this callback function. + void *pMsgUser; // A pointer to a user-defined object or context associated with the callback function. This pointer is passed through to the callback function and allows forwarding of messages to this object or context. -class ninjaDefaultComHandler : public ninjaComClass //concrete class -{ -public: - virtual void ninjaComHandler(msgType eMsg, const char *ninjaComMsg); -}; - - -class ninjaQuietComHandler : public ninjaComClass //concrete class -{ -public: - virtual void ninjaComHandler(msgType eMsg, const char *ninjaComMsg); -}; + bool printLogFile; + FILE* fpLog; + FILE* fpErr; + FILE* multiStream; // A pointer to a multi-stream FILE handle/stream. If defined, ninjaCom sends ALL messages to this stream, in addition to std::cout and std::cerr. -class ninjaLoggingComHandler : public ninjaComClass //concrete class -{ -public: - virtual void ninjaComHandler(msgType eMsg, const char *ninjaComMsg); -}; + //methods -#ifndef NINJA_GUI -class ninjaGUIComHandler : public ninjaComClass //concrete class -{ -public: - virtual void ninjaComHandler(msgType eMsg, const char *ninjaComMsg); -}; -#else -class ninjaGUIComHandler : public ninjaComClass //concrete class -{ - Q_OBJECT - public: - ninjaGUIComHandler(); - ~ninjaGUIComHandler(); - bool verbose; - virtual void ninjaComHandler(msgType eMsg, const char *ninjaComMsg); + void set_messageHandler(ninjaComMessageHandler pMessageHandler, void *pUser); - signals: - void sendProgress(int run, int progress); - void sendMessage(QString message, QColor color = Qt::black); + void noSolverProgress(); -}; -#endif // NINJA_GUI + void ninjaCom(msgType eMsgType, const char *fmt, ...); + void ninjaComV(msgType, const char *, va_list); -class ninjaWFDSSComHandler : public ninjaComClass //concrete class -{ -public: - virtual void ninjaComHandler(msgType eMsg, const char *ninjaComMsg); + void ninjaComDispatchMessage(msgType eMsgType, const char *ninjaComMsg); }; -class ninjaCLIComHandler : public ninjaComClass //concrete class -{ -public: - virtual void ninjaComHandler(msgType eMsg, const char *ninjaComMsg); -}; #endif //NINJACOM_H diff --git a/src/ninja/ninjaTools.cpp b/src/ninja/ninjaTools.cpp new file mode 100644 index 000000000..99eef431b --- /dev/null +++ b/src/ninja/ninjaTools.cpp @@ -0,0 +1,616 @@ +#include "ninjaTools.h" + +/** +* @brief Default constructor. +* +*/ +ninjaTools::ninjaTools() +{ + Com = new ninjaComClass(); + Com->runNumber = 9999; + Com->printRunNumber = false; + + nomadsCount = 0; + while( apszNomadsKeys[nomadsCount][0] != NULL ) + { + nomadsCount++; + } + nomadsModels = new NomadsWxModel*[nomadsCount]; + int i = 0; + while( apszNomadsKeys[i][0] != NULL ) + { + nomadsModels[i] = new NomadsWxModel( apszNomadsKeys[i][0] ); + i++; + } +} + +/** +* @brief Destructor. +* +*/ +ninjaTools::~ninjaTools() +{ + if(nomadsModels) + { + for(int i = 0; i < nomadsCount; i++) + { + if(nomadsModels[i]) + { + free(nomadsModels[i]); + } + } + + free(nomadsModels); + } + + delete Com; +} + +/** +* @brief Copy constructor. +* +* @param An Object to copy. +*/ +/*ninjaTools::ninjaTools(const ninjaTools& A) +{ + nomadsCount = A.nomadsCount; + nomadsModels = new NomadsWxModel*[nomadsCount]; + for(int i = 0; i < nomadsCount; i++) + { + nomadsModels[i] = new NomadsWxModel( apszNomadsKeys[i][0] ); // this should ACTUALLY be something like "new NomadsWxModel( A.nomadsModels[i] )" or something like that, but I don't think it has a proper copy constructor setup. I guess just replicate the constructor for now. + } + + Com = new ninjaComClass(*A.Com); +}*/ + +/** +* @brief Equals operator. +* +* @param A Right-hand side. +* @return An Object equal to the one on the right-hand side; +*/ +/*ninjaTools& ninjaTools::operator=(ninjaTools const& A) +{ + if(&A != this) + { + // I don't even want to know where to begin for the nomadsModels here + + delete Com; + Com = new ninjaComClass(); + *Com = *A.Com; + } + return *this; +}*/ + +/** + * @brief Fetches a DEM using bounding box. + * + * @param boundsBox Bounding box in the form of north, east, south, west. + * @param fileName Name of DEM file. + * @param resolution Resolution of DEM file. + * @param fetchType Type of DEM file to fetch. + * + */ +int ninjaTools::fetchDEMBBox(double *boundsBox, const char *fileName, double resolution, const char* fetchType, char ** papszOptions) +{ + SURF_FETCH_E retval = SURF_FETCH_E_NONE; + SurfaceFetch * fetcher; + if (strcmp(fetchType, "srtm") == 0){ + fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::SRTM_STR,""); + } + #ifdef HAVE_GMTED + else if (strcmp(fetchType, "gmted") == 0){ + fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::WORLD_GMTED_STR,""); + } + #endif + else if (strcmp(fetchType, "relief") == 0){ + fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::RELIEF_STR,""); + } + else if (strcmp(fetchType, "lcp") == 0){ + fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::LCP_STR,""); + } + if (fetcher == NULL) { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid input fetchType '%s' in ninjaTools::fetchDEMBBox()\nchoices are: 'srtm', 'gmted', 'relief', 'lcp'", fetchType); + delete fetcher; + return NINJA_E_INVALID; + } + + double northBound = boundsBox[0]; + double eastBound = boundsBox[1]; + double southBound = boundsBox[2]; + double westBound = boundsBox[3]; + int result = fetcher->FetchBoundingBox(boundsBox, resolution, fileName, NULL); + if (result != 0) + { + //Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + Com->ninjaCom(ninjaComClass::ninjaFailure, "in ninjaTools::fetchDEMBBox(), fetching failed!"); + delete fetcher; + return NINJA_E_INVALID; + } + delete fetcher; + return NINJA_SUCCESS; +} + +/** + * @brief Fetches a DEM using a point. + * + * @param adfPoint a x,y point in WGS 84 longitude, latitude + * @param adfBuff length of a buffer in the x and y directions + * @param units Units of buffer. + * @param dfCellSize Cell size of DEM. + * @param pszDstFile Destination file. + * @param papszOptions Options for fetching DEM. + * @param fetchType Type of DEM to fetch. + * + */ +int ninjaTools::fetchDEMPoint(double * adfPoint,double *adfBuff, const char* units, double dfCellSize, const char * pszDstFile, const char* fetchType, char ** papszOptions) +{ + if (pszDstFile == NULL) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Input dstFile '%s' in ninjaTools::fetchDEMPoint() is invalid.", pszDstFile); + return NINJA_E_INVALID; + } + SURF_FETCH_E retval = SURF_FETCH_E_NONE; + SurfaceFetch * fetcher; + if (strcmp(fetchType, "srtm") == 0){ + fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::SRTM_STR,""); + } + #ifdef HAVE_GMTED + else if (strcmp(fetchType, "gmted") == 0){ + fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::WORLD_GMTED_STR,""); + } + #endif + else if (strcmp(fetchType, "relief") == 0){ + fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::RELIEF_STR,""); + } + else if (strcmp(fetchType, "lcp") == 0){ + fetcher = FetchFactory::GetSurfaceFetch(FetchFactory::LCP_STR,""); + } + if (fetcher == NULL) { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid input fetchType '%s' in ninjaTools::fetchDEMPoint()\nchoices are: 'srtm', 'gmted', 'relief', 'lcp'", fetchType); + delete fetcher; + return NINJA_E_INVALID; + } + lengthUnits::eLengthUnits ninjaUnits = lengthUnits::getUnit(std::string(units)); + int result = fetcher->FetchPoint(adfPoint, adfBuff, ninjaUnits, dfCellSize, pszDstFile, papszOptions); + if (result != 0) + { + //Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + Com->ninjaCom(ninjaComClass::ninjaFailure, "in ninjaTools::fetchDEMPoint(), fetching failed!"); + delete fetcher; + return NINJA_E_INVALID; + } + delete fetcher; + return NINJA_SUCCESS; +} + +int ninjaTools::fetchWeatherModelData(const char* modelName, const char* demFile, int hours) +{ + try + { + wxModelInitialization *model = NULL; + model = wxModelInitializationFactory::makeWxInitializationFromId(std::string(modelName)); + if(!model) + { + throw std::runtime_error(std::string("Weather model not found: ") + modelName); + } + + std::string forecastFileName = model->fetchForecast(demFile, hours); + if(forecastFileName == "exception") + { + throw std::runtime_error("ninjaTools::fetchWeatherModelData() returned an invalid forecastFileName."); + } + } + catch(armyException &e) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception& e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } + + return NINJA_SUCCESS; +} + +int ninjaTools::fetchArchiveWeatherModelData(const char* modelName, const char* demFile, const char* timeZone, int startYear, int startMonth, int startDay, int startHour, int endYear, int endMonth, int endDay, int endHour) +{ + try + { + wxModelInitialization *model = wxModelInitializationFactory::makeWxInitializationFromId(std::string(modelName)); + if(!model) + { + throw std::runtime_error(std::string("Weather model not found: ") + modelName); + } + + boost::gregorian::date startDate(startYear, startMonth, startDay); + boost::gregorian::date endDate(endYear, endMonth, endDay); + + boost::local_time::tz_database tz_db; + tz_db.load_from_file( FindDataPath("date_time_zonespec.csv") ); + boost::local_time::time_zone_ptr timeZonePtr; + timeZonePtr = tz_db.time_zone_from_region(timeZone); + + boost::local_time::local_date_time ldtStart( + startDate, + boost::posix_time::hours(startHour), + timeZonePtr, + boost::local_time::local_date_time::NOT_DATE_TIME_ON_ERROR + ); + + boost::local_time::local_date_time ldtEnd( + endDate, + boost::posix_time::hours(endHour), + timeZonePtr, + boost::local_time::local_date_time::NOT_DATE_TIME_ON_ERROR + ); + + boost::posix_time::ptime startUTC = ldtStart.utc_time(); + boost::posix_time::ptime endUTC = ldtEnd.utc_time(); + + int hours = 0; + + auto* forecastModel = dynamic_cast(model); + forecastModel->setDateTime(startUTC.date(), + endUTC.date(), + boost::lexical_cast(startUTC.time_of_day().hours()), + boost::lexical_cast(endUTC.time_of_day().hours())); + + std::string forecastFileName = forecastModel->fetchForecast(demFile, hours); + if(forecastFileName == "exception") + { + throw std::runtime_error("ninjaTools::fetchArchiveWeatherModelData() returned an invalid forecastFileName."); + } + } + catch(armyException &e) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception& e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } + + return NINJA_SUCCESS; +} + +std::vector ninjaTools::getForecastIdentifiers() +{ + ncepGfsSurfInitialization gfs; + ncepNamSurfInitialization nam; + ncepNamAlaskaSurfInitialization namAla; + ncepRapSurfInitialization rap; + ncepNdfdInitialization ndfd; + + modelIdentifiers.push_back(ndfd.getForecastIdentifier()); + modelIdentifiers.push_back(nam.getForecastIdentifier()); + modelIdentifiers.push_back(rap.getForecastIdentifier()); + modelIdentifiers.push_back(namAla.getForecastIdentifier()); + modelIdentifiers.push_back(gfs.getForecastIdentifier()); + + for(int i = 0; i < nomadsCount; i++) + { + modelIdentifiers.push_back(nomadsModels[i]->getForecastIdentifier()); + } + + GCPWxModel archive; + modelIdentifiers.push_back(archive.getForecastIdentifier()); + return modelIdentifiers; +} + +std::vector ninjaTools::getTimeList(const char* fileName, std::string timeZone) +{ + wxModelInitialization *model = NULL; + model = wxModelInitializationFactory::makeWxInitialization(fileName); + std::vector temp = model->getTimeList(timeZone); + std::vector timeList; + + for(int i = 0; i < temp.size(); i++) + { + timeList.push_back(temp[i].to_string()); + } + + return timeList; +} + +int ninjaTools::getStartHour(const char* modelIdentifier) +{ + wxModelInitialization *model = NULL; + model = wxModelInitializationFactory::makeWxInitializationFromId(modelIdentifier); + return model->getStartHour(); +} + +int ninjaTools::getEndHour(const char* modelIdentifier) +{ + wxModelInitialization *model = NULL; + model = wxModelInitializationFactory::makeWxInitializationFromId(modelIdentifier); + return model->getEndHour(); +} + + +int ninjaTools::fetchStationFromBBox( const int* yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char* elevationFile, double buffer, const char* units, const char* timeZone, bool fetchLatestFlag, const char* outputPath, bool locationFileFlag, char ** papszOptions ) +{ + try + { + std::vector timeList; + for(size_t i=0; ininjaCom(ninjaComClass::ninjaFailure, "pointInitialization::fetchStationFromBbox() failed."); + return NINJA_E_INVALID; + } + if(locationFileFlag) + { + pointInitialization::writeStationLocationFile(stationPathName, std::string(elevationFile), fetchLatestFlag); + } + + return NINJA_SUCCESS; + } + catch(armyException &e) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception& e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } +} + +int ninjaTools::fetchStationByName( const int* yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char* elevationFile, const char* stationList, const char* timeZone, bool fetchLatestFlag, const char* outputPath, bool locationFileFlag, char ** papszOptions ) +{ + try + { + std::vector timeList; + for(size_t i=0; ininjaCom(ninjaComClass::ninjaFailure, "pointInitialization::fetchStationByName() failed."); + return NINJA_E_INVALID; + } + if(locationFileFlag) + { + pointInitialization::writeStationLocationFile(stationPathName, std::string(elevationFile), fetchLatestFlag); + } + + return NINJA_SUCCESS; + } + catch(armyException &e) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception& e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } +} + +int ninjaTools::getTimeList( const int * inputYearList, const int * inputMonthList, const int * inputDayList, const int * inputHourList, const int * inputMinuteList, int * outputYearList, int* outputMonthList, int * outputDayList, int * outputHourList, int* outputMinuteList, int nTimeSteps, const char* timeZone ) +{ + try + { + std::vector timeList = + pointInitialization::getTimeList( + inputYearList[0], inputMonthList[0], inputDayList[0], + inputHourList[0], inputMinuteList[0], + inputYearList[1], inputMonthList[1], inputDayList[1], + inputHourList[1], inputMinuteList[1], + nTimeSteps, std::string(timeZone) + ); + + for (int i = 0; i < nTimeSteps; ++i) + { + const boost::posix_time::ptime& time = timeList[i]; + boost::gregorian::date date = time.date(); + boost::posix_time::time_duration timeDuration = time.time_of_day(); + + outputYearList[i] = static_cast(date.year()); + outputMonthList[i] = static_cast(date.month()); + outputDayList[i] = static_cast(date.day()); + outputHourList[i] = timeDuration.hours(); + outputMinuteList[i] = timeDuration.minutes(); + } + + return NINJA_SUCCESS; + } + catch(armyException &e) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception& e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } +} + +int ninjaTools::generateSingleTimeObject( int inputYear, int inputMonth, int inputDay, int inputHour, int inputMinute, const char * timeZone, int * outYear, int * outMonth, int* outDay, int * outHour, int * outMinute ) +{ + try + { + if(!outYear || !outMonth || !outDay || !outHour || !outMinute) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Invalid 'empty' input outYear, outMonth, outDay, outHour, or outMinute in ninjaTools::generateSingleTimeObject()"); + return NINJA_E_OTHER; + } + + boost::posix_time::ptime timeObject = + pointInitialization::generateSingleTimeObject(inputYear, inputMonth, inputDay, inputHour, inputMinute, std::string(timeZone)); + + const boost::gregorian::date& date = timeObject.date(); + const boost::posix_time::time_duration& td = timeObject.time_of_day(); + + *outYear = static_cast(date.year()); + *outMonth = static_cast(date.month()); + *outDay = static_cast(date.day()); + *outHour = td.hours(); + *outMinute = td.minutes(); + + return NINJA_SUCCESS; + } + catch(armyException &e) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception& e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } +} + +int ninjaTools::checkTimeDuration( int* yearList, int* monthList, int * dayList, int * minuteList, int *hourList, int listSize, char ** papszOptions ) +{ + try + { + std::vector timeList; + for(size_t i=0; i < listSize; i++) + { + 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))); + } + + int isValid = pointInitialization::checkFetchTimeDuration(timeList); + if(isValid == -2) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "pointInitialization::checkFetchTimeDuration() failed."); + return NINJA_E_OTHER; + } + + return NINJA_SUCCESS; + } + catch(armyException &e) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( exception& e ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: %s", e.what()); + return NINJA_E_INVALID; + } + catch( ... ) + { + Com->ninjaCom(ninjaComClass::ninjaFailure, "Exception caught: Cannot determine exception type."); + return NINJA_E_INVALID; + } +} + +/*----------------------------------------------------------------------------- + * Ninja Communication Methods + *-----------------------------------------------------------------------------*/ + +int ninjaTools::setNinjaComMessageHandler( ninjaComMessageHandler pMsgHandler, void *pUser, + char ** papszOptions ) +{ + try + { + Com->set_messageHandler(pMsgHandler, pUser); + } + catch( ... ) + { + std::cerr << "CRITICAL: ninjaTools level ninjaComMessageHandler not set. Messages will NOT be delivered." << std::endl; + return NINJA_E_INVALID; + } + return NINJA_SUCCESS; +} + +int ninjaTools::setNinjaMultiComStream( FILE* stream, + char ** papszOptions ) +{ + try + { + Com->multiStream = stream; + } + catch( ... ) + { + std::cerr << "ERROR: ninjaTools level ninjaCom multiStream FILE pointer not set." << std::endl; + return NINJA_E_INVALID; + } + return NINJA_SUCCESS; +} diff --git a/src/ninja/ninjaTools.h b/src/ninja/ninjaTools.h new file mode 100644 index 000000000..1201ba991 --- /dev/null +++ b/src/ninja/ninjaTools.h @@ -0,0 +1,74 @@ +#ifndef NINJATOOLS_H +#define NINJATOOLS_H + +#include "fetch_factory.h" +#include "nomads_wx_init.h" +#include "wxModelInitializationFactory.h" +#include "wxStation.h" +#include "pointInitialization.h" + +#include "ninja_errors.h" + +#include "callbackFunctions.h" + +#include "ninjaCom.h" + +class ninjaTools +{ +public: + + ninjaTools(); + ~ninjaTools(); + +// ninjaTools(const ninjaTools& A); +// ninjaTools& operator=(ninjaTools const& A); + + ninjaComClass *Com; // pointer to the ninjaTools level com handler + + int fetchDEMBBox(double *boundsBox, const char *fileName, double resolution, const char* fetchType, char ** papszOptions=NULL ); + int fetchDEMPoint(double * adfPoint, double *adfBuff, const char* units, double dfCellSize, const char * pszDstFile, const char* fetchType, char ** papszOptions=NULL ); + + int fetchWeatherModelData(const char* modelName, const char* demFile, int hours); + int fetchArchiveWeatherModelData(const char* modelName, const char* demFile, const char* timeZone, int startYear, int startMonth, int startDay, int startHour, int endYear, int endMonth, int endDay, int endHour); + std::vector getForecastIdentifiers(); + std::vector getTimeList(const char* modelName, std::string timeZone); + int getStartHour(const char*modelIdentifier); + int getEndHour(const char* modelIdentifer); + + int fetchStationFromBBox( const int* yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char* elevationFile, double buffer, const char* units, const char* timeZone, bool fetchLatestFlag, const char* outputPath, bool locationFileFlag, char ** papszOptions=NULL ); + int fetchStationByName( const int* yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char* elevationFile, const char* stationList, const char* timeZone, bool fetchLatestFlag, const char* outputPath, bool locationFileFlag, char ** papszOptions=NULL ); + int getTimeList( const int * inputYearList, const int * inputMonthList, const int * inputDayList, const int * inputHourList, const int * inputMinuteList, int * outputYearList, int* outputMonthList, int * outputDayList, int * outputHourList, int* outputMinuteList, int nTimeSteps, const char* timeZone ); + int generateSingleTimeObject( int inputYear, int inputMonth, int inputDay, int inputHour, int inputMinute, const char * timeZone, int * outYear, int * outMonth, int* outDay, int * outHour, int * outMinute ); + int checkTimeDuration( int* yearList, int* monthList, int * dayList, int * minuteList, int *hourList, int listSize, char ** papszOptions=NULL ); + +private: + int nomadsCount; + NomadsWxModel** nomadsModels; + std::vector modelIdentifiers; + +public: + /*----------------------------------------------------------------------------- + * Ninja Communication Methods + *-----------------------------------------------------------------------------*/ + + /** + * \brief Set a ninjaComMessageHandler callback function to the ninjaTools level ninjaCom + * + * \param pMsgHandler A pointer to a ninjaComMessageHandler callback function. + * \param pUser A pointer to the object or context associated with the callback function. + * \return errval Returns NINJA_SUCCESS upon success + */ + int setNinjaComMessageHandler( ninjaComMessageHandler pMsgHandler, void *pUser, + char ** papszOptions = NULL); + + /** + * \brief Set a ninjaCom multi-stream FILE handle to the ninjaTools level ninjaCom + * + * \param stream A pointer to a multi-stream FILE handle/stream. + * \return errval Returns NINJA_SUCCESS upon success + */ + int setNinjaMultiComStream( FILE* stream, + char ** papszOptions = NULL); +}; + +#endif // NINJATOOLS_H diff --git a/src/ninja/ninja_init.cpp b/src/ninja/ninja_init.cpp index 0a04da9c0..5548f5bd4 100644 --- a/src/ninja/ninja_init.cpp +++ b/src/ninja/ninja_init.cpp @@ -260,13 +260,20 @@ int NinjaInitialize(const char* typeofrun) CPLDebug( "WINDNINJA", "Setting GDAL_DATA:%s", pszGdalData ); CPLSetConfigOption( "GDAL_DATA", pszGdalData ); + CPLDebug( "WINDNINJA", "Setting GDAL_DATA..." ); + std::string osProjData; + osProjData = FindDataPath( "proj-data/data/proj.db" ); + const char *pszProjData = CPLGetPath( osProjData.c_str() ); + CPLDebug( "WINDNINJA", "Setting PROJ_DATA:%s", pszProjData ); + CPLSetConfigOption( "PROJ_LIB", pszProjData ); + #if defined(FIRELAB_PACKAGE) char szDriverPath[MAX_PATH+1]; rc = CPLGetExecPath( szDriverPath, MAX_PATH+1); const char *pszPlugins = CPLSPrintf("%s/gdalplugins", CPLGetPath(szDriverPath)); - CPLDebug("WINDNINJA", "Setting GDAL_DRIVER_PATH: %s", pszPlugins); + //CPLDebug("WINDNINJA", "Setting GDAL_DRIVER_PATH: %s", pszPlugins); - CPLSetConfigOption("GDAL_DRIVER_PATH", pszPlugins); + //CPLSetConfigOption("GDAL_DRIVER_PATH", pszPlugins); #endif /* defined(FIRELAB_PACKAGE) */ #if defined(NINJAFOAM) && defined(FIRELAB_PACKAGE) diff --git a/src/ninja/ninjafoam.cpp b/src/ninja/ninjafoam.cpp index 647bfc334..77d361a12 100644 --- a/src/ninja/ninjafoam.cpp +++ b/src/ninja/ninjafoam.cpp @@ -2250,7 +2250,7 @@ int NinjaFoam::SampleCloud() } OGR_G_DestroyGeometry( hGeometry ); OGR_F_Destroy( hFeature ); - OGR_DS_Destroy( hDS ); + GDALClose( hDS ); GDALClose( hGriddedDS ); return 0; @@ -2404,7 +2404,7 @@ int NinjaFoam::SampleCloudGrid() CPLFree( (void*)padfData ); OGR_G_DestroyGeometry( hGeometry ); OGR_F_Destroy( hFeature ); - OGR_DS_Destroy( hDS ); + GDALClose( hDS ); GDALClose( hGriddedDS ); return 0; diff --git a/src/ninja/nomads_wx_init.cpp b/src/ninja/nomads_wx_init.cpp index 5228b7f67..2372841fa 100644 --- a/src/ninja/nomads_wx_init.cpp +++ b/src/ninja/nomads_wx_init.cpp @@ -678,7 +678,7 @@ void NomadsWxModel::setSurfaceGrids( WindNinjaInputs &input, if( EQUAL( pszElement, "TMP" ) ) { GDAL2AsciiGrid( (GDALDataset*)hVrtDS, i + 1, airGrid ); - if( cplIsNan( dfNoData ) ) + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue( -9999.0 ); airGrid.replaceNan( -9999.0 ); @@ -688,7 +688,7 @@ void NomadsWxModel::setSurfaceGrids( WindNinjaInputs &input, else if( EQUAL( pszElement, "UGRD" ) ) { GDAL2AsciiGrid( (GDALDataset*)hVrtDS, i + 1, uGrid ); - if( cplIsNan( dfNoData ) ) + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue( -9999.0 ); uGrid.replaceNan( -9999.0 ); @@ -697,7 +697,7 @@ void NomadsWxModel::setSurfaceGrids( WindNinjaInputs &input, else if( EQUAL( pszElement, "VGRD" ) ) { GDAL2AsciiGrid( (GDALDataset*)hVrtDS, i + 1, vGrid ); - if( cplIsNan( dfNoData ) ) + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue( -9999.0 ); vGrid.replaceNan( -9999.0 ); @@ -706,7 +706,7 @@ void NomadsWxModel::setSurfaceGrids( WindNinjaInputs &input, else if( EQUAL( pszElement, "TCDC" ) ) { GDAL2AsciiGrid( (GDALDataset*)hVrtDS, i + 1, cloudGrid ); - if( cplIsNan( dfNoData ) ) + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue( -9999.0 ); cloudGrid.replaceNan( -9999.0 ); @@ -723,7 +723,7 @@ void NomadsWxModel::setSurfaceGrids( WindNinjaInputs &input, } GDAL2AsciiGrid( (GDALDataset*)hVrtDS, i + 1, airGrid ); - if( cplIsNan( dfNoData ) ) + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue( -9999.0 ); airGrid.replaceNan( -9999.0 ); @@ -741,7 +741,7 @@ void NomadsWxModel::setSurfaceGrids( WindNinjaInputs &input, } GDAL2AsciiGrid( (GDALDataset*)hVrtDS, i + 1, speedGrid ); - if( cplIsNan( dfNoData ) ) + if( std::isnan( dfNoData ) ) { speedGrid.set_noDataValue( -9999.0 ); speedGrid.replaceNan( -9999.0 ); @@ -751,7 +751,7 @@ void NomadsWxModel::setSurfaceGrids( WindNinjaInputs &input, { blendCheck = true; GDAL2AsciiGrid( (GDALDataset*)hVrtDS, i + 1, directionGrid ); - if( cplIsNan( dfNoData ) ) + if( std::isnan( dfNoData ) ) { directionGrid.set_noDataValue( -9999.0 ); directionGrid.replaceNan( -9999.0 ); @@ -830,7 +830,7 @@ void NomadsWxModel::setSurfaceGrids( WindNinjaInputs &input, { dfNoData = -9999.0; } - if( cplIsNan( dfNoData ) ) + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue( -9999.0 ); cloudGrid.replaceNan( -9999.0 ); diff --git a/src/ninja/pointInitialization.cpp b/src/ninja/pointInitialization.cpp index bddee39c4..48ab0d1c8 100644 --- a/src/ninja/pointInitialization.cpp +++ b/src/ninja/pointInitialization.cpp @@ -777,7 +777,7 @@ vector pointInitialization::readDiskLine(st if( dfTempValue > 90.0 || dfTempValue < -90.0 ) { OGRFeature::DestroyFeature( poFeature ); - OGR_DS_Destroy( hDS ); + GDALClose( hDS ); oErrorString = "Bad latitude in weather station csv file"; oErrorString += " at station: "; @@ -792,7 +792,7 @@ vector pointInitialization::readDiskLine(st if( dfTempValue < -180.0 || dfTempValue > 360.0 ) { OGRFeature::DestroyFeature( poFeature ); - OGR_DS_Destroy( hDS ); + GDALClose( hDS ); oErrorString = "Bad longitude in weather station csv file"; oErrorString += " at station: "; @@ -1045,7 +1045,7 @@ vector pointInitialization::readDiskLine(st OGRFeature::DestroyFeature( poFeature ); } - OGR_DS_Destroy( hDS ); + GDALClose( hDS ); return oStations; } @@ -1436,7 +1436,7 @@ double pointInitialization::interpolator(double iPoint, double lowX, double high //MSVC 2010 is not c++11 compliant-> isnan doesn't work with MSVC2010 //changing to CPLISNan() - if(cplIsNan(result)) + if(std::isnan(result)) { result = work; } @@ -1641,7 +1641,7 @@ void pointInitialization::fetchMetaData(std::string fileName, std::string demFil OGR_F_Destroy( hFeature ); } - OGR_DS_Destroy(poDS); + GDALClose(poDS); GDALClose(hDS); } /** @@ -2249,8 +2249,8 @@ vector pointInitialization::Irradiate(vector solar_radiation, st { solFrac=one; } - //Note that cplIsNan is required to compile on MSVC2010 c++11's isnan doesn't work - if (cplIsNan(solFrac)) + //Note that std::isnan is required to compile on MSVC2010 c++11's isnan doesn't work + if (std::isnan(solFrac)) { solFrac=one; } diff --git a/src/ninja/shpopen.cpp b/src/ninja/shpopen.cpp deleted file mode 100644 index 54a9687aa..000000000 --- a/src/ninja/shpopen.cpp +++ /dev/null @@ -1,1867 +0,0 @@ -/****************************************************************************** - * $Id$ - * - * Project: Shapelib - * Purpose: Implementation of core Shapefile read/write functions. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 1999, 2001, Frank Warmerdam - * - * This software is available under the following "MIT Style" license, - * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This - * option is discussed in more detail in shapelib.html. - * - * -- - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * 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. - ****************************************************************************** - * - * $Log: shpopen.c,v $ - * Revision 1.39 2002/08/26 06:46:56 warmerda - * avoid c++ comments - * - * Revision 1.38 2002/05/07 16:43:39 warmerda - * Removed debugging printf. - * - * Revision 1.37 2002/04/10 17:35:22 warmerda - * fixed bug in ring reversal code - * - * Revision 1.36 2002/04/10 16:59:54 warmerda - * added SHPRewindObject - * - * Revision 1.35 2001/12/07 15:10:44 warmerda - * fix if .shx fails to open - * - * Revision 1.34 2001/11/01 16:29:55 warmerda - * move pabyRec into SHPInfo for thread safety - * - * Revision 1.33 2001/07/03 12:18:15 warmerda - * Improved cleanup if SHX not found, provied by Riccardo Cohen. - * - * Revision 1.32 2001/06/22 01:58:07 warmerda - * be more careful about establishing initial bounds in face of NULL shapes - * - * Revision 1.31 2001/05/31 19:35:29 warmerda - * added support for writing null shapes - * - * Revision 1.30 2001/05/28 12:46:29 warmerda - * Add some checking on reasonableness of record count when opening. - * - * Revision 1.29 2001/05/23 13:36:52 warmerda - * added use of SHPAPI_CALL - * - * Revision 1.28 2001/02/06 22:25:06 warmerda - * fixed memory leaks when SHPOpen() fails - * - * Revision 1.27 2000/07/18 15:21:33 warmerda - * added better enforcement of -1 for append in SHPWriteObject - * - * Revision 1.26 2000/02/16 16:03:51 warmerda - * added null shape support - * - * Revision 1.25 1999/12/15 13:47:07 warmerda - * Fixed record size settings in .shp file (was 4 words too long) - * Added stdlib.h. - * - * Revision 1.24 1999/11/05 14:12:04 warmerda - * updated license terms - * - * Revision 1.23 1999/07/27 00:53:46 warmerda - * added support for rewriting shapes - * - * Revision 1.22 1999/06/11 19:19:11 warmerda - * Cleanup pabyRec static buffer on SHPClose(). - * - * Revision 1.21 1999/06/02 14:57:56 kshih - * Remove unused variables - * - * Revision 1.20 1999/04/19 21:04:17 warmerda - * Fixed syntax error. - * - * Revision 1.19 1999/04/19 21:01:57 warmerda - * Force access string to binary in SHPOpen(). - * - * Revision 1.18 1999/04/01 18:48:07 warmerda - * Try upper case extensions if lower case doesn't work. - * - * Revision 1.17 1998/12/31 15:29:39 warmerda - * Disable writing measure values to multipatch objects if - * DISABLE_MULTIPATCH_MEASURE is defined. - * - * Revision 1.16 1998/12/16 05:14:33 warmerda - * Added support to write MULTIPATCH. Fixed reading Z coordinate of - * MULTIPATCH. Fixed record size written for all feature types. - * - * Revision 1.15 1998/12/03 16:35:29 warmerda - * r+b is proper binary access string, not rb+. - * - * Revision 1.14 1998/12/03 15:47:56 warmerda - * Fixed setting of nVertices in SHPCreateObject(). - * - * Revision 1.13 1998/12/03 15:33:54 warmerda - * Made SHPCalculateExtents() separately callable. - * - * Revision 1.12 1998/11/11 20:01:50 warmerda - * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines. - * - * Revision 1.11 1998/11/09 20:56:44 warmerda - * Fixed up handling of file wide bounds. - * - * Revision 1.10 1998/11/09 20:18:51 warmerda - * Converted to support 3D shapefiles, and use of SHPObject. - * - * Revision 1.9 1998/02/24 15:09:05 warmerda - * Fixed memory leak. - * - * Revision 1.8 1997/12/04 15:40:29 warmerda - * Fixed byte swapping of record number, and record length fields in the - * .shp file. - * - * Revision 1.7 1995/10/21 03:15:58 warmerda - * Added support for binary file access, the magic cookie 9997 - * and tried to improve the int32 selection logic for 16bit systems. - * - * Revision 1.6 1995/09/04 04:19:41 warmerda - * Added fix for file bounds. - * - * Revision 1.5 1995/08/25 15:16:44 warmerda - * Fixed a couple of problems with big endian systems ... one with bounds - * and the other with multipart polygons. - * - * Revision 1.4 1995/08/24 18:10:17 warmerda - * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc() - * functions (such as on the Sun). - * - * Revision 1.3 1995/08/23 02:23:15 warmerda - * Added support for reading bounds, and fixed up problems in setting the - * file wide bounds. - * - * Revision 1.2 1995/08/04 03:16:57 warmerda - * Added header. - * - */ - -static char rcsid[] = - "$Id$"; - -#include "shapefil.h" - -#include -#include -#include -#include -#include - -typedef unsigned char uchar; - -#if UINT_MAX == 65535 -typedef long int32; -#else -typedef int int32; -#endif - -#ifndef FALSE -# define FALSE 0 -# define TRUE 1 -#endif - -#define ByteCopy( a, b, c ) memcpy( b, a, c ) -#ifndef MAX -# define MIN(a,b) ((ab) ? a : b) -#endif - -static int bBigEndian; - - -/************************************************************************/ -/* SwapWord() */ -/* */ -/* Swap a 2, 4 or 8 byte word. */ -/************************************************************************/ - -static void SwapWord( int length, void * wordP ) - -{ - int i; - uchar temp; - - for( i=0; i < length/2; i++ ) - { - temp = ((uchar *) wordP)[i]; - ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1]; - ((uchar *) wordP)[length-i-1] = temp; - } -} - -/************************************************************************/ -/* SfRealloc() */ -/* */ -/* A realloc cover function that will access a NULL pointer as */ -/* a valid input. */ -/************************************************************************/ - -static void * SfRealloc( void * pMem, int nNewSize ) - -{ - if( pMem == NULL ) - return( (void *) malloc(nNewSize) ); - else - return( (void *) realloc(pMem,nNewSize) ); -} - -/************************************************************************/ -/* SHPWriteHeader() */ -/* */ -/* Write out a header for the .shp and .shx files as well as the */ -/* contents of the index (.shx) file. */ -/************************************************************************/ - -static void SHPWriteHeader( SHPHandle psSHP ) - -{ - uchar abyHeader[100]; - int i; - int32 i32; - double dValue; - int32 *panSHX; - -/* -------------------------------------------------------------------- */ -/* Prepare header block for .shp file. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < 100; i++ ) - abyHeader[i] = 0; - - abyHeader[2] = 0x27; /* magic cookie */ - abyHeader[3] = 0x0a; - - i32 = psSHP->nFileSize/2; /* file size */ - ByteCopy( &i32, abyHeader+24, 4 ); - if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - - i32 = 1000; /* version */ - ByteCopy( &i32, abyHeader+28, 4 ); - if( bBigEndian ) SwapWord( 4, abyHeader+28 ); - - i32 = psSHP->nShapeType; /* shape type */ - ByteCopy( &i32, abyHeader+32, 4 ); - if( bBigEndian ) SwapWord( 4, abyHeader+32 ); - - dValue = psSHP->adBoundsMin[0]; /* set bounds */ - ByteCopy( &dValue, abyHeader+36, 8 ); - if( bBigEndian ) SwapWord( 8, abyHeader+36 ); - - dValue = psSHP->adBoundsMin[1]; - ByteCopy( &dValue, abyHeader+44, 8 ); - if( bBigEndian ) SwapWord( 8, abyHeader+44 ); - - dValue = psSHP->adBoundsMax[0]; - ByteCopy( &dValue, abyHeader+52, 8 ); - if( bBigEndian ) SwapWord( 8, abyHeader+52 ); - - dValue = psSHP->adBoundsMax[1]; - ByteCopy( &dValue, abyHeader+60, 8 ); - if( bBigEndian ) SwapWord( 8, abyHeader+60 ); - - dValue = psSHP->adBoundsMin[2]; /* z */ - ByteCopy( &dValue, abyHeader+68, 8 ); - if( bBigEndian ) SwapWord( 8, abyHeader+68 ); - - dValue = psSHP->adBoundsMax[2]; - ByteCopy( &dValue, abyHeader+76, 8 ); - if( bBigEndian ) SwapWord( 8, abyHeader+76 ); - - dValue = psSHP->adBoundsMin[3]; /* m */ - ByteCopy( &dValue, abyHeader+84, 8 ); - if( bBigEndian ) SwapWord( 8, abyHeader+84 ); - - dValue = psSHP->adBoundsMax[3]; - ByteCopy( &dValue, abyHeader+92, 8 ); - if( bBigEndian ) SwapWord( 8, abyHeader+92 ); - -/* -------------------------------------------------------------------- */ -/* Write .shp file header. */ -/* -------------------------------------------------------------------- */ - fseek( psSHP->fpSHP, 0, 0 ); - fwrite( abyHeader, 100, 1, psSHP->fpSHP ); - -/* -------------------------------------------------------------------- */ -/* Prepare, and write .shx file header. */ -/* -------------------------------------------------------------------- */ - i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ - ByteCopy( &i32, abyHeader+24, 4 ); - if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - - fseek( psSHP->fpSHX, 0, 0 ); - fwrite( abyHeader, 100, 1, psSHP->fpSHX ); - -/* -------------------------------------------------------------------- */ -/* Write out the .shx contents. */ -/* -------------------------------------------------------------------- */ - panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords); - - for( i = 0; i < psSHP->nRecords; i++ ) - { - panSHX[i*2 ] = psSHP->panRecOffset[i]/2; - panSHX[i*2+1] = psSHP->panRecSize[i]/2; - if( !bBigEndian ) SwapWord( 4, panSHX+i*2 ); - if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); - } - - fwrite( panSHX, sizeof(int32) * 2, psSHP->nRecords, psSHP->fpSHX ); - - free( panSHX ); -} - -/************************************************************************/ -/* SHPOpen() */ -/* */ -/* Open the .shp and .shx files based on the basename of the */ -/* files or either file name. */ -/************************************************************************/ - -SHPHandle SHPAPI_CALL -SHPOpen( const char * pszLayer, const char * pszAccess ) - -{ - char *pszFullname, *pszBasename; - SHPHandle psSHP; - - uchar *pabyBuf; - int i; - double dValue; - -/* -------------------------------------------------------------------- */ -/* Ensure the access string is one of the legal ones. We */ -/* ensure the result string indicates binary to avoid common */ -/* problems on Windows. */ -/* -------------------------------------------------------------------- */ - if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 - || strcmp(pszAccess,"r+") == 0 ) - pszAccess = "r+b"; - else - pszAccess = "rb"; - -/* -------------------------------------------------------------------- */ -/* Establish the byte order on this machine. */ -/* -------------------------------------------------------------------- */ - i = 1; - if( *((uchar *) &i) == 1 ) - bBigEndian = FALSE; - else - bBigEndian = TRUE; - -/* -------------------------------------------------------------------- */ -/* Initialize the info structure. */ -/* -------------------------------------------------------------------- */ - psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); - - psSHP->bUpdated = FALSE; - -/* -------------------------------------------------------------------- */ -/* Compute the base (layer) name. If there is any extension */ -/* on the passed in filename we will strip it off. */ -/* -------------------------------------------------------------------- */ - pszBasename = (char *) malloc(strlen(pszLayer)+5); - strcpy( pszBasename, pszLayer ); - for( i = strlen(pszBasename)-1; - i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' - && pszBasename[i] != '\\'; - i-- ) {} - - if( pszBasename[i] == '.' ) - pszBasename[i] = '\0'; - -/* -------------------------------------------------------------------- */ -/* Open the .shp and .shx files. Note that files pulled from */ -/* a PC to Unix with upper case filenames won't work! */ -/* -------------------------------------------------------------------- */ - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.shp", pszBasename ); - psSHP->fpSHP = fopen(pszFullname, pszAccess ); - if( psSHP->fpSHP == NULL ) - { - sprintf( pszFullname, "%s.SHP", pszBasename ); - psSHP->fpSHP = fopen(pszFullname, pszAccess ); - } - - if( psSHP->fpSHP == NULL ) - { - free( psSHP ); - free( pszBasename ); - free( pszFullname ); - return( NULL ); - } - - sprintf( pszFullname, "%s.shx", pszBasename ); - psSHP->fpSHX = fopen(pszFullname, pszAccess ); - if( psSHP->fpSHX == NULL ) - { - sprintf( pszFullname, "%s.SHX", pszBasename ); - psSHP->fpSHX = fopen(pszFullname, pszAccess ); - } - - if( psSHP->fpSHX == NULL ) - { - fclose( psSHP->fpSHP ); - free( psSHP ); - free( pszBasename ); - free( pszFullname ); - return( NULL ); - } - - free( pszFullname ); - free( pszBasename ); - -/* -------------------------------------------------------------------- */ -/* Read the file size from the SHP file. */ -/* -------------------------------------------------------------------- */ - pabyBuf = (uchar *) malloc(100); - fread( pabyBuf, 100, 1, psSHP->fpSHP ); - - psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256 - + pabyBuf[25] * 256 * 256 - + pabyBuf[26] * 256 - + pabyBuf[27]) * 2; - -/* -------------------------------------------------------------------- */ -/* Read SHX file Header info */ -/* -------------------------------------------------------------------- */ - fread( pabyBuf, 100, 1, psSHP->fpSHX ); - - if( pabyBuf[0] != 0 - || pabyBuf[1] != 0 - || pabyBuf[2] != 0x27 - || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) - { - fclose( psSHP->fpSHP ); - fclose( psSHP->fpSHX ); - free( psSHP ); - - return( NULL ); - } - - psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 - + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; - psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; - - psSHP->nShapeType = pabyBuf[32]; - - if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) - { - /* this header appears to be corrupt. Give up. */ - fclose( psSHP->fpSHP ); - fclose( psSHP->fpSHX ); - free( psSHP ); - - return( NULL ); - } - -/* -------------------------------------------------------------------- */ -/* Read the bounds. */ -/* -------------------------------------------------------------------- */ - if( bBigEndian ) SwapWord( 8, pabyBuf+36 ); - memcpy( &dValue, pabyBuf+36, 8 ); - psSHP->adBoundsMin[0] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+44 ); - memcpy( &dValue, pabyBuf+44, 8 ); - psSHP->adBoundsMin[1] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+52 ); - memcpy( &dValue, pabyBuf+52, 8 ); - psSHP->adBoundsMax[0] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+60 ); - memcpy( &dValue, pabyBuf+60, 8 ); - psSHP->adBoundsMax[1] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ - memcpy( &dValue, pabyBuf+68, 8 ); - psSHP->adBoundsMin[2] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); - memcpy( &dValue, pabyBuf+76, 8 ); - psSHP->adBoundsMax[2] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ - memcpy( &dValue, pabyBuf+84, 8 ); - psSHP->adBoundsMin[3] = dValue; - - if( bBigEndian ) SwapWord( 8, pabyBuf+92 ); - memcpy( &dValue, pabyBuf+92, 8 ); - psSHP->adBoundsMax[3] = dValue; - - free( pabyBuf ); - -/* -------------------------------------------------------------------- */ -/* Read the .shx file to get the offsets to each record in */ -/* the .shp file. */ -/* -------------------------------------------------------------------- */ - psSHP->nMaxRecords = psSHP->nRecords; - - psSHP->panRecOffset = - (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); - psSHP->panRecSize = - (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); - - pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); - fread( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ); - - for( i = 0; i < psSHP->nRecords; i++ ) - { - int32 nOffset, nLength; - - memcpy( &nOffset, pabyBuf + i * 8, 4 ); - if( !bBigEndian ) SwapWord( 4, &nOffset ); - - memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); - if( !bBigEndian ) SwapWord( 4, &nLength ); - - psSHP->panRecOffset[i] = nOffset*2; - psSHP->panRecSize[i] = nLength*2; - } - free( pabyBuf ); - - return( psSHP ); -} - -/************************************************************************/ -/* SHPClose() */ -/* */ -/* Close the .shp and .shx files. */ -/************************************************************************/ - -void SHPAPI_CALL -SHPClose(SHPHandle psSHP ) - -{ -/* -------------------------------------------------------------------- */ -/* Update the header if we have modified anything. */ -/* -------------------------------------------------------------------- */ - if( psSHP->bUpdated ) - { - SHPWriteHeader( psSHP ); - } - -/* -------------------------------------------------------------------- */ -/* Free all resources, and close files. */ -/* -------------------------------------------------------------------- */ - free( psSHP->panRecOffset ); - free( psSHP->panRecSize ); - - fclose( psSHP->fpSHX ); - fclose( psSHP->fpSHP ); - - if( psSHP->pabyRec != NULL ) - { - free( psSHP->pabyRec ); - } - - free( psSHP ); -} - -/************************************************************************/ -/* SHPGetInfo() */ -/* */ -/* Fetch general information about the shape file. */ -/************************************************************************/ - -void SHPAPI_CALL -SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, - double * padfMinBound, double * padfMaxBound ) - -{ - int i; - - if( pnEntities != NULL ) - *pnEntities = psSHP->nRecords; - - if( pnShapeType != NULL ) - *pnShapeType = psSHP->nShapeType; - - for( i = 0; i < 4; i++ ) - { - if( padfMinBound != NULL ) - padfMinBound[i] = psSHP->adBoundsMin[i]; - if( padfMaxBound != NULL ) - padfMaxBound[i] = psSHP->adBoundsMax[i]; - } -} - -/************************************************************************/ -/* SHPCreate() */ -/* */ -/* Create a new shape file and return a handle to the open */ -/* shape file with read/write access. */ -/************************************************************************/ - -SHPHandle SHPAPI_CALL -SHPCreate( const char * pszLayer, int nShapeType ) - -{ - char *pszBasename, *pszFullname; - int i; - FILE *fpSHP, *fpSHX; - uchar abyHeader[100]; - int32 i32; - double dValue; - -/* -------------------------------------------------------------------- */ -/* Establish the byte order on this system. */ -/* -------------------------------------------------------------------- */ - i = 1; - if( *((uchar *) &i) == 1 ) - bBigEndian = FALSE; - else - bBigEndian = TRUE; - -/* -------------------------------------------------------------------- */ -/* Compute the base (layer) name. If there is any extension */ -/* on the passed in filename we will strip it off. */ -/* -------------------------------------------------------------------- */ - pszBasename = (char *) malloc(strlen(pszLayer)+5); - strcpy( pszBasename, pszLayer ); - for( i = strlen(pszBasename)-1; - i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' - && pszBasename[i] != '\\'; - i-- ) {} - - if( pszBasename[i] == '.' ) - pszBasename[i] = '\0'; - -/* -------------------------------------------------------------------- */ -/* Open the two files so we can write their headers. */ -/* -------------------------------------------------------------------- */ - pszFullname = (char *) malloc(strlen(pszBasename) + 5); - sprintf( pszFullname, "%s.shp", pszBasename ); - fpSHP = fopen(pszFullname, "wb" ); - if( fpSHP == NULL ) - return( NULL ); - - sprintf( pszFullname, "%s.shx", pszBasename ); - fpSHX = fopen(pszFullname, "wb" ); - if( fpSHX == NULL ) - return( NULL ); - - free( pszFullname ); - free( pszBasename ); - -/* -------------------------------------------------------------------- */ -/* Prepare header block for .shp file. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < 100; i++ ) - abyHeader[i] = 0; - - abyHeader[2] = 0x27; /* magic cookie */ - abyHeader[3] = 0x0a; - - i32 = 50; /* file size */ - ByteCopy( &i32, abyHeader+24, 4 ); - if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - - i32 = 1000; /* version */ - ByteCopy( &i32, abyHeader+28, 4 ); - if( bBigEndian ) SwapWord( 4, abyHeader+28 ); - - i32 = nShapeType; /* shape type */ - ByteCopy( &i32, abyHeader+32, 4 ); - if( bBigEndian ) SwapWord( 4, abyHeader+32 ); - - dValue = 0.0; /* set bounds */ - ByteCopy( &dValue, abyHeader+36, 8 ); - ByteCopy( &dValue, abyHeader+44, 8 ); - ByteCopy( &dValue, abyHeader+52, 8 ); - ByteCopy( &dValue, abyHeader+60, 8 ); - -/* -------------------------------------------------------------------- */ -/* Write .shp file header. */ -/* -------------------------------------------------------------------- */ - fwrite( abyHeader, 100, 1, fpSHP ); - -/* -------------------------------------------------------------------- */ -/* Prepare, and write .shx file header. */ -/* -------------------------------------------------------------------- */ - i32 = 50; /* file size */ - ByteCopy( &i32, abyHeader+24, 4 ); - if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); - - fwrite( abyHeader, 100, 1, fpSHX ); - -/* -------------------------------------------------------------------- */ -/* Close the files, and then open them as regular existing files. */ -/* -------------------------------------------------------------------- */ - fclose( fpSHP ); - fclose( fpSHX ); - - return( SHPOpen( pszLayer, "r+b" ) ); -} - -/************************************************************************/ -/* _SHPSetBounds() */ -/* */ -/* Compute a bounds rectangle for a shape, and set it into the */ -/* indicated location in the record. */ -/************************************************************************/ - -static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape ) - -{ - ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 ); - ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 ); - ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 ); - ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 ); - - if( bBigEndian ) - { - SwapWord( 8, pabyRec + 0 ); - SwapWord( 8, pabyRec + 8 ); - SwapWord( 8, pabyRec + 16 ); - SwapWord( 8, pabyRec + 24 ); - } -} - -/************************************************************************/ -/* SHPComputeExtents() */ -/* */ -/* Recompute the extents of a shape. Automatically done by */ -/* SHPCreateObject(). */ -/************************************************************************/ - -void SHPAPI_CALL -SHPComputeExtents( SHPObject * psObject ) - -{ - int i; - -/* -------------------------------------------------------------------- */ -/* Build extents for this object. */ -/* -------------------------------------------------------------------- */ - if( psObject->nVertices > 0 ) - { - psObject->dfXMin = psObject->dfXMax = psObject->padfX[0]; - psObject->dfYMin = psObject->dfYMax = psObject->padfY[0]; - psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; - psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; - } - - for( i = 0; i < psObject->nVertices; i++ ) - { - psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); - psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]); - psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]); - psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]); - - psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]); - psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]); - psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]); - psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]); - } -} - -/************************************************************************/ -/* SHPCreateObject() */ -/* */ -/* Create a shape object. It should be freed with */ -/* SHPDestroyObject(). */ -/************************************************************************/ - -SHPObject SHPAPI_CALL1(*) -SHPCreateObject( int nSHPType, int nShapeId, int nParts, - int * panPartStart, int * panPartType, - int nVertices, double * padfX, double * padfY, - double * padfZ, double * padfM ) - -{ - SHPObject *psObject; - int i, bHasM, bHasZ; - - psObject = (SHPObject *) calloc(1,sizeof(SHPObject)); - psObject->nSHPType = nSHPType; - psObject->nShapeId = nShapeId; - -/* -------------------------------------------------------------------- */ -/* Establish whether this shape type has M, and Z values. */ -/* -------------------------------------------------------------------- */ - if( nSHPType == SHPT_ARCM - || nSHPType == SHPT_POINTM - || nSHPType == SHPT_POLYGONM - || nSHPType == SHPT_MULTIPOINTM ) - { - bHasM = TRUE; - bHasZ = FALSE; - } - else if( nSHPType == SHPT_ARCZ - || nSHPType == SHPT_POINTZ - || nSHPType == SHPT_POLYGONZ - || nSHPType == SHPT_MULTIPOINTZ - || nSHPType == SHPT_MULTIPATCH ) - { - bHasM = TRUE; - bHasZ = TRUE; - } - else - { - bHasM = FALSE; - bHasZ = FALSE; - } - -/* -------------------------------------------------------------------- */ -/* Capture parts. Note that part type is optional, and */ -/* defaults to ring. */ -/* -------------------------------------------------------------------- */ - if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON - || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM - || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ - || nSHPType == SHPT_MULTIPATCH ) - { - psObject->nParts = MAX(1,nParts); - - psObject->panPartStart = (int *) - malloc(sizeof(int) * psObject->nParts); - psObject->panPartType = (int *) - malloc(sizeof(int) * psObject->nParts); - - psObject->panPartStart[0] = 0; - psObject->panPartType[0] = SHPP_RING; - - for( i = 0; i < nParts; i++ ) - { - psObject->panPartStart[i] = panPartStart[i]; - if( panPartType != NULL ) - psObject->panPartType[i] = panPartType[i]; - else - psObject->panPartType[i] = SHPP_RING; - } - } - -/* -------------------------------------------------------------------- */ -/* Capture vertices. Note that Z and M are optional, but X and */ -/* Y are not. */ -/* -------------------------------------------------------------------- */ - if( nVertices > 0 ) - { - psObject->padfX = (double *) calloc(sizeof(double),nVertices); - psObject->padfY = (double *) calloc(sizeof(double),nVertices); - psObject->padfZ = (double *) calloc(sizeof(double),nVertices); - psObject->padfM = (double *) calloc(sizeof(double),nVertices); - - assert( padfX != NULL ); - assert( padfY != NULL ); - - for( i = 0; i < nVertices; i++ ) - { - psObject->padfX[i] = padfX[i]; - psObject->padfY[i] = padfY[i]; - if( padfZ != NULL && bHasZ ) - psObject->padfZ[i] = padfZ[i]; - if( padfM != NULL && bHasM ) - psObject->padfM[i] = padfM[i]; - } - } - -/* -------------------------------------------------------------------- */ -/* Compute the extents. */ -/* -------------------------------------------------------------------- */ - psObject->nVertices = nVertices; - SHPComputeExtents( psObject ); - - return( psObject ); -} - -/************************************************************************/ -/* SHPCreateSimpleObject() */ -/* */ -/* Create a simple (common) shape object. Destroy with */ -/* SHPDestroyObject(). */ -/************************************************************************/ - -SHPObject SHPAPI_CALL1(*) -SHPCreateSimpleObject( int nSHPType, int nVertices, - double * padfX, double * padfY, - double * padfZ ) - -{ - return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, - nVertices, padfX, padfY, padfZ, NULL ) ); -} - -/************************************************************************/ -/* SHPWriteObject() */ -/* */ -/* Write out the vertices of a new structure. Note that it is */ -/* only possible to write vertices at the end of the file. */ -/************************************************************************/ - -int SHPAPI_CALL -SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) - -{ - int nRecordOffset, i, nRecordSize; - uchar *pabyRec; - int32 i32; - - psSHP->bUpdated = TRUE; - -/* -------------------------------------------------------------------- */ -/* Ensure that shape object matches the type of the file it is */ -/* being written to. */ -/* -------------------------------------------------------------------- */ - assert( psObject->nSHPType == psSHP->nShapeType - || psObject->nSHPType == SHPT_NULL ); - -/* -------------------------------------------------------------------- */ -/* Ensure that -1 is used for appends. Either blow an */ -/* assertion, or if they are disabled, set the shapeid to -1 */ -/* for appends. */ -/* -------------------------------------------------------------------- */ - assert( nShapeId == -1 - || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); - - if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) - nShapeId = -1; - -/* -------------------------------------------------------------------- */ -/* Add the new entity to the in memory index. */ -/* -------------------------------------------------------------------- */ - if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords ) - { - psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100); - - psSHP->panRecOffset = (int *) - SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords ); - psSHP->panRecSize = (int *) - SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords ); - } - -/* -------------------------------------------------------------------- */ -/* Initialize record. */ -/* -------------------------------------------------------------------- */ - pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) - + psObject->nParts * 8 + 128); - -/* -------------------------------------------------------------------- */ -/* Extract vertices for a Polygon or Arc. */ -/* -------------------------------------------------------------------- */ - if( psObject->nSHPType == SHPT_POLYGON - || psObject->nSHPType == SHPT_POLYGONZ - || psObject->nSHPType == SHPT_POLYGONM - || psObject->nSHPType == SHPT_ARC - || psObject->nSHPType == SHPT_ARCZ - || psObject->nSHPType == SHPT_ARCM - || psObject->nSHPType == SHPT_MULTIPATCH ) - { - int32 nPoints, nParts; - int i; - - nPoints = psObject->nVertices; - nParts = psObject->nParts; - - _SHPSetBounds( pabyRec + 12, psObject ); - - if( bBigEndian ) SwapWord( 4, &nPoints ); - if( bBigEndian ) SwapWord( 4, &nParts ); - - ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); - ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); - - nRecordSize = 52; - - /* - * Write part start positions. - */ - ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, - 4 * psObject->nParts ); - for( i = 0; i < psObject->nParts; i++ ) - { - if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); - nRecordSize += 4; - } - - /* - * Write multipatch part types if needed. - */ - if( psObject->nSHPType == SHPT_MULTIPATCH ) - { - memcpy( pabyRec + nRecordSize, psObject->panPartType, - 4*psObject->nParts ); - for( i = 0; i < psObject->nParts; i++ ) - { - if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize ); - nRecordSize += 4; - } - } - - /* - * Write the (x,y) vertex values. - */ - for( i = 0; i < psObject->nVertices; i++ ) - { - ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); - ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); - - if( bBigEndian ) - SwapWord( 8, pabyRec + nRecordSize ); - - if( bBigEndian ) - SwapWord( 8, pabyRec + nRecordSize + 8 ); - - nRecordSize += 2 * 8; - } - - /* - * Write the Z coordinates (if any). - */ - if( psObject->nSHPType == SHPT_POLYGONZ - || psObject->nSHPType == SHPT_ARCZ - || psObject->nSHPType == SHPT_MULTIPATCH ) - { - ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - - ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - - for( i = 0; i < psObject->nVertices; i++ ) - { - ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - } - } - - /* - * Write the M values, if any. - */ - if( psObject->nSHPType == SHPT_POLYGONM - || psObject->nSHPType == SHPT_ARCM -#ifndef DISABLE_MULTIPATCH_MEASURE - || psObject->nSHPType == SHPT_MULTIPATCH -#endif - || psObject->nSHPType == SHPT_POLYGONZ - || psObject->nSHPType == SHPT_ARCZ ) - { - ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - - ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - - for( i = 0; i < psObject->nVertices; i++ ) - { - ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - } - } - } - -/* -------------------------------------------------------------------- */ -/* Extract vertices for a MultiPoint. */ -/* -------------------------------------------------------------------- */ - else if( psObject->nSHPType == SHPT_MULTIPOINT - || psObject->nSHPType == SHPT_MULTIPOINTZ - || psObject->nSHPType == SHPT_MULTIPOINTM ) - { - int32 nPoints; - int i; - - nPoints = psObject->nVertices; - - _SHPSetBounds( pabyRec + 12, psObject ); - - if( bBigEndian ) SwapWord( 4, &nPoints ); - ByteCopy( &nPoints, pabyRec + 44, 4 ); - - for( i = 0; i < psObject->nVertices; i++ ) - { - ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); - ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); - - if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); - if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); - } - - nRecordSize = 48 + 16 * psObject->nVertices; - - if( psObject->nSHPType == SHPT_MULTIPOINTZ ) - { - ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - - ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - - for( i = 0; i < psObject->nVertices; i++ ) - { - ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - } - } - - if( psObject->nSHPType == SHPT_MULTIPOINTZ - || psObject->nSHPType == SHPT_MULTIPOINTM ) - { - ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - - ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - - for( i = 0; i < psObject->nVertices; i++ ) - { - ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - } - } - } - -/* -------------------------------------------------------------------- */ -/* Write point. */ -/* -------------------------------------------------------------------- */ - else if( psObject->nSHPType == SHPT_POINT - || psObject->nSHPType == SHPT_POINTZ - || psObject->nSHPType == SHPT_POINTM ) - { - ByteCopy( psObject->padfX, pabyRec + 12, 8 ); - ByteCopy( psObject->padfY, pabyRec + 20, 8 ); - - if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); - if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); - - nRecordSize = 28; - - if( psObject->nSHPType == SHPT_POINTZ ) - { - ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - } - - if( psObject->nSHPType == SHPT_POINTZ - || psObject->nSHPType == SHPT_POINTM ) - { - ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); - if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); - nRecordSize += 8; - } - } - -/* -------------------------------------------------------------------- */ -/* Not much to do for null geometries. */ -/* -------------------------------------------------------------------- */ - else if( psObject->nSHPType == SHPT_NULL ) - { - nRecordSize = 12; - } - - else - { - /* unknown type */ - assert( FALSE ); - } - -/* -------------------------------------------------------------------- */ -/* Establish where we are going to put this record. If we are */ -/* rewriting and existing record, and it will fit, then put it */ -/* back where the original came from. Otherwise write at the end. */ -/* -------------------------------------------------------------------- */ - if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) - { - if( nShapeId == -1 ) - nShapeId = psSHP->nRecords++; - - psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize; - psSHP->panRecSize[nShapeId] = nRecordSize-8; - psSHP->nFileSize += nRecordSize; - } - else - { - nRecordOffset = psSHP->panRecOffset[nShapeId]; - } - -/* -------------------------------------------------------------------- */ -/* Set the shape type, record number, and record size. */ -/* -------------------------------------------------------------------- */ - i32 = nShapeId+1; /* record # */ - if( !bBigEndian ) SwapWord( 4, &i32 ); - ByteCopy( &i32, pabyRec, 4 ); - - i32 = (nRecordSize-8)/2; /* record size */ - if( !bBigEndian ) SwapWord( 4, &i32 ); - ByteCopy( &i32, pabyRec + 4, 4 ); - - i32 = psObject->nSHPType; /* shape type */ - if( bBigEndian ) SwapWord( 4, &i32 ); - ByteCopy( &i32, pabyRec + 8, 4 ); - -/* -------------------------------------------------------------------- */ -/* Write out record. */ -/* -------------------------------------------------------------------- */ - if( fseek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 - || fwrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) - { - //printf( "Error in fseek() or fwrite().\n" ); - //ninjaCom(ninjaComClass::ninjaFailure, "Error in fseek() or fwrite()." ); - free( pabyRec ); - return -1; - } - - free( pabyRec ); - -/* -------------------------------------------------------------------- */ -/* Expand file wide bounds based on this shape. */ -/* -------------------------------------------------------------------- */ - if( psSHP->adBoundsMin[0] == 0.0 - && psSHP->adBoundsMax[0] == 0.0 - && psSHP->adBoundsMin[1] == 0.0 - && psSHP->adBoundsMax[1] == 0.0 - && psObject->nSHPType != SHPT_NULL ) - { - psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; - psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; - psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0]; - psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0]; - } - - for( i = 0; i < psObject->nVertices; i++ ) - { - psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); - psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); - psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); - psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); - psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); - psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); - psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); - psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); - } - - return( nShapeId ); -} - -/************************************************************************/ -/* SHPReadObject() */ -/* */ -/* Read the vertices, parts, and other non-attribute information */ -/* for one shape. */ -/************************************************************************/ - -SHPObject SHPAPI_CALL1(*) -SHPReadObject( SHPHandle psSHP, int hEntity ) - -{ - SHPObject *psShape; - -/* -------------------------------------------------------------------- */ -/* Validate the record/entity number. */ -/* -------------------------------------------------------------------- */ - if( hEntity < 0 || hEntity >= psSHP->nRecords ) - return( NULL ); - -/* -------------------------------------------------------------------- */ -/* Ensure our record buffer is large enough. */ -/* -------------------------------------------------------------------- */ - if( psSHP->panRecSize[hEntity]+8 > psSHP->nBufSize ) - { - psSHP->nBufSize = psSHP->panRecSize[hEntity]+8; - psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,psSHP->nBufSize); - } - -/* -------------------------------------------------------------------- */ -/* Read the record. */ -/* -------------------------------------------------------------------- */ - fseek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ); - fread( psSHP->pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP ); - -/* -------------------------------------------------------------------- */ -/* Allocate and minimally initialize the object. */ -/* -------------------------------------------------------------------- */ - psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); - psShape->nShapeId = hEntity; - - memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 ); - if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) ); - -/* ==================================================================== */ -/* Extract vertices for a Polygon or Arc. */ -/* ==================================================================== */ - if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC - || psShape->nSHPType == SHPT_POLYGONZ - || psShape->nSHPType == SHPT_POLYGONM - || psShape->nSHPType == SHPT_ARCZ - || psShape->nSHPType == SHPT_ARCM - || psShape->nSHPType == SHPT_MULTIPATCH ) - { - int32 nPoints, nParts; - int i, nOffset; - -/* -------------------------------------------------------------------- */ -/* Get the X/Y bounds. */ -/* -------------------------------------------------------------------- */ - memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); - memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); - memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); - memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); - - if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); - -/* -------------------------------------------------------------------- */ -/* Extract part/point count, and build vertex and part arrays */ -/* to proper size. */ -/* -------------------------------------------------------------------- */ - memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); - memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); - - if( bBigEndian ) SwapWord( 4, &nPoints ); - if( bBigEndian ) SwapWord( 4, &nParts ); - - psShape->nVertices = nPoints; - psShape->padfX = (double *) calloc(nPoints,sizeof(double)); - psShape->padfY = (double *) calloc(nPoints,sizeof(double)); - psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); - psShape->padfM = (double *) calloc(nPoints,sizeof(double)); - - psShape->nParts = nParts; - psShape->panPartStart = (int *) calloc(nParts,sizeof(int)); - psShape->panPartType = (int *) calloc(nParts,sizeof(int)); - - for( i = 0; i < nParts; i++ ) - psShape->panPartType[i] = SHPP_RING; - -/* -------------------------------------------------------------------- */ -/* Copy out the part array from the record. */ -/* -------------------------------------------------------------------- */ - memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); - for( i = 0; i < nParts; i++ ) - { - if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); - } - - nOffset = 44 + 8 + 4*nParts; - -/* -------------------------------------------------------------------- */ -/* If this is a multipatch, we will also have parts types. */ -/* -------------------------------------------------------------------- */ - if( psShape->nSHPType == SHPT_MULTIPATCH ) - { - memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); - for( i = 0; i < nParts; i++ ) - { - if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); - } - - nOffset += 4*nParts; - } - -/* -------------------------------------------------------------------- */ -/* Copy out the vertices from the record. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < nPoints; i++ ) - { - memcpy(psShape->padfX + i, - psSHP->pabyRec + nOffset + i * 16, - 8 ); - - memcpy(psShape->padfY + i, - psSHP->pabyRec + nOffset + i * 16 + 8, - 8 ); - - if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); - if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); - } - - nOffset += 16*nPoints; - -/* -------------------------------------------------------------------- */ -/* If we have a Z coordinate, collect that now. */ -/* -------------------------------------------------------------------- */ - if( psShape->nSHPType == SHPT_POLYGONZ - || psShape->nSHPType == SHPT_ARCZ - || psShape->nSHPType == SHPT_MULTIPATCH ) - { - memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); - memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); - - if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); - - for( i = 0; i < nPoints; i++ ) - { - memcpy( psShape->padfZ + i, - psSHP->pabyRec + nOffset + 16 + i*8, 8 ); - if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); - } - - nOffset += 16 + 8*nPoints; - } - -/* -------------------------------------------------------------------- */ -/* If we have a M measure value, then read it now. We assume */ -/* that the measure can be present for any shape if the size is */ -/* big enough, but really it will only occur for the Z shapes */ -/* (options), and the M shapes. */ -/* -------------------------------------------------------------------- */ - if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints ) - { - memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); - memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); - - if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); - - for( i = 0; i < nPoints; i++ ) - { - memcpy( psShape->padfM + i, - psSHP->pabyRec + nOffset + 16 + i*8, 8 ); - if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); - } - } - - } - -/* ==================================================================== */ -/* Extract vertices for a MultiPoint. */ -/* ==================================================================== */ - else if( psShape->nSHPType == SHPT_MULTIPOINT - || psShape->nSHPType == SHPT_MULTIPOINTM - || psShape->nSHPType == SHPT_MULTIPOINTZ ) - { - int32 nPoints; - int i, nOffset; - - memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); - if( bBigEndian ) SwapWord( 4, &nPoints ); - - psShape->nVertices = nPoints; - psShape->padfX = (double *) calloc(nPoints,sizeof(double)); - psShape->padfY = (double *) calloc(nPoints,sizeof(double)); - psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); - psShape->padfM = (double *) calloc(nPoints,sizeof(double)); - - for( i = 0; i < nPoints; i++ ) - { - memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); - memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); - - if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); - if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); - } - - nOffset = 48 + 16*nPoints; - -/* -------------------------------------------------------------------- */ -/* Get the X/Y bounds. */ -/* -------------------------------------------------------------------- */ - memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); - memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); - memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); - memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); - - if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); - -/* -------------------------------------------------------------------- */ -/* If we have a Z coordinate, collect that now. */ -/* -------------------------------------------------------------------- */ - if( psShape->nSHPType == SHPT_MULTIPOINTZ ) - { - memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); - memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); - - if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); - - for( i = 0; i < nPoints; i++ ) - { - memcpy( psShape->padfZ + i, - psSHP->pabyRec + nOffset + 16 + i*8, 8 ); - if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); - } - - nOffset += 16 + 8*nPoints; - } - -/* -------------------------------------------------------------------- */ -/* If we have a M measure value, then read it now. We assume */ -/* that the measure can be present for any shape if the size is */ -/* big enough, but really it will only occur for the Z shapes */ -/* (options), and the M shapes. */ -/* -------------------------------------------------------------------- */ - if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints ) - { - memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); - memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); - - if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); - if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); - - for( i = 0; i < nPoints; i++ ) - { - memcpy( psShape->padfM + i, - psSHP->pabyRec + nOffset + 16 + i*8, 8 ); - if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); - } - } - } - -/* ==================================================================== */ -/* Extract vertices for a point. */ -/* ==================================================================== */ - else if( psShape->nSHPType == SHPT_POINT - || psShape->nSHPType == SHPT_POINTM - || psShape->nSHPType == SHPT_POINTZ ) - { - int nOffset; - - psShape->nVertices = 1; - psShape->padfX = (double *) calloc(1,sizeof(double)); - psShape->padfY = (double *) calloc(1,sizeof(double)); - psShape->padfZ = (double *) calloc(1,sizeof(double)); - psShape->padfM = (double *) calloc(1,sizeof(double)); - - memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); - memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); - - if( bBigEndian ) SwapWord( 8, psShape->padfX ); - if( bBigEndian ) SwapWord( 8, psShape->padfY ); - - nOffset = 20 + 8; - -/* -------------------------------------------------------------------- */ -/* If we have a Z coordinate, collect that now. */ -/* -------------------------------------------------------------------- */ - if( psShape->nSHPType == SHPT_POINTZ ) - { - memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); - - if( bBigEndian ) SwapWord( 8, psShape->padfZ ); - - nOffset += 8; - } - -/* -------------------------------------------------------------------- */ -/* If we have a M measure value, then read it now. We assume */ -/* that the measure can be present for any shape if the size is */ -/* big enough, but really it will only occur for the Z shapes */ -/* (options), and the M shapes. */ -/* -------------------------------------------------------------------- */ - if( psSHP->panRecSize[hEntity]+8 >= nOffset + 8 ) - { - memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); - - if( bBigEndian ) SwapWord( 8, psShape->padfM ); - } - -/* -------------------------------------------------------------------- */ -/* Since no extents are supplied in the record, we will apply */ -/* them from the single vertex. */ -/* -------------------------------------------------------------------- */ - psShape->dfXMin = psShape->dfXMax = psShape->padfX[0]; - psShape->dfYMin = psShape->dfYMax = psShape->padfY[0]; - psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0]; - psShape->dfMMin = psShape->dfMMax = psShape->padfM[0]; - } - - return( psShape ); -} - -/************************************************************************/ -/* SHPTypeName() */ -/************************************************************************/ - -const char SHPAPI_CALL1(*) -SHPTypeName( int nSHPType ) - -{ - switch( nSHPType ) - { - case SHPT_NULL: - return "NullShape"; - - case SHPT_POINT: - return "Point"; - - case SHPT_ARC: - return "Arc"; - - case SHPT_POLYGON: - return "Polygon"; - - case SHPT_MULTIPOINT: - return "MultiPoint"; - - case SHPT_POINTZ: - return "PointZ"; - - case SHPT_ARCZ: - return "ArcZ"; - - case SHPT_POLYGONZ: - return "PolygonZ"; - - case SHPT_MULTIPOINTZ: - return "MultiPointZ"; - - case SHPT_POINTM: - return "PointM"; - - case SHPT_ARCM: - return "ArcM"; - - case SHPT_POLYGONM: - return "PolygonM"; - - case SHPT_MULTIPOINTM: - return "MultiPointM"; - - case SHPT_MULTIPATCH: - return "MultiPatch"; - - default: - return "UnknownShapeType"; - } -} - -/************************************************************************/ -/* SHPPartTypeName() */ -/************************************************************************/ - -const char SHPAPI_CALL1(*) -SHPPartTypeName( int nPartType ) - -{ - switch( nPartType ) - { - case SHPP_TRISTRIP: - return "TriangleStrip"; - - case SHPP_TRIFAN: - return "TriangleFan"; - - case SHPP_OUTERRING: - return "OuterRing"; - - case SHPP_INNERRING: - return "InnerRing"; - - case SHPP_FIRSTRING: - return "FirstRing"; - - case SHPP_RING: - return "Ring"; - - default: - return "UnknownPartType"; - } -} - -/************************************************************************/ -/* SHPDestroyObject() */ -/************************************************************************/ - -void SHPAPI_CALL -SHPDestroyObject( SHPObject * psShape ) - -{ - if( psShape == NULL ) - return; - - if( psShape->padfX != NULL ) - free( psShape->padfX ); - if( psShape->padfY != NULL ) - free( psShape->padfY ); - if( psShape->padfZ != NULL ) - free( psShape->padfZ ); - if( psShape->padfM != NULL ) - free( psShape->padfM ); - - if( psShape->panPartStart != NULL ) - free( psShape->panPartStart ); - if( psShape->panPartType != NULL ) - free( psShape->panPartType ); - - free( psShape ); -} - -/************************************************************************/ -/* SHPRewindObject() */ -/* */ -/* Reset the winding of polygon objects to adhere to the */ -/* specification. */ -/************************************************************************/ - -int SHPAPI_CALL -SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) - -{ - int iOpRing, bAltered = 0; - -/* -------------------------------------------------------------------- */ -/* Do nothing if this is not a polygon object. */ -/* -------------------------------------------------------------------- */ - if( psObject->nSHPType != SHPT_POLYGON - && psObject->nSHPType != SHPT_POLYGONZ - && psObject->nSHPType != SHPT_POLYGONM ) - return 0; - -/* -------------------------------------------------------------------- */ -/* Process each of the rings. */ -/* -------------------------------------------------------------------- */ - for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ ) - { - int bInner, iVert, nVertCount, nVertStart, iCheckRing; - double dfSum, dfTestX, dfTestY; - -/* -------------------------------------------------------------------- */ -/* Determine if this ring is an inner ring or an outer ring */ -/* relative to all the other rings. For now we assume the */ -/* first ring is outer and all others are inner, but eventually */ -/* we need to fix this to handle multiple island polygons and */ -/* unordered sets of rings. */ -/* -------------------------------------------------------------------- */ - dfTestX = psObject->padfX[psObject->panPartStart[iOpRing]]; - dfTestY = psObject->padfY[psObject->panPartStart[iOpRing]]; - - bInner = FALSE; - for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ ) - { - int iEdge; - - if( iCheckRing == iOpRing ) - continue; - - nVertStart = psObject->panPartStart[iCheckRing]; - - if( iCheckRing == psObject->nParts-1 ) - nVertCount = psObject->nVertices - - psObject->panPartStart[iCheckRing]; - else - nVertCount = psObject->panPartStart[iCheckRing+1] - - psObject->panPartStart[iCheckRing]; - - for( iEdge = 0; iEdge < nVertCount; iEdge++ ) - { - int iNext; - - if( iEdge < nVertCount-1 ) - iNext = iEdge+1; - else - iNext = 0; - - if( (psObject->padfY[iEdge+nVertStart] < dfTestY - && psObject->padfY[iNext+nVertStart] >= dfTestY) - || (psObject->padfY[iNext+nVertStart] < dfTestY - && psObject->padfY[iEdge+nVertStart] >= dfTestY) ) - { - if( psObject->padfX[iEdge+nVertStart] - + (dfTestY - psObject->padfY[iEdge+nVertStart]) - / (psObject->padfY[iNext+nVertStart] - - psObject->padfY[iEdge+nVertStart]) - * (psObject->padfX[iNext+nVertStart] - - psObject->padfX[iEdge+nVertStart]) < dfTestX ) - bInner = !bInner; - } - } - } - -/* -------------------------------------------------------------------- */ -/* Determine the current order of this ring so we will know if */ -/* it has to be reversed. */ -/* -------------------------------------------------------------------- */ - nVertStart = psObject->panPartStart[iOpRing]; - - if( iOpRing == psObject->nParts-1 ) - nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; - else - nVertCount = psObject->panPartStart[iOpRing+1] - - psObject->panPartStart[iOpRing]; - - dfSum = 0.0; - for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ ) - { - dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1] - - psObject->padfY[iVert] * psObject->padfX[iVert+1]; - } - - dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart] - - psObject->padfY[iVert] * psObject->padfX[nVertStart]; - -/* -------------------------------------------------------------------- */ -/* Reverse if necessary. */ -/* -------------------------------------------------------------------- */ - if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) ) - { - int i; - - bAltered++; - for( i = 0; i < nVertCount/2; i++ ) - { - double dfSaved; - - /* Swap X */ - dfSaved = psObject->padfX[nVertStart+i]; - psObject->padfX[nVertStart+i] = - psObject->padfX[nVertStart+nVertCount-i-1]; - psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; - - /* Swap Y */ - dfSaved = psObject->padfY[nVertStart+i]; - psObject->padfY[nVertStart+i] = - psObject->padfY[nVertStart+nVertCount-i-1]; - psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; - - /* Swap Z */ - if( psObject->padfZ ) - { - dfSaved = psObject->padfZ[nVertStart+i]; - psObject->padfZ[nVertStart+i] = - psObject->padfZ[nVertStart+nVertCount-i-1]; - psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; - } - - /* Swap M */ - if( psObject->padfM ) - { - dfSaved = psObject->padfM[nVertStart+i]; - psObject->padfM[nVertStart+i] = - psObject->padfM[nVertStart+nVertCount-i-1]; - psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; - } - } - } - } - - return bAltered; -} diff --git a/src/ninja/windninja.cpp b/src/ninja/windninja.cpp index 5db6e4c9b..5a9beebeb 100644 --- a/src/ninja/windninja.cpp +++ b/src/ninja/windninja.cpp @@ -29,8 +29,8 @@ #include "windninja.h" #include "ninjaArmy.h" +#include "ninjaTools.h" #include "ninjaException.h" -#include #ifdef _OPENMP omp_lock_t netCDF_lock; @@ -68,8 +68,21 @@ NinjaErr handleException() extern "C" { + /** - * \brief Create a new suite of domain average windninja runs. + * \brief Automatically allocate an empty ninjaArmy. + * + * \return An opaque handle to a ninjaArmy on success, NULL otherwise. + */ +WINDNINJADLL_EXPORT NinjaArmyH* NinjaInitializeArmy() +{ + NinjaArmyH* army; + army = reinterpret_cast(new ninjaArmy()); + return army; +} + +/** + * \brief Generate a new suite of domain average windninja runs. * * Use this method to create a finite, known number of runs for windninja. * There are other creation methods that automatically allocate the correct @@ -81,6 +94,7 @@ extern "C" * Avaliable Creation Options: * None * + * \param army An opaque handle to a valid ninjaArmy. * \param numNinjas The number of runs to create. * \param momentumFlag Flag specifying if the mass and momentum solver should be used. * \param speedList List of wind speeds to simulate. @@ -98,68 +112,22 @@ extern "C" * \param cloudCoverUnits String indicating cloud cover units ("fraction" or "percent"), can be NULL. * \param options Key, value option pairs from the options listed above, can be NULL. * - * \return An opaque handle to a ninjaArmy on success, NULL otherwise. + * \return NINJA_SUCCESS on success, non-zero otherwise. */ - -WINDNINJADLL_EXPORT NinjaArmyH* NinjaMakeDomainAverageArmy - ( unsigned int numNinjas, bool momentumFlag, const double * speedList, const char * speedUnits, const double * directionList, char ** options ) -// ( unsigned int numNinjas, bool momentumFlag, const double * speedList, const char * speedUnits, const double * directionList, const int * yearList, const int * monthList, const int * dayList, const int * hourList, -// const int * minuteList, const char * timeZone, const double * airTempList, const char * airTempUnits, const double * cloudCoverList, const char * cloudCoverUnits, char ** options ) +WINDNINJADLL_EXPORT NinjaErr NinjaMakeDomainAverageArmy + ( NinjaArmyH * army, int numNinjas, bool momentumFlag, const double * speedList, const char * speedUnits, const double * directionList, + const int * yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const char * timeZone, const double * airTempList, const char * airTempUnits, const double * cloudCoverList, const char * cloudCoverUnits, char ** options ) { - -#ifndef NINJAFOAM - if(momentumFlag == true) + if(!army) { - throw std::runtime_error("momentumFlag cannot be set to true. WindNinja was not compiled with mass and momentum support."); + return NINJA_E_NULL_PTR; } -#endif - - //Get the number of elements in the arrays -/* size_t length1 = sizeof(speedList) / sizeof(speedList[0]); - size_t length2 = sizeof(directionList) / sizeof(directionList[0]); */ -// size_t length1 = sizeof(yearList) / sizeof(yearList[0]); -// size_t length2 = sizeof(monthList) / sizeof(monthList[0]); -// size_t length3 = sizeof(dayList) / sizeof(dayList[0]); -// size_t length4 = sizeof(hourList) / sizeof(hourList[0]); -// size_t length5 = sizeof(minuteList) / sizeof(minuteList[0]); -// size_t length6 = sizeof(airTempList) / sizeof(airTempList[0]); -// size_t length7 = sizeof(cloudCoverList) / sizeof(cloudCoverList[0]); -// -// if(!(length1 == length2 == length3 == length4 == length5 == length6 == length7)) -// { -// throw std::runtime_error("yearList, monthList, dayList, hourList, minuteList, airTempList, and cloudCoverList must be the same length!"); -// - - NinjaArmyH* army; - - try - { - army = reinterpret_cast( new ninjaArmy() ); - reinterpret_cast( army )->makeDomainAverageArmy( numNinjas, momentumFlag); - for(int i=0; i( army )->getSize(); i++) - { - reinterpret_cast( army )->setInputSpeed( i, speedList[i], std::string( speedUnits ) ); - - reinterpret_cast( army )->setInputDirection( i, directionList[i] ); - -// reinterpret_cast( army )->setDateTime( i, yearList[i], monthList[i], dayList[i], hourList[i], minuteList[i], 0, timeZone ); -// -// reinterpret_cast( army )->setUniAirTemp( i, airTempList[i], std::string( airTempUnits ) ); -// -// reinterpret_cast( army )->setUniCloudCover( i, cloudCoverList[i], std::string( cloudCoverUnits ) ); - } - - return army; - } - catch( bad_alloc& ) - { - return NULL; - } + return reinterpret_cast( army )->NinjaMakeDomainAverageArmy(numNinjas, momentumFlag, speedList, speedUnits, directionList, yearList, monthList, dayList, hourList, minuteList, timeZone, airTempList, airTempUnits, cloudCoverList, cloudCoverUnits, options); } /** - * \brief Automatically allocate and generate a ninjaArmy from a weather station file. + * \brief Generate a ninjaArmy from a weather station file. * * This method will create a set of runs for windninja based on the contents of * a weather station file and list of datetimes specified by arrays of years, months, days, and hours @@ -169,6 +137,7 @@ WINDNINJADLL_EXPORT NinjaArmyH* NinjaMakeDomainAverageArmy * Avaliable Creation Options: * None * + * \param army An opaque handle to a valid ninjaArmy. * \param yearList A pointer to an array of years. * \param monthList A pointer to an array of months. * \param dayList A pointer to an array of days. @@ -181,53 +150,21 @@ WINDNINJADLL_EXPORT NinjaArmyH* NinjaMakeDomainAverageArmy * \param momentumFlag A flag representing whether to use the momentum solver or not (the momentum solver is not currently supported in point initializations). Default is false. * \param options Key, value option pairs from the options listed above, can be NULL. * - * \return An opaque handle to a ninjaArmy on success, NULL otherwise. + * \return NINJA_SUCCESS on success, non-zero otherwise. */ -WINDNINJADLL_EXPORT NinjaArmyH* NinjaMakePointArmy - ( int * yearList, int * monthList, int * dayList, int * hourList, int * minuteList, int size, char * timeZone, char * stationFileName, char * elevationFile, bool matchPointsFlag, bool momentumFlag, char ** options) +WINDNINJADLL_EXPORT NinjaErr NinjaMakePointArmy + ( NinjaArmyH * army, int * yearList, int * monthList, int * dayList, int * hourList, int * minuteList, int timeListSize, char * timeZone, const char ** stationFileNames, int numStationFiles, char * elevationFile, bool matchPointsFlag, bool momentumFlag, char ** options) { - if(momentumFlag == true) + if(!army) { - throw std::runtime_error("The momentum solver is not available for use with Point Initialization runs."); + return NINJA_E_NULL_PTR; } - //Get the number of elements in the arrays - - NinjaArmyH* army; - try{ - std::vector timeList; - for(size_t i=0; i sFiles; - sFiles.push_back(stationFileName); - pointInitialization::storeFileNames(sFiles); - - // TODO: Include check for using multiple .csv files - //sFiles=pointInitialization::openCSVList(vm["wx_station_filename"].as()); - //pointInitialization::storeFileNames(sFiles); - - army = reinterpret_cast( new ninjaArmy() ); - reinterpret_cast( army )->makePointArmy - ( timeList, - std::string(timeZone), - std::string(stationFileName), - std::string(elevationFile), - matchPointsFlag, - momentumFlag); - return army; - } - catch( armyException & e ){ - return NULL; - } - return NULL; + return reinterpret_cast( army )->NinjaMakePointArmy(yearList, monthList, dayList, hourList, minuteList, timeListSize, timeZone, stationFileNames, numStationFiles, elevationFile, matchPointsFlag, momentumFlag, options); } /** - * \brief Automatically allocate and generate a ninjaArmy from a forecast file. + * \brief Generate a ninjaArmy from a forecast file. * * This method will create a set of runs for windninja based on the contents of * the weather forecast file. One run is done for each timestep in the forecast @@ -237,6 +174,7 @@ WINDNINJADLL_EXPORT NinjaArmyH* NinjaMakePointArmy * None * TODO: include parameters for start/stop times and a list of timesteps as options->for cases where you don't want to simulate every time step in the forecast file * + * \param army An opaque handle to a valid ninjaArmy. * \param forecastFilename A valid NOMADS/UCAR based weather model file. * \param timezone a timezone string representing a valid timezone, e.g. * America/Boise. @@ -244,35 +182,168 @@ WINDNINJADLL_EXPORT NinjaArmyH* NinjaMakePointArmy * \param momentumFlag A flag representing whether to use the momentum solver or not. * \param options Key, value option pairs from the options listed above, can be NULL. * - * \return An opaque handle to a ninjaArmy on success, NULL otherwise. + * \return NINJA_SUCCESS on success, non-zero otherwise. */ -WINDNINJADLL_EXPORT NinjaArmyH* NinjaMakeWeatherModelArmy - ( const char * forecastFilename, const char * timezone, bool momentumFlag, char ** options ) +WINDNINJADLL_EXPORT NinjaErr NinjaMakeWeatherModelArmy + ( NinjaArmyH * army, const char * forecastFilename, const char * timeZone, const char** inputTimeList, int size, bool momentumFlag, char ** options ) { -#ifndef NINJAFOAM - if(momentumFlag == true) + if(!army) { - throw std::runtime_error("bMomentumFlag cannot be set to true. WindNinja was not compiled with mass and momentum support."); + return NINJA_E_NULL_PTR; } -#endif - NinjaArmyH* army; - try + return reinterpret_cast( army )->NinjaMakeWeatherModelArmy(forecastFilename, timeZone, inputTimeList, size, momentumFlag, options); +} + +/** + * \brief Automatically allocate a ninjaTools. + * + * \return An opaque handle to a ninjaTools on success, NULL otherwise. + */ +WINDNINJADLL_EXPORT NinjaToolsH* NinjaMakeTools() +{ + NinjaToolsH* army; + army = reinterpret_cast(new ninjaTools()); + return army; +} + +/** + * \brief Fetch Forecast file from UCAR/THREDDS server, or from NOMADS server. + * + * This method will fetch a forecast file from the UCAR/THREDDS server, also can be from the NOMADS server. + * + * \param tools An opaque handle to a valid ninjaTools. + * \param modelName A string representing a valid weather model type (e.g. "NOMADS-HRRR-CONUS-3-KM"). + * \param demFile A valid path to an elevation file. + * \param hours Number of hours to be requested (the forecast duration). + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaFetchWeatherData + (NinjaToolsH* tools, const char* modelName, const char* demFile, int hours) +{ + if(!tools) { - army = reinterpret_cast( new ninjaArmy() ); + return NINJA_E_NULL_PTR; + } + + return reinterpret_cast( tools )->fetchWeatherModelData(modelName, demFile, hours); +} - reinterpret_cast( army )->makeWeatherModelArmy - ( std::string( forecastFilename ), - std::string( timezone ), - momentumFlag ); - return army; +WINDNINJADLL_EXPORT NinjaErr NinjaFetchArchiveWeatherData + (NinjaToolsH* tools, const char* modelName, const char* demFile, const char* timeZone, int startYear, int startMonth, int startDay, int startHour, int endYear, int endMonth, int endDay, int endHour) +{ + if(!tools) + { + return NINJA_E_NULL_PTR; } - catch( armyException & e ) + + return reinterpret_cast( tools )->fetchArchiveWeatherModelData(modelName, demFile, timeZone, startYear, startMonth, startDay, startHour, endYear, endMonth, endDay, endHour); +} + +WINDNINJADLL_EXPORT const char** NinjaGetAllWeatherModelIdentifiers + (NinjaToolsH* tools, int* count) +{ + if(!tools) { - return NULL; + return nullptr; + } + + if(!count) + { + return nullptr; + } + + std::vector temp = reinterpret_cast(tools)->getForecastIdentifiers(); + *count = static_cast(temp.size()); + + const char** identifiers = new const char*[*count]; + for (int i = 0; i < *count; i++) + { + char* identifier = new char[temp[i].size() + 1]; + std::strcpy(identifier, temp[i].c_str()); + identifiers[i] = identifier; + } + + return identifiers; +} + +WINDNINJADLL_EXPORT NinjaErr NinjaFreeAllWeatherModelIdentifiers + (const char** identifiers, int count) +{ + if(!identifiers) + { + return NINJA_E_NULL_PTR; + } + + char** ids = (char**)identifiers; + for (int i = 0; i < count; i++) + { + delete[] ids[i]; + } + delete[] ids; + + return NINJA_SUCCESS; +} + +WINDNINJADLL_EXPORT const char** NinjaGetWeatherModelTimeList + (NinjaToolsH* tools, int* count, const char* fileName, const char* timeZone) +{ + if(!tools) + { + return nullptr; + } + + std:string timeZoneString = timeZone; + std::vector temp = reinterpret_cast(tools)->getTimeList(fileName, timeZoneString); + *count = static_cast(temp.size()); + + const char** timeList = new const char*[*count]; + for (int i = 0; i < *count; i++) + { + char* time = new char[temp[i].size() + 1]; + std::strcpy(time, temp[i].c_str()); + timeList[i] = time; + } + + return timeList; +} + +WINDNINJADLL_EXPORT NinjaErr NinjaFreeWeatherModelTimeList + (const char** timeList, int timeListSize) +{ + if(!timeList) + { + return NINJA_E_NULL_PTR; + } + + char** times = (char**)timeList; + for (int i = 0; i < timeListSize; i++) + { + delete[] times[i]; } - - return NULL; + delete[] times; + + return NINJA_SUCCESS; +} + +WINDNINJADLL_EXPORT NinjaErr NinjaGetWeatherModelHours + (NinjaToolsH* tools, const char* modelIdentifier, int* startHour, int* endHour) +{ + if(!tools) + { + return NINJA_E_NULL_PTR; + } + + if(!startHour || !endHour) + { + return NINJA_E_NULL_PTR; + } + + *startHour = reinterpret_cast(tools)->getStartHour(modelIdentifier); + *endHour = reinterpret_cast(tools)->getEndHour(modelIdentifier); + + return NINJA_SUCCESS; } /** @@ -290,16 +361,15 @@ WINDNINJADLL_EXPORT NinjaArmyH* NinjaMakeWeatherModelArmy WINDNINJADLL_EXPORT NinjaErr NinjaDestroyArmy ( NinjaArmyH * army, char ** options ) { - if( NULL != army ) - { - delete reinterpret_cast( army ); - army = NULL; - return NINJA_SUCCESS; - } - else + if(!army) { return NINJA_E_NULL_PTR; } + + delete reinterpret_cast( army ); + army = NULL; + + return NINJA_SUCCESS; } /** @@ -311,13 +381,20 @@ WINDNINJADLL_EXPORT NinjaErr NinjaDestroyArmy * \param dfCellSize The resolution of the DEM file in meters. * \param pszDstFile Output file name * \param papszOptions options - * * * \return NINJA_SUCCESS on success, NINJA_E_INVALID otherwise. */ -WINDNINJADLL_EXPORT NinjaErr NinjaFetchDEMPoint(NinjaArmyH * army, double * adfPoint, double *adfBuff, const char* units, double dfCellSize, char * pszDstFile, char* fetchType, char ** papszOptions ){ - return reinterpret_cast( army )->fetchDEMPoint(adfPoint, adfBuff, units, dfCellSize, pszDstFile, fetchType, papszOptions); +WINDNINJADLL_EXPORT NinjaErr NinjaFetchDEMPoint + (NinjaToolsH * tools, double * adfPoint, double *adfBuff, const char* units, double dfCellSize, char * pszDstFile, char* fetchType, char ** papszOptions ) +{ + if(!tools) + { + return NINJA_E_NULL_PTR; + } + + return reinterpret_cast( tools )->fetchDEMPoint(adfPoint, adfBuff, units, dfCellSize, pszDstFile, fetchType, papszOptions); } + /** * \brief Fetch DEM file using a bounding box * @@ -330,80 +407,106 @@ WINDNINJADLL_EXPORT NinjaErr NinjaFetchDEMPoint(NinjaArmyH * army, double * adfP * * \return NINJA_SUCCESS on success, NINJA_E_INVALID otherwise. */ +WINDNINJADLL_EXPORT NinjaErr NinjaFetchDEMBBox + (NinjaToolsH * tools, double *boundsBox, const char *fileName, double resolution, char * fetchType, char ** papszOptions) +{ + if(!tools) + { + return NINJA_E_NULL_PTR; + } -WINDNINJADLL_EXPORT NinjaErr NinjaFetchDEMBBox(NinjaArmyH * army, double *boundsBox, const char *fileName, double resolution, char * fetchType, char ** papszOptions){ - return reinterpret_cast( army )->fetchDEMBBox(boundsBox, fileName, resolution, fetchType); + return reinterpret_cast( tools )->fetchDEMBBox(boundsBox, fileName, resolution, fetchType, papszOptions); } /** - * \brief Fetch Forecast file from UCAR/THREDDS server. + * \brief Fetch Station forecast files using bbox from elevation file * - * This method will fetch a forecast file from the UCAR/THREDDS server. + * Avaliable Creation Options: + * None + * TODO: Add option parameter to specify a buffer to use in station fetching * - * \param wx_model_type A string representing a valid weather model type (e.g. "NOMADS-HRRR-CONUS-3-KM") - * \param numNinjas Number of Ninjas + * \param tools An opaque handle to a valid ninjaTools. + * \param yearList A pointer to an array of years. + * \param monthList A pointer to an array of months. + * \param dayList A pointer to an array of days. + * \param hourList A pointer to an array of hours. + * \param minuteList A pointer to an array of minutes. + * \param size The size of time arrays * \param elevation_file A valid path to an elevation file. + * \param buffer The buffer around the elevation file + * \param units The units of the buffer + * \param osTimeZone A string representing a valid timezone. + * \param fetchLatestFlag An integer representing whether to fetch the latest forecast. + * \param outputPath An optional valid path to a custom output directory, can be NULL. + * \param locationFileFlag An integer representing whether to write location file for station data + * \param options Key, value option pairs from the options listed above, can be NULL. * - * \return Forecast file name on success, "exception" otherwise. + * \return Station file name on success, "exception" otherwise. + * TODO: This function currently doesn't return a the path to a station file, need to determine what the proper behavior is + * Note: the pointInitialization class currently only has static public functions. We should consider if this is the best + * approach or if the class should be refactored. For example, I was going to add a function to return a path to + * the stationLocationFilename (the path on disk to the list of station files that the user will need). But right + * now this isn't possible since that path is created by a static function, writeStationLocationFile. If we change + * these functions to non-static to enhance functionality, we'll need to add accessor functions in ninja/ninjaArmy to access + * functions on the pointInitialziation object. Another option for this immediate use case is to just change writeStationLocationFile + * to return the path to the file rather than a bool for success/failure. This might be the simplest for now. + * */ - -WINDNINJADLL_EXPORT const char* NinjaFetchForecast(NinjaArmyH * army, const char*wx_model_type, unsigned int numNinjas, const char * elevation_file, char ** papszOptions) +WINDNINJADLL_EXPORT NinjaErr NinjaFetchStationFromBBox + (NinjaToolsH* tools, const int* yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char* elevationFile, double buffer, const char* units, const char* timeZone, bool fetchLatestFlag, const char* outputPath, bool locationFileFlag, char ** options) { - return reinterpret_cast( army )->fetchForecast(wx_model_type, numNinjas, elevation_file); - + if(!tools) + { + return NINJA_E_NULL_PTR; + } + + return reinterpret_cast( tools )->fetchStationFromBBox(yearList, monthList, dayList, hourList, minuteList, size, elevationFile, buffer, units, timeZone, fetchLatestFlag, outputPath, locationFileFlag, options); } /** - * \brief Fetch Station forecast files using bbox from elevation file + * \brief Fetch Station forecast files using station ID * * Avaliable Creation Options: * None - * TODO: Add option parameter to specify a buffer to use in station fetching * + * \param tools An opaque handle to a valid ninjaTools. * \param yearList A pointer to an array of years. * \param monthList A pointer to an array of months. * \param dayList A pointer to an array of days. * \param hourList A pointer to an array of hours. * \param minuteList A pointer to an array of minutes. + * \param size The size of time arrays * \param elevation_file A valid path to an elevation file. + * \param stationList A string containing station IDs in the format of "KMSO,PNTM8,..." * \param osTimeZone A string representing a valid timezone. * \param fetchLatestFlag An integer representing whether to fetch the latest forecast. - * \param output_path An optional valid path to a custom output directory, can be NULL. + * \param outputPath An optional valid path to a custom output directory, can be NULL. + * \param locationFileFlag An integer representing whether to write location file for station data * \param options Key, value option pairs from the options listed above, can be NULL. * * \return Station file name on success, "exception" otherwise. * TODO: This function currently doesn't return a the path to a station file, need to determine what the proper behavior is - * Note: the pointInitialization class currently only has static public functions. We should consider if this is the best + * Note: the pointInitialization class currently only has static public functions. We should consider if this is the best * approach or if the class should be refactored. For example, I was going to add a function to return a path to * the stationLocationFilename (the path on disk to the list of station files that the user will need). But right - * now this isn't possible since that path is created by a static function, writeStationLocationFile. If we change - * these functions to non-static to enhance functionality, we'll need to add accessor functions in ninja/ninjaArmy to access + * now this isn't possible since that path is created by a static function, writeStationLocationFile. If we change + * these functions to non-static to enhance functionality, we'll need to add accessor functions in ninja/ninjaArmy to access * functions on the pointInitialziation object. Another option for this immediate use case is to just change writeStationLocationFile * to return the path to the file rather than a bool for success/failure. This might be the simplest for now. - * + * */ -WINDNINJADLL_EXPORT NinjaErr NinjaFetchStation(const int* yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, - const char* elevationFile, double buffer, const char* units, const char* timeZone, bool fetchLatestFlag, const char* outputPath, char ** options) +WINDNINJADLL_EXPORT NinjaErr NinjaFetchStationByName + (NinjaToolsH* tools, const int* yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char* elevationFile, const char* stationList, const char* timeZone, bool fetchLatestFlag, const char* outputPath, bool locationFileFlag, char ** options) { - std::vector timeList; - for(size_t i=0; i( tools )->fetchStationByName(yearList, monthList, dayList, hourList, minuteList, size, elevationFile, stationList, timeZone, fetchLatestFlag, outputPath, locationFileFlag, options); } + /** * \brief Start the simulations. * @@ -418,21 +521,21 @@ WINDNINJADLL_EXPORT NinjaErr NinjaFetchStation(const int* yearList, const int * WINDNINJADLL_EXPORT NinjaErr NinjaStartRuns ( NinjaArmyH * army, const unsigned int nprocessors, char ** papszOptions ) { - if( NULL != army ) + if(!army) { - try - { - return reinterpret_cast( army )->startRuns( nprocessors ); - } - catch( ... ) - { - return handleException(); - } + return NINJA_E_NULL_PTR; } - else + + try { - return NINJA_E_NULL_PTR; + return reinterpret_cast( army )->startRuns( nprocessors ); } + catch( ... ) + { + return handleException(); + } + + return NINJA_SUCCESS; } /** @@ -478,12 +581,12 @@ WINDNINJADLL_EXPORT NinjaErr NinjaStartRuns * \return NINJA_SUCCESS on success, non-zero otherwise. */ WINDNINJADLL_EXPORT NinjaErr NinjaSetInitializationMethod - (NinjaArmyH * army, const int nIndex, const char * initializationMethod, char ** papszOptions ) + (NinjaArmyH * army, const int nIndex, const char * initializationMethod, bool matchedPoints, char ** papszOptions ) { if( NULL != army && NULL != initializationMethod ) { return reinterpret_cast( army )->setInitializationMethod - ( nIndex, std::string( initializationMethod ) ); + ( nIndex, std::string( initializationMethod ), matchedPoints); } else { @@ -492,11 +595,11 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetInitializationMethod } WINDNINJADLL_EXPORT NinjaErr NinjaInit - ( char ** papszOptions ) + ( const char * runType, char ** papszOptions ) { NinjaErr retval = NINJA_E_INVALID; - retval = NinjaInitialize(); + retval = NinjaInitialize(runType); return retval; } @@ -526,26 +629,91 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetNumberCPUs } /** - * \brief Set the communication handler for simulations. + * \brief Set a ninjaComMessageHandler callback function, for message communications during simulations, to the ninjaArmy level ninjaCom. + * + * Allows the caller to receive status, progress, informational messages, and error + * messages generated during simulations via a user-provided callback function. * * \param army An opaque handle to a valid ninjaArmy. - * \param nIndex The run to apply the setting to. - * \param comType Type of communication. For now, comType is always "cli". + * \param pMsgHandler A pointer to a user defined ninjaComMessageHandler callback function. If defined, ninjaCom sends messages to this callback function. + * \param pUser A pointer to a user-defined object or context associated with the callback function. This pointer is passed through to the callback function and allows forwarding of messages to this object or context. + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaSetArmyComMessageHandler + ( NinjaArmyH * army, ninjaComMessageHandler pMsgHandler, void *pUser, char ** papszOptions ) +{ + if( NULL != army ) + { + return reinterpret_cast( army )->setNinjaComMessageHandler + ( pMsgHandler, pUser ); + } + else + { + return NINJA_E_NULL_PTR; + } +} + +/** + * \brief Set a ninjaCom multi-stream FILE handle, for message communications during simulations, to the ninjaArmy level ninjaCom. + * + * \param army An opaque handle to a valid ninjaArmy. + * \param stream A pointer to a multi-stream FILE handle/stream. If defined, ninjaCom sends ALL messages to this stream, in addition to std::cout and std::cerr. * * \return NINJA_SUCCESS on success, non-zero otherwise. */ -WINDNINJADLL_EXPORT NinjaErr NinjaSetCommunication - ( NinjaArmyH * army, const int nIndex, const char * comType, char ** papszOptions ) +WINDNINJADLL_EXPORT NinjaErr NinjaSetArmyMultiComStream + ( NinjaArmyH * army, FILE* stream, char ** papszOptions ) { if( NULL != army ) { - return reinterpret_cast( army )->setNinjaCommunication - ( nIndex, std::string( comType ) ); + return reinterpret_cast( army )->setNinjaMultiComStream + ( stream ); } else + { + return NULL; + } +} + +/** + * \brief Set a ninjaComMessageHandler callback function, for message communications during simulations, to the ninjaTools level ninjaCom + * + * Allows the caller to receive status, progress, informational messages, and error + * messages generated during simulations via a user-provided callback function. + * + * \param tools An opaque handle to a valid ninjaTools. + * \param pMsgHandler A pointer to a user defined ninjaComMessageHandler callback function. If defined, ninjaCom sends messages to this callback function. + * \param pUser A pointer to a user-defined object or context associated with the callback function. This pointer is passed through to the callback function and allows forwarding of messages to this object or context. + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaSetToolsComMessageHandler + ( NinjaToolsH * tools, ninjaComMessageHandler pMsgHandler, void *pUser, char ** papszOptions ) +{ + if(!tools) { return NINJA_E_NULL_PTR; } + + return reinterpret_cast( tools )->setNinjaComMessageHandler( pMsgHandler, pUser ); +} + +/** + * \brief Set a ninjaCom multi-stream FILE handle, for message communications during simulations, to the ninjaTools level ninjaCom + * + * \param tools An opaque handle to a valid ninjaTools. + * \param stream A pointer to a multi-stream FILE handle/stream. If defined, ninjaCom sends ALL messages to this stream, in addition to std::cout and std::cerr. + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaSetToolsMultiComStream + ( NinjaToolsH * tools, FILE* stream, char ** papszOptions ) +{ + if(!tools) + { + return NINJA_E_NULL_PTR; + } + + return reinterpret_cast( tools )->setNinjaMultiComStream( stream ); } /** @@ -1461,9 +1629,40 @@ WINDNINJADLL_EXPORT const int NinjaGetOutputGridnRows WINDNINJADLL_EXPORT NinjaErr NinjaSetOutputBufferClipping ( NinjaArmyH * army, const int nIndex, const double percent, char ** papszOptions ) { + if(!army) + { + NINJA_E_NULL_PTR; + } + + return reinterpret_cast( army )->setOutputBufferClipping( nIndex, percent ); +} + +/** + * \brief Set station kml output for a point initialization simulation. + * + * \param army An opaque handle to a valid ninjaArmy. + * \param nIndex The run to apply the setting to. + * \param demFileName The filepath of the simulation DEM. + * \param outputDirectory The outputDirectory location for this file. + * \param outputSpeedUnits The output speed units ("mph", "mps", "kph", "kts") + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaSetStationKML + ( NinjaArmyH * army, const int nIndex, const char * demFileName, const char * outputDirectory, const char * outputSpeedUnits, char ** papszOptions) +{ + if( NULL != army ) { - return reinterpret_cast( army )->setOutputBufferClipping( nIndex, percent ); + try + { + wxStation::writeKmlFile( reinterpret_cast( army )->getWxStations(nIndex), demFileName, outputDirectory, velocityUnits::getUnit(outputSpeedUnits)); + return NINJA_SUCCESS; + } + catch (const std::exception& e) + { + return NINJA_E_OTHER; + } } else { @@ -1471,6 +1670,7 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetOutputBufferClipping } } + /** * \brief Set the flag to write the weather model winds used for initialzation as * a Google Earth file. @@ -1615,7 +1815,7 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogResolution * * \param army An opaque handle to a valid ninjaArmy. * \param nIndex The run to apply the setting to. - * \param scaling The scaling at which to write the Google Earth output. + * \param scaling The scaling at which to write the Google Earth output. ("equal_color", "color", "equal_interval", "interval") * * \return NINJA_SUCCESS on success, non-zero otherwise. */ @@ -1640,7 +1840,7 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogSpeedScaling * * \param army An opaque handle to a valid ninjaArmy. * \param nIndex The run to apply the setting to. - * \param scaling The line width at which to write the Google Earth output. + * \param width The line width at which to write the Google Earth output. * * \return NINJA_SUCCESS on success, non-zero otherwise. */ @@ -1657,6 +1857,56 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogLineWidth } } +/** + * \brief Set the Color Scheme of the Google Earth output for a simulation. + * + * \note Only valid if NinjaSetGoogOutFlag is set to 1. + * + * \param army An opaque handle to a valid ninjaArmy. + * \param nIndex The run to apply the setting to. + * \param colorScheme A string that specifies the color scheme ("default", "ROPGW", "oranges", "blues", "pinks", "greens", "magic_beans", "pink_to_greens"). + * \param scaling The flag which determines if vector scaling will be used. + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogColor + ( NinjaArmyH * army, const int nIndex, const char * colorScheme, bool scaling, char ** papszOptions ) +{ + if( NULL != army ) + { + return reinterpret_cast( army )->setGoogColor(nIndex, std::string( colorScheme ), scaling); + } + else + { + return NINJA_E_NULL_PTR; + } +} + +/** + * \brief Set the flag to use a Consistent Color Scheme for all Google Earth Outputs of a simulation. + * + * \note Only valid if NinjaSetGoogOutFlag is set to 1. + * + * \param army An opaque handle to a valid ninjaArmy. + * \param nIndex The run to apply the setting to. + * \param flag The flag that determines whether consistent color scaling will be used. + * \param numRuns The number of runs that will be simulated + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogConsistentColorScale + ( NinjaArmyH * army, const int nIndex, bool flag, int numRuns, char ** papszOptions ) +{ + if( NULL != army ) + { + return reinterpret_cast( army )->setGoogConsistentColorScale(nIndex, flag, numRuns); + } + else + { + return NINJA_E_NULL_PTR; + } +} + /** * \brief Set the flag to write shapefile output for a simulation. * @@ -1731,6 +1981,7 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetAsciiOutFlag } } + /** * \brief Set the resolution of the raster output for a simulation. * @@ -1758,6 +2009,37 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetAsciiResolution } } +/** + * \brief Set the resolution of the raster output for a simulation. + * + * \note Only valid if NinjaSetAsciiOutFlag is set to 1. + * + * \param army An opaque handle to a valid ninjaArmy. + * \param flag That flag that determines whether to write the .atm file. + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaSetAsciiAtmFile + ( NinjaArmyH * army, bool flag, char ** papszOptions) +{ + if( NULL != army) + { + try + { + reinterpret_cast(army)->set_writeFarsiteAtmFile(flag); + return NINJA_SUCCESS; + } + catch (const std::exception& e) + { + return NINJA_E_OTHER; + } + } + else + { + return 1; + } +} + /** * \brief Set the flag to write VTK output for a simulation. * @@ -1816,7 +2098,7 @@ WINDNINJADLL_EXPORT NinjaErr NinjaSetTxtOutFlag * \return NINJA_SUCCESS on success, non-zero otherwise. */ WINDNINJADLL_EXPORT NinjaErr NinjaSetPDFOutFlag - ( NinjaArmyH* army, const int nIndex, const bool flag, char ** options ) + ( NinjaArmyH* army, const int nIndex, const bool flag, char ** papszOptions ) { if( NULL != army ) { @@ -2044,4 +2326,276 @@ WINDNINJADLL_EXPORT NinjaErr NinjaCancelAndReset( NinjaArmyH * army, char ** pap } } +/*----------------------------------------------------------------------------- +* Helper Methods +*-----------------------------------------------------------------------------*/ +/** + * \brief Get the header version for a weather station file. + * + * \param stationPath The file path for the weather station file. + * + * \return Header version number (1 = Old Format, 2 = New Format, 3 = csv list for time series, 4 = csv list for non time series) + */ +WINDNINJADLL_EXPORT int NinjaGetWxStationHeaderVersion(const char * stationPath, char ** options) +{ + return wxStation::GetHeaderVersion(stationPath); +} + +/** + * \brief Get a time series in UTC with a specific number time steps between the inputted start and stop times. + * + * \param tools An opaque handle to a valid ninjaTools. + * \param inputYearList A pointer to an array of input years. + * \param inputMonthList A pointer to an array of input months. + * \param inputDayList A pointer to an array of input days. + * \param inputHourList A pointer to an array of input hours. + * \param inputMinuteList A pointer to an array of input minutes. + * \param outputYearList A pointer to an array of output years in UTC. + * \param outputMonthList A pointer to an array of output months in UTC. + * \param outputDayList A pointer to an array of output days. + * \param outputHourList A pointer to an array of output hours. + * \param outputMinuteList A pointer to an array of output minutes. + * \param nTimeSteps Number of time steps wanted between output date times. + * \param timeZone The time zone of input date times. + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaGetTimeList( + NinjaToolsH* tools, + const int * inputYearList, const int * inputMonthList, const int * inputDayList, + const int * inputHourList, const int * inputMinuteList, + int * outputYearList, int* outputMonthList, int * outputDayList, + int * outputHourList, int* outputMinuteList, + int nTimeSteps, const char* timeZone) +{ + if(!tools) + { + return NINJA_E_NULL_PTR; + } + + return reinterpret_cast( tools )->getTimeList(inputYearList, inputMonthList, inputDayList, inputHourList, inputMinuteList, outputYearList, outputMonthList, outputDayList, outputHourList, outputMinuteList, nTimeSteps, timeZone); +} + +/** + * \brief Get a date time in UTC from an inpute date time in a specified timezone. + * + * \param tools An opaque handle to a valid ninjaTools. + * \param inputYear An input year. + * \param inputMonth An input month. + * \param inputDay An input day. + * \param inputHour An hour. + * \param inputMinute An input minute. + * \param timeZone The time zone of input date times. + * \param outputYear A pointer to an output year in UTC. + * \param outputMonth A pointer to an output month in UTC. + * \param outputDay A pointer to an output day in UTC. + * \param outputHour A pointer to an output hour in UTC. + * \param outputMinute A pointer to an output minute in UTC. + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaGenerateSingleTimeObject( + NinjaToolsH* tools, + int inputYear, int inputMonth, int inputDay, int inputHour, int inputMinute, const char * timeZone, + int * outYear, int * outMonth, int* outDay, int * outHour, int * outMinute) +{ + if(!tools) + { + return NINJA_E_NULL_PTR; + } + + return reinterpret_cast( tools )->generateSingleTimeObject(inputYear, inputMonth, inputDay, inputHour, inputMinute, timeZone, outYear, outMonth, outDay, outHour, outMinute); +} + +/** + * \brief Get a time series in UTC with a specific number of time steps between the inputted start and stop times. + * + * \param tools An opaque handle to a valid ninjaTools. + * \param yearList A pointer to an array of years. + * \param monthList A pointer to an array of months. + * \param dayList A pointer to an array of days. + * \param hourList A pointer to an array of hours. + * \param minuteList A pointer to an array of minutes. + * \param listSize The number of elements in the input arrays. + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaCheckTimeDuration + (NinjaToolsH* tools, int* yearList, int* monthList, int * dayList, int * minuteList, int *hourList, int listSize, char ** papszOptions) +{ + if(!tools) + { + return NINJA_E_NULL_PTR; + } + + return reinterpret_cast( tools )->checkTimeDuration( yearList, monthList, dayList, minuteList, hourList, listSize, papszOptions ); +} + +/** + * \brief calls wxStation::writeBlankStationFile(), which writes a weather station csv file with no data, just a header. + * + * \param outputStationFilename A csv file to write a blank weather station file to. + * \param papszOptions options + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaWriteBlankWxStationFile( const char * outputStationFilename, char ** papszOptions ) +{ + wxStation::writeBlankStationFile(outputStationFilename); + + bool doesOutputFileExist = CPLCheckForFile((char*)outputStationFilename, NULL); + if( doesOutputFileExist ) + { + return NINJA_SUCCESS; + } + else + { + return NINJA_E_NULL_PTR; + } +} + +/** + * \brief calls ninjaArmy::getRunKmzFilenames(), which gets the ninjas[i] output kmz filenames, + * as well as ninjas[i] station kml filenames and ninjas[i] weather model filenames, + * if they were created for the run. + * + * \note Must be called after NinjaStartRuns is called and finished successfully. + * \note NinjaDestroyRunKmzFilenames() must be called on the run kmz filenames when done with the filenames, to properly deallocate the filenames memory. + * + * \param army An opaque handle to a valid ninjaArmy. + * \param numRuns The number of runs that were simulated, to be filled. Also the expected size of the filled filename arrays. + * \param kmzFilenames The ninjas[i] output kmz filenames array, as a NULL char**, to be created and filled, to be created of size numRuns. + * \param numStationKmls The number of station kmls SHARED across each run, to be filled. Also the expected size of the filled stationKmlFilenames array. + * \param stationKmlFilenames The ninjas[i] station kml filenames array, as a NULL char**, to be created and filled, to be created of size numStationKmls. Runs without station kml file output use "" for the station kml filenames. Each run SHARES this same list of stationkmlFilenames. + * \param weatherModelKmzFilenames The ninjas[i] weather model kmz filenames array, as a NULL char**, to be created and filled, to be created of size numRuns. Runs without weather model kmz file output use "" for the weather model kmz filenames. + * \param papszOptions options + * + * \return NINJA_SUCCESS on success, non-zero otherwise. + */ +WINDNINJADLL_EXPORT NinjaErr NinjaGetRunKmzFilenames(NinjaArmyH * army, int *numRuns, char*** kmzFilenames, int *numStationKmls, char*** stationKmlFilenames, char*** weatherModelKmzFilenames, char ** papszOptions) +{ + std::vector kmzFilenameStrings; + std::vector stationKmlFilenameStrings; + std::vector wxModelKmzFilenameStrings; + + if( NULL != army ) + { + NinjaErr retval = reinterpret_cast( army )->getRunKmzFilenames( kmzFilenameStrings, stationKmlFilenameStrings, wxModelKmzFilenameStrings ); + if( retval != NINJA_SUCCESS ) + { + return retval; + } + + int n = (int)kmzFilenameStrings.size(); + *numRuns = n; + + *kmzFilenames = (char **)malloc(sizeof(char *) * n); + *weatherModelKmzFilenames = (char **)malloc(sizeof(char *) * n); + + for(int i = 0; i < n; i++) + { + std::string kmzFilenameStr = kmzFilenameStrings[i]; + std::string wxModelKmzFilenameStr = wxModelKmzFilenameStrings[i]; + + char *kmzFilename = (char *)malloc(kmzFilenameStr.size() + 1); + char *wxModelKmzFilename = (char *)malloc(wxModelKmzFilenameStr.size() + 1); + + if(!kmzFilename || !wxModelKmzFilename) + { + return NINJA_E_BAD_ALLOC; + } + + memcpy(kmzFilename, kmzFilenameStr.c_str(), kmzFilenameStr.size() + 1); + memcpy(wxModelKmzFilename, wxModelKmzFilenameStr.c_str(), wxModelKmzFilenameStr.size() + 1); + + (*kmzFilenames)[i] = kmzFilename; + (*weatherModelKmzFilenames)[i] = wxModelKmzFilename; + } + + int m = (int)stationKmlFilenameStrings.size(); + *numStationKmls = m; + + *stationKmlFilenames = (char **)malloc(sizeof(char *) * m); + + for(int j = 0; j < m; j++) + { + std::string stationKmlFilenameStr = stationKmlFilenameStrings[j]; + + char *stationKmlFilename = (char *)malloc(stationKmlFilenameStr.size() + 1); + + if(!stationKmlFilename) + { + return NINJA_E_BAD_ALLOC; + } + + memcpy(stationKmlFilename, stationKmlFilenameStr.c_str(), stationKmlFilenameStr.size() + 1); + + (*stationKmlFilenames)[j] = stationKmlFilename; + } + + return NINJA_SUCCESS; + } + else + { + return NINJA_E_NULL_PTR; + } +} + +WINDNINJADLL_EXPORT NinjaErr NinjaDestroyRunKmzFilenames(int numRuns, char** kmzFilenames, int numStationKmls, char** stationKmlFilenames, char** weatherModelKmzFilenames, char ** papszOptions) +{ + for(int i = 0; i < numRuns; i++) + { + if(kmzFilenames) + { + if(kmzFilenames[i]) + { + free(kmzFilenames[i]); + } + } + + if(weatherModelKmzFilenames) + { + if(weatherModelKmzFilenames[i]) + { + free(weatherModelKmzFilenames[i]); + } + } + } + + for(int j = 0; j < numStationKmls; j++) + { + if(stationKmlFilenames) + { + if(stationKmlFilenames[j]) + { + free(stationKmlFilenames[j]); + } + } + } + + if(kmzFilenames) + { + free(kmzFilenames); + } + if(stationKmlFilenames) + { + free(stationKmlFilenames); + } + if(weatherModelKmzFilenames) + { + free(weatherModelKmzFilenames); + } + + return NINJA_SUCCESS; +} + +WINDNINJADLL_EXPORT const char* NinjaFindBinDir(char ** papszOptions) +{ + std::string path = FindNinjaBinDir(); + char* result = static_cast(std::malloc(path.size() + 1)); + std::memcpy(result, path.c_str(), path.size() + 1); + return result; } + + +} // extern "C" diff --git a/src/ninja/windninja.h b/src/ninja/windninja.h index 4e38d6fa5..a400bb0c4 100644 --- a/src/ninja/windninja.h +++ b/src/ninja/windninja.h @@ -51,59 +51,75 @@ #endif //WIN32 && WINDNINJA_EXPORTS #endif //WINDNINJADLL_EXPORT -#ifdef WIN32 -#define true 1 -#define false 0 -#else #include -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE 0 -#endif +#include +#include WN_C_START -#include - //Use structs instead of void * for type checking by C compilier struct NinjaArmyH; typedef struct NinjaArmyH NinjaArmyH; + +struct NinjaToolsH; +typedef struct NinjaToolsH NinjaToolsH; + typedef int NinjaErr; +#include "callbackFunctions.h" + /*----------------------------------------------------------------------------- * Contructor/Destructors *-----------------------------------------------------------------------------*/ - WINDNINJADLL_EXPORT NinjaArmyH * NinjaMakeDomainAverageArmy - ( unsigned int numNinjas, bool momentumFlag, const double * speedList, const char * speedUnits, const double * directionList, char ** options); -// ( unsigned int numNinjas, bool momentumFlag, const double * speedList, const char * speedUnits, const double * directionList, const int * yearList, const int * monthList, const int * dayList, -// const int * hourList, const int * minuteList, const char * timeZone, const double * airTempList, const char* airTempUnits, const double * cloudCoverList, const char * cloudCoverUnits, char ** options); + WINDNINJADLL_EXPORT NinjaArmyH* NinjaInitializeArmy(); + + WINDNINJADLL_EXPORT NinjaErr NinjaMakeDomainAverageArmy + ( NinjaArmyH * ninjaArmy, int numNinjas, bool momentumFlag, const double * speedList, const char * speedUnits, const double * directionList, + const int * yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const char * timeZone, const double * airTempList, const char* airTempUnits, const double * cloudCoverList, const char * cloudCoverUnits, char ** options); //TODO: add helper function to generate arrays of years, months, days, hours, and minutes from a station file - WINDNINJADLL_EXPORT NinjaArmyH * NinjaMakePointArmy - ( int * yearList, int * monthList, int * dayList, int * hourList, int * minuteList, int size, char * timeZone, char * stationFileName, char * elevationFile, bool matchPointsFlag, bool momentumFlag, char ** options ); + WINDNINJADLL_EXPORT NinjaErr NinjaMakePointArmy + ( NinjaArmyH * ninjaArmy, int * yearList, int * monthList, int * dayList, int * hourList, int * minuteList, int timeListSize, char * timeZone, const char ** stationFileNames, int numStationFiles, char * elevationFile, bool matchPointsFlag, bool momentumFlag, char ** options); //TODO: add helper function to get first and last timesteps in a forecast file //TODO: add helper function to get list of times in a forecast file //TODO: include parameters for start/stop times and a list of timesteps as options->for cases where you don't want to simulate every time step in the forecast file - WINDNINJADLL_EXPORT NinjaArmyH * NinjaMakeWeatherModelArmy - ( const char * forecastFilename, const char * timezone, bool momentumFlag, char ** options ); + WINDNINJADLL_EXPORT NinjaErr NinjaMakeWeatherModelArmy + ( NinjaArmyH * ninjaArmy, const char * forecastFilename, const char * timeZone, const char** inputTimeList, int size, bool momentumFlag, char ** options ); + + WINDNINJADLL_EXPORT NinjaToolsH * NinjaMakeTools(); + + WINDNINJADLL_EXPORT NinjaErr NinjaFetchWeatherData + (NinjaToolsH* tools, const char* modelName, const char* demFile, int hours); + + WINDNINJADLL_EXPORT NinjaErr NinjaFetchArchiveWeatherData + (NinjaToolsH* tools, const char* modelName, const char* demFile, const char * timeZone, int startYear, int startMonth, int startDay, int startHour, int endYear, int endMonth, int endDay, int endHour); + + WINDNINJADLL_EXPORT const char** NinjaGetAllWeatherModelIdentifiers(NinjaToolsH* tools, int* count); + + WINDNINJADLL_EXPORT NinjaErr NinjaGetWeatherModelHours + (NinjaToolsH* tools, const char* modelIdentifier, int* starHours, int* endHour); + + WINDNINJADLL_EXPORT NinjaErr NinjaFreeAllWeatherModelIdentifiers + (const char** identifiers, int count); + + WINDNINJADLL_EXPORT const char** NinjaGetWeatherModelTimeList + (NinjaToolsH * tools, int * count, const char * fileName, const char * timeZone); + + WINDNINJADLL_EXPORT NinjaErr NinjaFreeWeatherModelTimeList + (const char** timeList, int timeListSize); - WINDNINJADLL_EXPORT NinjaErr NinjaFetchStation - (const int * yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char * elevationFile, double buffer, const char* units, const char * timeZone, bool fetchLatestFlag, const char * outputPath, char ** options ); + WINDNINJADLL_EXPORT NinjaErr NinjaFetchStationFromBBox + (NinjaToolsH* tools, const int * yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char * elevationFile, double buffer, const char* units, const char * timeZone, bool fetchLatestFlag, const char * outputPath, bool locationFileFlag, char ** options ); + + WINDNINJADLL_EXPORT NinjaErr NinjaFetchStationByName + (NinjaToolsH* tools, const int * yearList, const int * monthList, const int * dayList, const int * hourList, const int * minuteList, const int size, const char * elevationFile, const char* stationList, const char * timeZone, bool fetchLatestFlag, const char * outputPath, bool locationFileFlag, char ** options ); WINDNINJADLL_EXPORT NinjaErr NinjaFetchDEMPoint - (NinjaArmyH * ninjaArmy, double * point, double * buff, const char * units, double cellSize, char * dstFile, char * fetchType, char ** options ); + (NinjaToolsH * tools, double * point, double * buff, const char * units, double cellSize, char * dstFile, char * fetchType, char ** options ); WINDNINJADLL_EXPORT NinjaErr NinjaFetchDEMBBox - (NinjaArmyH * ninjaArmy, double * boundsBox, const char * fileName, double resolution, char * fetchType, char ** options ); - - WINDNINJADLL_EXPORT const char * NinjaFetchForecast - (NinjaArmyH * ninjaArmy, const char * wx_model_type, unsigned int numNinjas, const char * elevation_file, char ** options ); + (NinjaToolsH * tools, double * boundsBox, const char * fileName, double resolution, char * fetchType, char ** options ); WINDNINJADLL_EXPORT NinjaErr NinjaDestroyArmy ( NinjaArmyH * ninjaArmy, char ** options ); @@ -115,7 +131,7 @@ typedef int NinjaErr; ( NinjaArmyH * ninjaArmy, const unsigned int nprocessors, char ** options ); WINDNINJADLL_EXPORT NinjaErr NinjaInit - ( char ** options ); + ( const char * runType, char ** options ); /*----------------------------------------------------------------------------- * Various Simulation Parameters @@ -131,14 +147,23 @@ typedef int NinjaErr; ( NinjaArmyH * ninjaArmy, const int nIndex, char ** options ); WINDNINJADLL_EXPORT NinjaErr NinjaSetInitializationMethod - ( NinjaArmyH * ninjaArmy, const int nIndex, const char * initializationMethod, char ** options ); + ( NinjaArmyH * ninjaArmy, const int nIndex, const char * initializationMethod, bool matchedPoints, char ** options ); WINDNINJADLL_EXPORT NinjaErr NinjaSetNumberCPUs ( NinjaArmyH * ninjaArmy, const int nIndex, const int nCPUs, char ** options ); /* Communication */ - WINDNINJADLL_EXPORT NinjaErr NinjaSetCommunication - ( NinjaArmyH * ninjaArmy, const int nIndex, const char * comType, char ** options ); + WINDNINJADLL_EXPORT NinjaErr NinjaSetArmyComMessageHandler + ( NinjaArmyH * ninjaArmy, ninjaComMessageHandler pMsgHandler, void *pUser, char ** options ); + + WINDNINJADLL_EXPORT NinjaErr NinjaSetArmyMultiComStream + ( NinjaArmyH * ninjaArmy, FILE* stream, char ** options ); + + WINDNINJADLL_EXPORT NinjaErr NinjaSetToolsComMessageHandler + ( NinjaToolsH * tools, ninjaComMessageHandler pMsgHandler, void *pUser, char ** options ); + + WINDNINJADLL_EXPORT NinjaErr NinjaSetToolsMultiComStream + ( NinjaToolsH * tools, FILE* stream, char ** options ); /* Input Parameters */ WINDNINJADLL_EXPORT NinjaErr NinjaSetInputSpeed @@ -189,6 +214,8 @@ typedef int NinjaErr; WINDNINJADLL_EXPORT const char * NinjaGetInitializationMethod ( NinjaArmyH * ninjaArmy, const int nIndex, char ** options ); + + /*----------------------------------------------------------------------------- * Dust Methods *-----------------------------------------------------------------------------*/ @@ -266,6 +293,9 @@ typedef int NinjaErr; WINDNINJADLL_EXPORT NinjaErr NinjaSetOutputBufferClipping ( NinjaArmyH * ninjaArmy, const int nIndex, const double percent, char ** options ); + WINDNINJADLL_EXPORT NinjaErr NinjaSetStationKML + ( NinjaArmyH * ninjaArmy, const int nIndex, const char * demFileName, const char * outputDirectory, const char * outputSpeedUnits, char ** papszOptions); + WINDNINJADLL_EXPORT NinjaErr NinjaSetWxModelGoogOutFlag ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** options ); @@ -285,31 +315,40 @@ typedef int NinjaErr; WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogSpeedScaling ( NinjaArmyH * ninjaArmy, const int nIndex, const char * scaling, char ** options ); + WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogColor + ( NinjaArmyH * army, const int nIndex, const char * colorScheme, bool scaling, char ** papszOptions ); + WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogLineWidth - ( NinjaArmyH * ninjaArmy, const int nIndex, const double width, char ** options ); + ( NinjaArmyH * ninjaArmy, const int nIndex, const double width, char ** papszOptions ); + + WINDNINJADLL_EXPORT NinjaErr NinjaSetGoogConsistentColorScale + ( NinjaArmyH * army, const int nIndex, bool flag, int numRuns, char ** papszOptions); WINDNINJADLL_EXPORT NinjaErr NinjaSetShpOutFlag - ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** options ); + ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** papszOptions ); WINDNINJADLL_EXPORT NinjaErr NinjaSetShpResolution ( NinjaArmyH * ninjaArmy, const int nIndex, const double resolution, const char * units, char ** options ); WINDNINJADLL_EXPORT NinjaErr NinjaSetAsciiOutFlag - ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** options ); + ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** papszOptions ); WINDNINJADLL_EXPORT NinjaErr NinjaSetAsciiResolution ( NinjaArmyH * ninjaArmy, const int nIndex, const double resolution, const char * units, char ** options ); + WINDNINJADLL_EXPORT NinjaErr NinjaSetAsciiAtmFile + ( NinjaArmyH * army, bool flag, char ** papszOptions); + WINDNINJADLL_EXPORT NinjaErr NinjaSetVtkOutFlag - ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** options ); + ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** papszOptions ); WINDNINJADLL_EXPORT NinjaErr NinjaSetTxtOutFlag - ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** options ); + ( NinjaArmyH * ninjaArmy, const int nIndex, const bool flag, char ** papszOptions ); WINDNINJADLL_EXPORT NinjaErr NinjaSetPDFOutFlag - ( NinjaArmyH* ninjaArmy, const int nIndex, const bool flag, char ** options ); + ( NinjaArmyH* ninjaArmy, const int nIndex, const bool flag, char ** papszOptions ); WINDNINJADLL_EXPORT NinjaErr NinjaSetPDFResolution ( NinjaArmyH* ninjaArmy, const int nIndex, const double resolution, const char * units, char ** papszOptions ); @@ -338,4 +377,26 @@ typedef int NinjaErr; WINDNINJADLL_EXPORT NinjaErr NinjaCancel( NinjaArmyH * ninjaArmy, char ** options ); WINDNINJADLL_EXPORT NinjaErr NinjaCancelAndReset( NinjaArmyH * ninjaArmy, char ** options ); + /*----------------------------------------------------------------------------- + * Helper Methods + *-----------------------------------------------------------------------------*/ + WINDNINJADLL_EXPORT int NinjaGetWxStationHeaderVersion(const char * filePath, char ** papszOptions); + WINDNINJADLL_EXPORT NinjaErr NinjaGetTimeList( + NinjaToolsH* tools, + const int* inputYearList, const int* inputMonthList, const int* inputDayList, + const int* inputHourList, const int* inputMinuteList, + int* outputYearList, int* outputMonthList, int* outputDayList, + int* outputHourList, int* outputMinuteList, + int nTimeSteps, const char* timeZone); + WINDNINJADLL_EXPORT NinjaErr NinjaGenerateSingleTimeObject( + NinjaToolsH* tools, + int inputYear, int inputMonth, int inputDay, int inputHour, int inputMinute, const char* timeZone, + int* outYear, int* outMonth, int* outDay, int* outHour, int* outMinute); + WINDNINJADLL_EXPORT NinjaErr NinjaCheckTimeDuration + (NinjaToolsH* tools, int* yearList, int* monthList, int * dayList, int * minuteList, int *hourList, int listSize, char ** papszOptions); + WINDNINJADLL_EXPORT NinjaErr NinjaWriteBlankWxStationFile( const char * outputStationFilename, char ** papszOptions ); + WINDNINJADLL_EXPORT NinjaErr NinjaGetRunKmzFilenames(NinjaArmyH * army, int *numRuns, char*** kmzFilenames, int *numStationKmls, char*** stationKmlFilenames, char*** weatherModelKmzFilenames, char ** papszOptions); + WINDNINJADLL_EXPORT NinjaErr NinjaDestroyRunKmzFilenames(int numRuns, char** kmzFilenames, int numStationKmls, char** stationKmlFilenames, char** weatherModelKmzFilenames, char ** papszOptions); + WINDNINJADLL_EXPORT const char* NinjaFindBinDir(char ** papszOptions); + WN_C_END diff --git a/src/ninja/wrf3dInitialization.cpp b/src/ninja/wrf3dInitialization.cpp index b32be17de..c7179cb6c 100644 --- a/src/ninja/wrf3dInitialization.cpp +++ b/src/ninja/wrf3dInitialization.cpp @@ -412,7 +412,7 @@ void wrf3dInitialization::set3dGrids( WindNinjaInputs &input, Mesh const& mesh ) /*AsciiGrid tempGrid; AsciiGrid temp2Grid; GDAL2AsciiGrid( wrpDS, 12, tempGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { tempGrid.set_noDataValue( -9999.0 ); tempGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/wrfSurfInitialization.cpp b/src/ninja/wrfSurfInitialization.cpp index 3d4483021..0bf1e7802 100644 --- a/src/ninja/wrfSurfInitialization.cpp +++ b/src/ninja/wrfSurfInitialization.cpp @@ -182,7 +182,7 @@ void wrfSurfInitialization::checkForValidData() else { noDataValueExists = true; - noDataIsNan = cplIsNan(dfNoData); + noDataIsNan = std::isnan(dfNoData); } //set the data @@ -196,7 +196,7 @@ void wrfSurfInitialization::checkForValidData() { if(noDataIsNan) { - if(cplIsNan(padfScanline[k])) + if(std::isnan(padfScanline[k])) throw badForecastFile("Forecast file contains no_data values."); }else { @@ -743,7 +743,7 @@ void wrfSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, if( varList[i] == "U10" ) { GDAL2AsciiGrid( wrpDS, 12, tempGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { tempGrid.set_noDataValue(-9999.0); tempGrid.replaceNan( -9999.0 ); } @@ -762,28 +762,28 @@ void wrfSurfInitialization::setSurfaceGrids( WindNinjaInputs &input, if( varList[i] == "T2" ) { GDAL2AsciiGrid( wrpDS, bandNum, airGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { airGrid.set_noDataValue(-9999.0); airGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "V10" ) { GDAL2AsciiGrid( wrpDS, bandNum, vGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { vGrid.set_noDataValue(-9999.0); vGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "U10" ) { GDAL2AsciiGrid( wrpDS, bandNum, uGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { uGrid.set_noDataValue(-9999.0); uGrid.replaceNan( -9999.0 ); } } else if( varList[i] == "QCLOUD" ) { GDAL2AsciiGrid( wrpDS, bandNum, cloudGrid ); - if( cplIsNan( dfNoData ) ) { + if( std::isnan( dfNoData ) ) { cloudGrid.set_noDataValue(-9999.0); cloudGrid.replaceNan( -9999.0 ); } diff --git a/src/ninja/wxStation.cpp b/src/ninja/wxStation.cpp index 499a36ef9..72f892143 100644 --- a/src/ninja/wxStation.cpp +++ b/src/ninja/wxStation.cpp @@ -577,32 +577,32 @@ bool wxStation::check_station(wxStation station) for (int i=0;i105.0) + if(station.speedList[i] < 0.0 || std::isnan(station.speedList[i]) || station.speedList[i]>105.0) { cout<<"failed speed Check on "< 360.0 || cplIsNan(station.directionList[i])) + if(station.directionList[i] < 0.0 || station.directionList[i] > 360.0 || std::isnan(station.directionList[i])) { cout<<"failed direction Check on "< 330.00 || cplIsNan(station.temperatureList[i])) + if(station.temperatureList[i]< 173.15 || station.temperatureList[i] > 330.00 || std::isnan(station.temperatureList[i])) { cout<<"failed temperature Check on "<1.10 || cplIsNan(station.cloudCoverList[i])) + if(station.cloudCoverList[i]<0.0||station.cloudCoverList[i]>1.10 || std::isnan(station.cloudCoverList[i])) { cout<<"failed cloud check on "< - - data/wn-splash.png - images/icons/wn-icon.png - images/icons/wn-desktop.ico - images/icons/resample.png - images/icons/bug_link.png - images/icons/bullet_blue.png - images/icons/jason_bullet_blue.png - images/icons/bullet_error.png - images/icons/cross.png - images/icons/disk.png - images/icons/folder_page.png - images/icons/cog_go.png - images/icons/help.png - images/icons/link.png - images/icons/page_white_acrobat.png - images/icons/tick.png - images/icons/weather_sun.png - images/icons/server_go.png - images/icons/application_get.png - images/icons/cancel.png - images/icons/noaa_bullet.png - images/icons/arrow_rotate_clockwise.png - images/icons/add.png - images/icons/lightning_add.png - images/icons/weather_cloudy.png - images/icons/delete.png - images/icons/jason_caution.png - images/icons/folder.png - images/icons/magnifier.png - images/icons/swoop_final.png - images/icons/center_click_final.png - images/icons/email.png - images/icons/citation.png - + + data/wn-splash.png + images/icons/add.png + images/icons/application_get.png + images/icons/arrow_rotate_clockwise.png + images/icons/bug_link.png + images/icons/bullet_blue.png + images/icons/bullet_error.png + images/icons/cancel.png + images/icons/center_click_final.png + images/icons/citation.png + images/icons/cog_go.png + images/icons/cross.png + images/icons/delete.png + images/icons/disk.png + images/icons/email.png + images/icons/folder.png + images/icons/folder_page.png + images/icons/help.png + images/icons/jason_bullet_blue.png + images/icons/jason_caution.png + images/icons/lightning_add.png + images/icons/link.png + images/icons/magnifier.png + images/icons/noaa_bullet.png + images/icons/page_white_acrobat.png + images/icons/resample.png + images/icons/server_go.png + images/icons/swoop_final.png + images/icons/tick.png + images/icons/weather_cloudy.png + images/icons/weather_sun.png + images/icons/wn-desktop.ico + images/icons/wn-icon.png + data/date_time_zonespec.csv +