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
This commit is contained in:
Alexander Bock
2017-08-19 20:23:08 -04:00
committed by GitHub
parent 09a94e6bf5
commit 222bbe22ab
17 changed files with 813 additions and 28 deletions

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -50,14 +50,73 @@
#include <ghoul/misc/templatefactory.h>
#include <ghoul/misc/assert.h>
#include <ghoul/systemcapabilities/generalcapabilitiescomponent.h>
#ifdef GLOBEBROWSING_USE_GDAL
#include <gdal.h>
#include <cpl_string.h>
#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<Layer> 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>
GlobeBrowsingModule::urlInfo(const std::string& globe) const
{
auto range = _urlList.equal_range(globe);
std::vector<UrlInfo> res;
for (auto i = range.first; i != range.second; ++i) {
res.emplace_back(i->second);
}
return res;
}
#endif // GLOBEBROWSING_USE_GDAL
} // namespace openspace

View File

@@ -28,6 +28,7 @@
#include <openspace/util/openspacemodule.h>
#include <ghoul/glm.h>
#include <memory>
#include <future>
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<Layer>;
// 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> 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<globebrowsing::cache::MemoryAwareTileCache> _tileCache;
#ifdef GLOBEBROWSING_USE_GDAL
// name -> capabilities
std::map<std::string, std::future<Capabilities>> _inFlightCapabilitiesMap;
// name -> capabilities
std::map<std::string, Capabilities> _capabilitiesMap;
std::multimap<std::string, UrlInfo> _urlList;
#endif // GLOBEBROWSING_USE_GDAL
};
} // namespace openspace

View File

@@ -148,17 +148,17 @@ int goToChunk(lua_State* L) {
OsEng.moduleEngine().module<GlobeBrowsingModule>()->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<double>(lua_tonumber(L, 1));
double longitude = static_cast<double>(lua_tonumber(L, 2));
@@ -168,20 +168,20 @@ int goToGeo(lua_State* L) {
else if (nArguments == 3) {
double altitude = static_cast<int>(lua_tonumber(L, 3));
OsEng.moduleEngine().module<GlobeBrowsingModule>()->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<GlobeBrowsingModule>()->castFocusNodeRenderableToGlobe();
OsEng.moduleEngine().module<GlobeBrowsingModule>()->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<double>::fromRadians(geo2.lat).asDegrees());
lua_pushnumber(L, Angle<double>::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<GlobeBrowsingModule>()->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<GlobeBrowsingModule>()->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<GlobeBrowsingModule>()->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

View File

@@ -24,6 +24,7 @@
#include <modules/globebrowsing/rendering/layer/layer.h>
#include <modules/globebrowsing/rendering/layer/layergroup.h>
#include <modules/globebrowsing/rendering/layer/layermanager.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/tile/tiletextureinitdata.h>
@@ -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<std::string>(keyName),
layerDict.hasKey(keyDescription) ? layerDict.value<std::string>(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<layergroupid::TypeID>(_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);

View File

@@ -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::TileProvider> _tileProvider;

View File

@@ -95,7 +95,7 @@ void LayerGroup::addLayer(const ghoul::Dictionary& layerDict) {
LERROR("'Name' must be specified for layer.");
return;
}
auto layer = std::make_shared<Layer>(_groupId, layerDict);
auto layer = std::make_shared<Layer>(_groupId, layerDict, *this);
layer->onChange(_onChangeCallback);
if (hasPropertySubOwner(layer->name())) {
LINFO("Layer with name " + layer->name() + " already exists.");

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -27,6 +27,7 @@
#include <modules/imgui/include/guicomponent.h>
#include <modules/imgui/include/guifilepathcomponent.h>
#include <modules/imgui/include/guiglobebrowsingcomponent.h>
#include <modules/imgui/include/guihelpcomponent.h>
#include <modules/imgui/include/guiperformancecomponent.h>
#include <modules/imgui/include/guipropertycomponent.h>
@@ -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();

View File

@@ -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 <modules/imgui/include/guipropertycomponent.h>
#include <openspace/engine/downloadmanager.h>
#include <map>
#include <string>
#include <vector>
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__

View File

@@ -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();
}

View File

@@ -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 <modules/imgui/include/guiglobebrowsingcomponent.h>
#include <modules/imgui/include/imgui_include.h>
#include <modules/globebrowsing/globebrowsingmodule.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/rendering/renderable.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scripting/scriptengine.h>
#include <ghoul/misc/onscopeexit.h>
#include <ghoul/lua/ghoul_lua.h>
#include <fmt/format.h>
#include <numeric>
namespace {
const ImVec2 WindowSize = ImVec2(350, 500);
} // namespace
namespace openspace::gui {
GuiGlobeBrowsingComponent::GuiGlobeBrowsingComponent()
: GuiPropertyComponent("GlobeBrowsing")
{}
void GuiGlobeBrowsingComponent::render() {
GlobeBrowsingModule* module = OsEng.moduleEngine().module<GlobeBrowsingModule>();
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<SceneGraphNode*> 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<int>(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<int>(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<int>(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> 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<int>(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

View File

@@ -268,6 +268,8 @@ std::future<DownloadManager::MemoryFile> DownloadManager::fetchFile(
curl_easy_setopt(curl, CURLOPT_WRITEDATA, reinterpret_cast<void*>(&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::MemoryFile> 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 );