From 222bbe22ab1176ae544ec5d2d66afabaf98891d4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 19 Aug 2017 20:23:08 -0400 Subject: [PATCH] Feature/gb gui (#390) Implemented new GUI component to handle WMS servers * Add Lua scripts to support adding GIBS datasets (closes #222) * Add Lua function to load WMS servers from a predefined file * Workaround for Visual Studio 15.3 compile fix in Windows headers * Initial support for parsing GetCapabilities file and automatically add layers * Add a Trigger property to remove a layer * Support default servers * Add default file * Move WMS server code from GUI component into GlobeBrowsingModule * Add Lua scripts for loading and removing WMS servers Automatically load default servers on startup * Reset tile provider before removing a layer tolimit the crash risk Add "From focus" button to switch globebrowsing gui to the same node as the focus * Remove warnings Remove compile error with nonexisting GDALOpenEx function --- data/globebrowsing_servers.lua | 38 ++ data/scene/default.scene | 4 + modules/globebrowsing/CMakeLists.txt | 2 +- modules/globebrowsing/globebrowsingmodule.cpp | 181 +++++++++- modules/globebrowsing/globebrowsingmodule.h | 37 ++ .../globebrowsing/globebrowsingmodule_lua.inl | 83 ++++- .../globebrowsing/rendering/layer/layer.cpp | 22 +- modules/globebrowsing/rendering/layer/layer.h | 7 +- .../rendering/layer/layergroup.cpp | 2 +- .../globebrowsing/scripts/layer_support.lua | 23 ++ .../tile/rawtiledatareader/gdalwrapper.cpp | 4 + modules/imgui/CMakeLists.txt | 2 + modules/imgui/include/gui.h | 4 + .../imgui/include/guiglobebrowsingcomponent.h | 51 +++ modules/imgui/src/gui.cpp | 39 +- .../imgui/src/guiglobebrowsingcomponent.cpp | 334 ++++++++++++++++++ src/engine/downloadmanager.cpp | 8 +- 17 files changed, 813 insertions(+), 28 deletions(-) create mode 100644 data/globebrowsing_servers.lua create mode 100644 modules/imgui/include/guiglobebrowsingcomponent.h create mode 100644 modules/imgui/src/guiglobebrowsingcomponent.cpp diff --git a/data/globebrowsing_servers.lua b/data/globebrowsing_servers.lua new file mode 100644 index 0000000000..254fd9edb8 --- /dev/null +++ b/data/globebrowsing_servers.lua @@ -0,0 +1,38 @@ +-- This file contains a list of default servers for globes that can be used in the GUI +-- to easily add layers + +return { + Earth = { + { + Name = "GIBS", + URL = "https://gibs.earthdata.nasa.gov/twms/epsg4326/best/twms.cgi?request=GetTileService" + }, + }, + Moon = { + { + Name = "OnMoon LMMP", + URL = "https://onmoon.lmmp.nasa.gov/wms.cgi?request=GetCapabilities" + }, + }, + Mercury = { + { + Name = "USGS Mercury", + URL = "https://planetarymaps.usgs.gov/cgi-bin/mapserv?map=/maps/mercury/mercury_simp_cyl.map&service=WMS&request=GetCapabilities" + }, + }, + Callisto = { + { + Name = "USGS Callisto", + URL = "https://planetarymaps.usgs.gov/cgi-bin/mapserv?map=/maps/jupiter/callisto_simp_cyl.map&service=WMS&request=GetCapabilities" + }, + }, + Deimos = { + { + Name = "USGS Deimos", + URL = "https://planetarymaps.usgs.gov/cgi-bin/mapserv?map=/maps/mars/deimos_simp_cyl.map&service=WMS&request=GetCapabilities" + }, + }, +} + + +-- https://astrowebmaps.wr.usgs.gov/webmapatlas/Layers/maps.html \ No newline at end of file diff --git a/data/scene/default.scene b/data/scene/default.scene index 4bfab819ee..dfd83d1e6c 100644 --- a/data/scene/default.scene +++ b/data/scene/default.scene @@ -56,6 +56,10 @@ function preInitialization() "openspace.setPropertyValue('*Trail.renderable.Enabled', false)", "Disables visibility of the trails" ) + + openspace.globebrowsing.loadWMSServersFromFile( + openspace.absPath("${OPENSPACE_DATA}/globebrowsing_servers.lua") + ) end function postInitialization() diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index b5d99df05f..75be9dfa96 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -263,5 +263,5 @@ if (OPENSPACE_MODULE_GLOBEBROWSING_USE_GDAL) ${GDAL_LIBRARY} ) endif () # WIN32 - target_compile_definitions(openspace-module-globebrowsing PRIVATE GLOBEBROWSING_USE_GDAL) + target_compile_definitions(openspace-module-globebrowsing PUBLIC GLOBEBROWSING_USE_GDAL) endif () # OPENSPACE_MODULE_GLOBEBROWSING_USE_GDAL diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index 109ac7704c..bdd716d3d0 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -50,14 +50,73 @@ #include #include - #include +#ifdef GLOBEBROWSING_USE_GDAL +#include +#include +#endif // GLOBEBROWSING_USE_GDAL + #include "globebrowsingmodule_lua.inl" + namespace { const char* _loggerCat = "GlobeBrowsingModule"; -} + +#ifdef GLOBEBROWSING_USE_GDAL + openspace::GlobeBrowsingModule::Capabilities + parseSubDatasets(char** subDatasets, int nSubdatasets) + { + // Idea: Iterate over the list of sublayers keeping a current layer and identify it + // by its number. If this number changes, we know that we have a new layer + + + using Layer = openspace::GlobeBrowsingModule::Layer; + std::vector result; + + int currentLayerNumber = -1; + Layer currentLayer; + for (int i = 0; i < nSubdatasets; ++i) { + int iDataset = -1; + static char IdentifierBuffer[64]; + sscanf( + subDatasets[i], + "SUBDATASET_%i_%[^=]", + &iDataset, + IdentifierBuffer + ); + + + + if (iDataset != currentLayerNumber) { + // We are done with the previous version + result.push_back(std::move(currentLayer)); + currentLayer = Layer(); + currentLayerNumber = iDataset; + } + + std::string identifier = std::string(IdentifierBuffer); + std::string ds(subDatasets[i]); + std::string value = ds.substr(ds.find_first_of('=') + 1); + + // The DESC/NAME difference is not a typo + if (identifier == "DESC") { + currentLayer.name = value; + } + else if (identifier == "NAME") { + currentLayer.url = value; + } + else { + LINFOC("GlobeBrowsingGUI", "Unknown subdataset identifier: " + identifier); + } + } + + return result; + } + +#endif // GLOBEBROWSING_USE_GDAL + +} // namespace namespace openspace { @@ -172,6 +231,33 @@ scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const { "void", "Get geographic coordinates of the camera poosition in latitude, " "longitude, and altitude" + }, + { + "loadWMSCapabilities", + &globebrowsing::luascriptfunctions::loadWMSCapabilities, + "string, string, string", + "Loads and parses the WMS capabilities xml file from a remote server. " + "The first argument is the name of the capabilities that can be used to " + "later refer to the set of capabilities. The second argument is the " + "globe for which this server is applicable. The third argument is the " + "URL at which the capabilities file can be found." + }, + { + "removeWMSServer", + &globebrowsing::luascriptfunctions::removeWMSServer, + "string", + "Removes the WMS server identified by the first argument from the list " + "of available servers. The parameter corrsponds to the first argument in " + "the loadWMSCapabilities call that was used to load the WMS server." + }, + { + "capabilitiesWMS", + &globebrowsing::luascriptfunctions::capabilities, + "string", + "Returns an array of tables that describe the available layers that are " + "supported by the WMS server identified by the provided name. The 'URL'" + "component of the returned table can be used in the 'FilePath' argument " + "for a call to the 'addLayer' function to add the value to a globe." } }, { @@ -368,4 +454,95 @@ std::string GlobeBrowsingModule::layerTypeNamesList() { return listLayerTypes; } +#ifdef GLOBEBROWSING_USE_GDAL + +void GlobeBrowsingModule::loadWMSCapabilities(std::string name, std::string globe, + std::string url) +{ + auto downloadFunction = [](const std::string& url) { + GDALDatasetH dataset = GDALOpen( + url.c_str(), + GA_ReadOnly + ); + // GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, + // nullptr, + // nullptr, + // nullptr + //); + + char** subDatasets = GDALGetMetadata(dataset, "SUBDATASETS"); + int nSubdatasets = CSLCount(subDatasets); + Capabilities cap = parseSubDatasets(subDatasets, nSubdatasets); + GDALClose(dataset); + return cap; + }; + + _inFlightCapabilitiesMap[name] = std::async(std::launch::async, downloadFunction, url); + + _urlList.emplace(std::move(globe), UrlInfo{ std::move(name), std::move(url) }); +} + +GlobeBrowsingModule::Capabilities +GlobeBrowsingModule::capabilities(const std::string& name) +{ + // First check the ones that have already finished + auto it = _capabilitiesMap.find(name); + if (it != _capabilitiesMap.end()) { + return it->second; + } + else { + auto inFlightIt = _inFlightCapabilitiesMap.find(name); + if (inFlightIt != _inFlightCapabilitiesMap.end()) { + // If the download and the parsing has not finished yet, this will block, + // otherwise it will just return + Capabilities cap = inFlightIt->second.get(); + _capabilitiesMap[name] = cap; + _inFlightCapabilitiesMap.erase(inFlightIt); + return cap; + } + else { + return {}; + } + } +} + +void GlobeBrowsingModule::removeWMSServer(const std::string& name) { + // First delete all the capabilities that are currently in flight + auto inFlightIt = _inFlightCapabilitiesMap.find(name); + if (inFlightIt != _inFlightCapabilitiesMap.end()) { + _inFlightCapabilitiesMap.erase(inFlightIt); + } + + // Then download the ones that are already finished + auto capIt = _capabilitiesMap.find(name); + if (capIt != _capabilitiesMap.end()) { + _capabilitiesMap.erase(capIt); + } + + // Then remove the calues from the globe server list + for (auto it = _urlList.begin(); it != _urlList.end(); ) { + // We have to increment first because the erase will invalidate the iterator + auto eraseIt = it++; + + if (eraseIt->second.name == name) { + _urlList.erase(eraseIt); + } + } +} + + +std::vector +GlobeBrowsingModule::urlInfo(const std::string& globe) const +{ + auto range = _urlList.equal_range(globe); + std::vector res; + for (auto i = range.first; i != range.second; ++i) { + res.emplace_back(i->second); + } + return res; +} + + +#endif // GLOBEBROWSING_USE_GDAL + } // namespace openspace diff --git a/modules/globebrowsing/globebrowsingmodule.h b/modules/globebrowsing/globebrowsingmodule.h index f6fe3c289b..c9874794d3 100644 --- a/modules/globebrowsing/globebrowsingmodule.h +++ b/modules/globebrowsing/globebrowsingmodule.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace openspace::globebrowsing { class RenderableGlobe; @@ -56,6 +57,30 @@ public: scripting::LuaLibrary luaLibrary() const override; globebrowsing::RenderableGlobe* castFocusNodeRenderableToGlobe(); +#ifdef GLOBEBROWSING_USE_GDAL + + struct Layer { + std::string name; + std::string url; + }; + using Capabilities = std::vector; + + // Stores the mapping between globe to names + struct UrlInfo { + std::string name; + std::string url; + }; + + // Registers then user-usable name + void loadWMSCapabilities(std::string name, std::string globe, std::string url); + Capabilities capabilities(const std::string& name); + + std::vector urlInfo(const std::string& globe) const; + + void removeWMSServer(const std::string& name); + +#endif // GLOBEBROWSING_USE_GDAL + protected: void internalInitialize() override; @@ -80,6 +105,18 @@ private: static std::string layerTypeNamesList(); std::unique_ptr _tileCache; + +#ifdef GLOBEBROWSING_USE_GDAL + + // name -> capabilities + std::map> _inFlightCapabilitiesMap; + // name -> capabilities + std::map _capabilitiesMap; + + + std::multimap _urlList; + +#endif // GLOBEBROWSING_USE_GDAL }; } // namespace openspace diff --git a/modules/globebrowsing/globebrowsingmodule_lua.inl b/modules/globebrowsing/globebrowsingmodule_lua.inl index 35a1fd2b7d..6ed1fe8472 100644 --- a/modules/globebrowsing/globebrowsingmodule_lua.inl +++ b/modules/globebrowsing/globebrowsingmodule_lua.inl @@ -148,17 +148,17 @@ int goToChunk(lua_State* L) { OsEng.moduleEngine().module()->goToChunk(x, y, level); - return 0; +return 0; } int goToGeo(lua_State* L) { using ghoul::lua::luaTypeToString; - + int nArguments = lua_gettop(L); if (nArguments != 2 && nArguments != 3) { return luaL_error(L, "Expected 2 or 3 arguments."); } - + double latitude = static_cast(lua_tonumber(L, 1)); double longitude = static_cast(lua_tonumber(L, 2)); @@ -168,20 +168,20 @@ int goToGeo(lua_State* L) { else if (nArguments == 3) { double altitude = static_cast(lua_tonumber(L, 3)); OsEng.moduleEngine().module()->goToGeo(latitude, longitude, - altitude); + altitude); } - + return 0; } int getGeoPosition(lua_State* L) { int nArguments = lua_gettop(L); if (nArguments != 0) { - return luaL_error(L, "Expected 0 arguments."); + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); } RenderableGlobe* globe = - OsEng.moduleEngine().module()->castFocusNodeRenderableToGlobe(); + OsEng.moduleEngine().module()->castFocusNodeRenderableToGlobe(); if (!globe) { return luaL_error(L, "Focus node must be a RenderableGlobe"); } @@ -192,16 +192,79 @@ int getGeoPosition(lua_State* L) { glm::dvec3 cameraPositionModelSpace = inverseModelTransform * glm::dvec4(cameraPosition, 1.0); SurfacePositionHandle posHandle = globe->calculateSurfacePositionHandle( - cameraPositionModelSpace); - + cameraPositionModelSpace); + Geodetic2 geo2 = globe->ellipsoid().cartesianToGeodetic2(posHandle.centerToReferenceSurface); double altitude = glm::length(cameraPositionModelSpace - posHandle.centerToReferenceSurface); lua_pushnumber(L, Angle::fromRadians(geo2.lat).asDegrees()); lua_pushnumber(L, Angle::fromRadians(geo2.lon).asDegrees()); lua_pushnumber(L, altitude); - + return 3; } +int loadWMSCapabilities(lua_State* L) { + int nArguments = lua_gettop(L); + + if (nArguments != 3) { + return luaL_error(L, "Expected %i arguments, got %i", 3, nArguments); + } + + std::string name = lua_tostring(L, -3); + std::string globe = lua_tostring(L, -2); + std::string url = lua_tostring(L, -1); + + OsEng.moduleEngine().module()->loadWMSCapabilities( + std::move(name), + std::move(globe), + std::move(url) + ); + + return 0; +} + +int removeWMSServer(lua_State* L) { + int nArguments = lua_gettop(L); + + if (nArguments != 1) { + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + } + + std::string name = lua_tostring(L, -1); + OsEng.moduleEngine().module()->removeWMSServer(name); + return 0; +} + +int capabilities(lua_State* L) { + int nArguments = lua_gettop(L); + + if (nArguments != 1) { + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + } + + std::string name = lua_tostring(L, -1); + GlobeBrowsingModule::Capabilities cap = + OsEng.moduleEngine().module()->capabilities(name); + + lua_newtable(L); + for (int i = 0; i < cap.size(); ++i) { + const GlobeBrowsingModule::Layer& l = cap[i]; + + lua_newtable(L); + + lua_pushstring(L, "Name"); + lua_pushstring(L, l.name.c_str()); + lua_settable(L, -3); + + lua_pushstring(L, "URL"); + lua_pushstring(L, l.url.c_str()); + lua_settable(L, -3); + + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + } // namespace openspace::globebrowsing::luascriptfunctions diff --git a/modules/globebrowsing/rendering/layer/layer.cpp b/modules/globebrowsing/rendering/layer/layer.cpp index 105a05e7d3..20366a1954 100644 --- a/modules/globebrowsing/rendering/layer/layer.cpp +++ b/modules/globebrowsing/rendering/layer/layer.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -71,6 +72,13 @@ namespace { "local cache for this layer and will trigger a fresh load of all tiles." }; + static const openspace::properties::Property::PropertyInfo RemoveInfo = { + "Remove", + "Remove", + "If this value is triggered, a script will be executed that will remove this " + "layer before the next frame." + }; + static const openspace::properties::Property::PropertyInfo ColorInfo = { "Color", "Color", @@ -79,15 +87,18 @@ namespace { }; } // namespace -Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict) +Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict, + LayerGroup& parent) : properties::PropertyOwner({ layerDict.value(keyName), layerDict.hasKey(keyDescription) ? layerDict.value(keyDescription) : "" }) + , _parent(parent) , _typeOption(TypeInfo, properties::OptionProperty::DisplayType::Dropdown) , _blendModeOption(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown) , _enabled(EnabledInfo, false) , _reset(ResetInfo) + , _remove(RemoveInfo) , _tileProvider(nullptr) , _otherTypesProperties({ { ColorInfo, glm::vec4(1.f), glm::vec4(0.f), glm::vec4(1.f) } @@ -160,6 +171,14 @@ Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict) } }); + _remove.onChange([&](){ + if (_tileProvider) { + _tileProvider->reset(); + } + + _parent.deleteLayer(name()); + }); + _typeOption.onChange([&](){ removeVisibleProperties(); _type = static_cast(_typeOption.value()); @@ -190,6 +209,7 @@ Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict) addProperty(_blendModeOption); addProperty(_enabled); addProperty(_reset); + addProperty(_remove); _otherTypesProperties.color.setViewOption(properties::Property::ViewOptions::Color); diff --git a/modules/globebrowsing/rendering/layer/layer.h b/modules/globebrowsing/rendering/layer/layer.h index 4700b15c53..9adbeae1fc 100644 --- a/modules/globebrowsing/rendering/layer/layer.h +++ b/modules/globebrowsing/rendering/layer/layer.h @@ -39,6 +39,8 @@ namespace openspace::globebrowsing { +struct LayerGroup; + namespace tileprovider { class TileProvider; } class Layer : public properties::PropertyOwner { @@ -51,7 +53,7 @@ public: properties::Vec3Property color; }; - Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict); + Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict, LayerGroup& parent); ChunkTilePile getChunkTilePile(const TileIndex& tileIndex, int pileSize) const; Tile::Status getTileStatus(const TileIndex& index) const; @@ -81,11 +83,14 @@ private: void initializeBasedOnType(layergroupid::TypeID typeId, ghoul::Dictionary initDict); void addVisibleProperties(); void removeVisibleProperties(); + + LayerGroup& _parent; properties::OptionProperty _typeOption; properties::OptionProperty _blendModeOption; properties::BoolProperty _enabled; properties::TriggerProperty _reset; + properties::TriggerProperty _remove; layergroupid::TypeID _type; std::shared_ptr _tileProvider; diff --git a/modules/globebrowsing/rendering/layer/layergroup.cpp b/modules/globebrowsing/rendering/layer/layergroup.cpp index 7ff67d6861..85ddabfecf 100644 --- a/modules/globebrowsing/rendering/layer/layergroup.cpp +++ b/modules/globebrowsing/rendering/layer/layergroup.cpp @@ -95,7 +95,7 @@ void LayerGroup::addLayer(const ghoul::Dictionary& layerDict) { LERROR("'Name' must be specified for layer."); return; } - auto layer = std::make_shared(_groupId, layerDict); + auto layer = std::make_shared(_groupId, layerDict, *this); layer->onChange(_onChangeCallback); if (hasPropertySubOwner(layer->name())) { LINFO("Layer with name " + layer->name() + " already exists."); diff --git a/modules/globebrowsing/scripts/layer_support.lua b/modules/globebrowsing/scripts/layer_support.lua index 9304a41da7..51b718051b 100644 --- a/modules/globebrowsing/scripts/layer_support.lua +++ b/modules/globebrowsing/scripts/layer_support.lua @@ -69,6 +69,13 @@ openspace.globebrowsing.documentation = { "info files are then added to the RenderableGlobe identified by name passed " .. "to the second argument." .. "Usage: openspace.globebrowsing.addBlendingLayersFromDirectory(directory, \"Earth\")" + }, + { + Name = "loadWMSServersFromFile", + Arguments = "string", + Documentation = + "Loads all WMS servers from the provided file and passes them to the " .. + "'openspace.globebrowsing.loadWMSCapabilities' file." } } @@ -183,4 +190,20 @@ openspace.globebrowsing.addBlendingLayersFromDirectory = function (dir, node_nam openspace.globebrowsing.addLayer(node_name, "HeightLayers", h) end end +end + +openspace.globebrowsing.loadWMSServersFromFile = function (file_path) + local servers = dofile(file_path) + + for key, value in pairs(servers) do + local globe = key + for _,val in pairs(value) do + openspace.globebrowsing.loadWMSCapabilities( + val["Name"], + globe, + val["URL"] + ) + end + end + end \ No newline at end of file diff --git a/modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.cpp b/modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.cpp index e66cc3c2a0..a4bd99204f 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.cpp +++ b/modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.cpp @@ -130,6 +130,10 @@ GdalWrapper::GdalWrapper(size_t maximumCacheSize, size_t maximumMaximumCacheSize "CPL_TMPDIR", absPath("${BASE_PATH}").c_str() ); + CPLSetConfigOption( + "GDAL_HTTP_UNSAFESSL", + "YES" + ); setGdalProxyConfiguration(); CPLSetErrorHandler(gdalErrorHandler); diff --git a/modules/imgui/CMakeLists.txt b/modules/imgui/CMakeLists.txt index 40c237ba56..b89c0762a9 100644 --- a/modules/imgui/CMakeLists.txt +++ b/modules/imgui/CMakeLists.txt @@ -30,6 +30,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/gui.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guicomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guifilepathcomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guiglobebrowsingcomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guihelpcomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guiorigincomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guiperformancecomponent.h @@ -47,6 +48,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/gui_lua.inl ${CMAKE_CURRENT_SOURCE_DIR}/src/guicomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guifilepathcomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guiglobebrowsingcomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guihelpcomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guiorigincomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guiperformancecomponent.cpp diff --git a/modules/imgui/include/gui.h b/modules/imgui/include/gui.h index fd3afe972f..18bb1ee9c2 100644 --- a/modules/imgui/include/gui.h +++ b/modules/imgui/include/gui.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -66,6 +67,7 @@ public: //protected: GuiHelpComponent _help; GuiFilePathComponent _filePath; + GuiGlobeBrowsingComponent _globeBrowsing; GuiOriginComponent _origin; GuiPerformanceComponent _performance; GuiPropertyComponent _globalProperty; @@ -78,6 +80,8 @@ public: #endif // OPENSPACE_MODULE_ISWA_ENABLED GuiParallelComponent _parallel; + bool _showInternals; + private: void renderAndUpdatePropertyVisibility(); diff --git a/modules/imgui/include/guiglobebrowsingcomponent.h b/modules/imgui/include/guiglobebrowsingcomponent.h new file mode 100644 index 0000000000..3626b8bd4b --- /dev/null +++ b/modules/imgui/include/guiglobebrowsingcomponent.h @@ -0,0 +1,51 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * 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. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_IMGUI___GUIGLOBEBROWSINGCOMPONENT___H__ +#define __OPENSPACE_MODULE_IMGUI___GUIGLOBEBROWSINGCOMPONENT___H__ + +#include + +#include + +#include +#include +#include + +namespace openspace::gui { + +class GuiGlobeBrowsingComponent : public GuiPropertyComponent { +public: + GuiGlobeBrowsingComponent(); + void render() override; + +private: + std::string _currentNode; + std::string _currentServer; +}; + +} // namespace openspace::gui + + +#endif // __OPENSPACE_MODULE_IMGUI___GUIGLOBEBROWSINGCOMPONENT___H__ diff --git a/modules/imgui/src/gui.cpp b/modules/imgui/src/gui.cpp index b2e56bcd2d..6405c9780d 100644 --- a/modules/imgui/src/gui.cpp +++ b/modules/imgui/src/gui.cpp @@ -232,6 +232,7 @@ GUI::GUI() , _property("Properties") , _screenSpaceProperty("ScreenSpace Properties") , _virtualProperty("Virtual Properties") + , _showInternals(false) , _currentVisibility(properties::Property::Visibility::Developer) { addPropertySubOwner(_help); @@ -241,6 +242,7 @@ GUI::GUI() addPropertySubOwner(_property); addPropertySubOwner(_screenSpaceProperty); addPropertySubOwner(_virtualProperty); + addPropertySubOwner(_globeBrowsing); addPropertySubOwner(_filePath); addPropertySubOwner(_time); #ifdef OPENSPACE_MODULE_ISWA_ENABLED @@ -328,6 +330,7 @@ void GUI::initialize() { _globalProperty.setHasRegularProperties(true); _virtualProperty.initialize(); _filePath.initialize(); + _globeBrowsing.initialize(); _performance.initialize(); _help.initialize(); _parallel.initialize(); @@ -349,6 +352,7 @@ void GUI::deinitialize() { _screenSpaceProperty.deinitialize(); _virtualProperty.deinitialize(); _filePath.deinitialize(); + _globeBrowsing.deinitialize(); _property.deinitialize(); delete iniFileBuffer; @@ -424,6 +428,8 @@ void GUI::initializeGL() { _globalProperty.initializeGL(); _performance.initializeGL(); _help.initializeGL(); + _globeBrowsing.initializeGL(); + _filePath.initializeGL(); _parallel.initializeGL(); #ifdef OPENSPACE_MODULE_ISWA_ENABLED _iswa.initializeGL(); @@ -453,6 +459,8 @@ void GUI::deinitializeGL() { _performance.deinitializeGL(); _globalProperty.deinitializeGL(); _screenSpaceProperty.deinitializeGL(); + _globeBrowsing.deinitializeGL(); + _filePath.deinitializeGL(); _property.deinitializeGL(); } @@ -511,6 +519,10 @@ void GUI::endFrame() { if (_filePath.isEnabled()) { _filePath.render(); } + + if (_globeBrowsing.isEnabled()) { + _globeBrowsing.render(); + } } ImGui::Render(); @@ -610,6 +622,10 @@ void GUI::render() { ImGui::Checkbox("File Paths", &filePath); _filePath.setEnabled(filePath); + bool globeBrowsing = _globeBrowsing.isEnabled(); + ImGui::Checkbox("GlobeBrowsing", &globeBrowsing); + _globeBrowsing.setEnabled(globeBrowsing); + #ifdef OPENSPACE_MODULE_ISWA_ENABLED bool iswa = _iswa.isEnabled(); ImGui::Checkbox("iSWA", &iswa); @@ -638,19 +654,20 @@ void GUI::render() { addScreenSpaceRenderable(std::string(addImageBuffer)); } -#ifdef SHOW_IMGUI_HELPERS - ImGui::Begin("Style Editor"); - ImGui::ShowStyleEditor(); - ImGui::End(); + ImGui::Checkbox("ImGUI Internals", &_showInternals); + if (_showInternals) { + ImGui::Begin("Style Editor"); + ImGui::ShowStyleEditor(); + ImGui::End(); - ImGui::Begin("Test Window"); - ImGui::ShowTestWindow(); - ImGui::End(); + ImGui::Begin("Test Window"); + ImGui::ShowTestWindow(); + ImGui::End(); - ImGui::Begin("Metrics Window"); - ImGui::ShowMetricsWindow(); - ImGui::End(); -#endif + ImGui::Begin("Metrics Window"); + ImGui::ShowMetricsWindow(); + ImGui::End(); + } ImGui::End(); } diff --git a/modules/imgui/src/guiglobebrowsingcomponent.cpp b/modules/imgui/src/guiglobebrowsingcomponent.cpp new file mode 100644 index 0000000000..d935593ecb --- /dev/null +++ b/modules/imgui/src/guiglobebrowsingcomponent.cpp @@ -0,0 +1,334 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * 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. * + ****************************************************************************************/ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +namespace { + const ImVec2 WindowSize = ImVec2(350, 500); +} // namespace + +namespace openspace::gui { + +GuiGlobeBrowsingComponent::GuiGlobeBrowsingComponent() + : GuiPropertyComponent("GlobeBrowsing") +{} + +void GuiGlobeBrowsingComponent::render() { + GlobeBrowsingModule* module = OsEng.moduleEngine().module(); + using UrlInfo = GlobeBrowsingModule::UrlInfo; + using Capabilities = GlobeBrowsingModule::Capabilities; + using Layer = GlobeBrowsingModule::Layer; + + bool e = _isEnabled; + e = e; + + ImGui::Begin("Globe Browsing", &e, WindowSize, 0.5f); + _isEnabled = e; + OnExit([]() {ImGui::End(); }); // We escape early from this function in a few places + + + // Render the list of planets + std::vector nodes = + OsEng.renderEngine().scene()->allSceneGraphNodes(); + + nodes.erase( + std::remove_if( + nodes.begin(), + nodes.end(), + [](SceneGraphNode* n) { + return !(n->renderable() && n->renderable()->name() == "RenderableGlobe"); + } + ), + nodes.end() + ); + std::sort( + nodes.begin(), + nodes.end(), + [](SceneGraphNode* lhs, SceneGraphNode* rhs) { return lhs->name() < rhs->name(); } + ); + std::string nodeNames; + for (SceneGraphNode* n : nodes) { + nodeNames += n->name() + '\0'; + } + + int iNode = -1; + if (_currentNode.empty()) { + // We haven't selected a node yet, so first instinct is to use the current focus + // node + + // Check if the focus node is a RenderableGlobe + const SceneGraphNode* const focus = OsEng.navigationHandler().focusNode(); + auto it = std::find(nodes.cbegin(), nodes.cend(), focus); + if (it != nodes.end()) { + _currentNode = focus->name(); + iNode = static_cast(std::distance(nodes.cbegin(), it)); + } + } + else { + auto it = std::find_if( + nodes.cbegin(), + nodes.cend(), + [this](SceneGraphNode* lhs) { + return lhs->name() == _currentNode; + } + ); + iNode = static_cast(std::distance(nodes.cbegin(), it)); + } + + bool nodeChanged = ImGui::Combo("Globe", &iNode, nodeNames.c_str()); + + ImGui::SameLine(); + bool selectFocusNode = ImGui::Button("From Focus"); + if (selectFocusNode) { + const SceneGraphNode* const focus = OsEng.navigationHandler().focusNode(); + auto it = std::find(nodes.cbegin(), nodes.cend(), focus); + if (it != nodes.end()) { + _currentNode = focus->name(); + iNode = static_cast(std::distance(nodes.cbegin(), it)); + nodeChanged = true; + } + } + + if (iNode == -1) { + // This should only occur if the Focusnode is not a RenderableGlobe + // or if there are no nodes + return; + } + _currentNode = nodes[iNode]->name(); + + if (nodeChanged) { + _currentServer = ""; + } + + ImGui::Separator(); + + // Render the list of servers for the planet + std::vector urlInfo = module->urlInfo(_currentNode); + + std::string serverList = std::accumulate( + urlInfo.cbegin(), + urlInfo.cend(), + std::string(), + [](std::string lhs, const UrlInfo& i) { + return lhs + i.name + ": (" + i.url + ")" + '\0'; + } + ); + + int iServer = -1; + if (_currentServer.empty()) { + // We haven't selected a server yet, so first instinct is to just use the first + if (!urlInfo.empty()) { + _currentServer = urlInfo[0].name; + iServer = 0; + } + } + else { + auto it = std::find_if( + urlInfo.cbegin(), + urlInfo.cend(), + [this](const UrlInfo& i) { + return i.name == _currentServer; + } + ); + if (it != urlInfo.end()) { + iServer = static_cast(std::distance(urlInfo.cbegin(), it)); + } + } + + ImGui::Combo("Server", &iServer, serverList.c_str()); + + ImGui::SameLine(0.f, 60.f); + + if (ImGui::Button("Add Server")) { + ImGui::OpenPopup("globebrowsing_add_server"); + } + + if (ImGui::BeginPopup("globebrowsing_add_server")) { + constexpr int InputBufferSize = 512; + static char NameInputBuffer[InputBufferSize]; + static char UrlInputBuffer[InputBufferSize]; + ImGui::InputText("Server Name", NameInputBuffer, InputBufferSize); + + ImGui::InputText("Server URL", UrlInputBuffer, InputBufferSize); + + bool addServer = ImGui::Button("Add Server"); + if (addServer && (!_currentNode.empty())) { + module->loadWMSCapabilities( + std::string(NameInputBuffer), + _currentNode, + std::string(UrlInputBuffer) + ); + std::memset(NameInputBuffer, 0, InputBufferSize * sizeof(char)); + std::memset(UrlInputBuffer, 0, InputBufferSize * sizeof(char)); + + urlInfo = module->urlInfo(_currentNode); + _currentServer = urlInfo.back().name; + --iServer; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(0.f, 20.f); + + bool deleteServer = ImGui::Button("Delete Server"); + if (deleteServer) { + module->removeWMSServer(_currentServer); + _currentServer = ""; + iServer = -1; + + } + + if (iServer == -1) { + return; + } + _currentServer = urlInfo[iServer].name; + + + + // Can't use urlIt here since it might have been invalidated before + if (urlInfo.empty()) { + // There are no server so we have to bail + return; + } + + ImGui::Separator(); + + Capabilities cap = module->capabilities(_currentServer); + + if (cap.empty()) { + LWARNINGC( + "GlobeBrowsingGUI", + "Unknown server: '" << _currentServer << "'" + ); + } + + ImGui::Columns(6, nullptr, false); + + float width = ImGui::GetWindowWidth(); + constexpr float ButtonWidth = 60.f; + ImGui::SetColumnOffset(5, width - 1.5f * ButtonWidth); + ImGui::SetColumnOffset(4, width - 2.5f * ButtonWidth); + ImGui::SetColumnOffset(3, width - 3.5f * ButtonWidth); + ImGui::SetColumnOffset(2, width - 4.5f * ButtonWidth); + ImGui::SetColumnOffset(1, width - 5.5f * ButtonWidth); + ImGui::SetColumnOffset(0, 0); + + //ImGui::PushItemWidth(500.f); + ImGui::Text("%s", "Layer name"); + ImGui::NextColumn(); + ImGui::Text("%s", "Add as ..."); + ImGui::NextColumn(); + ImGui::NextColumn(); + ImGui::NextColumn(); + ImGui::NextColumn(); + ImGui::NextColumn(); + ImGui::Separator(); + + for (const Layer& l : cap) { + if (l.name.empty() || l.url.empty()) { + continue; + } + + ImGui::PushID(l.url.c_str()); + + ImGui::Text("%s", l.name.c_str()); + ImGui::NextColumn(); + + bool addColor = ImGui::Button("Color", { ButtonWidth, 25.f }); + ImGui::NextColumn(); + + bool addNight = ImGui::Button("Night", { ButtonWidth, 25.f }); + ImGui::NextColumn(); + + bool addOverlay = ImGui::Button("Overlay", { ButtonWidth, 25.f }); + ImGui::NextColumn(); + + bool addHeight = ImGui::Button("Height", { ButtonWidth, 25.f }); + ImGui::NextColumn(); + + bool addWaterMask = ImGui::Button("Water", { ButtonWidth, 25.f }); + ImGui::NextColumn(); + + auto addFunc = [this, &l](const std::string& type) { + std::string layerName = l.name; + std::replace(layerName.begin(), layerName.end(), '.', '-'); + OsEng.scriptEngine().queueScript( + fmt::format( + "openspace.globebrowsing.addLayer(\ + '{}', \ + '{}', \ + {{ Name = '{}', FilePath = '{}', Enabled = true }}\ + );", + _currentNode, + type, + layerName, + l.url + ), + scripting::ScriptEngine::RemoteScripting::Yes + ); + }; + + if (addColor) { + addFunc("ColorLayers"); + } + if (addNight) { + addFunc("NightLayers"); + } + if (addOverlay) { + addFunc("Overlays"); + } + if (addHeight) { + addFunc("HeightLayers"); + } + if (addWaterMask) { + addFunc("WaterMasks"); + } + + ImGui::PopID(); + } + ImGui::Columns(1); +} + +} // namespace openspace::gui diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index 1923be3863..f41b9483a6 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -268,6 +268,8 @@ std::future DownloadManager::fetchFile( curl_easy_setopt(curl, CURLOPT_WRITEDATA, reinterpret_cast(&file)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeMemoryCallback); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); + // Will fail when response status is 400 or above curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); @@ -290,7 +292,11 @@ std::future DownloadManager::fetchFile( return file; } else { std::string err = curl_easy_strerror(res); - errorCallback(err); + if (errorCallback) { + errorCallback(err); + } else { + LWARNING("Error downloading '" << url << "': " << err); + } curl_easy_cleanup(curl); // Throw an error and use try-catch around call to future.get() //throw std::runtime_error( err );