/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2021 * * * * 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", "Globe Browsing") {} void GuiGlobeBrowsingComponent::render() { GlobeBrowsingModule* module = global::moduleEngine->module(); using UrlInfo = GlobeBrowsingModule::UrlInfo; using Capabilities = GlobeBrowsingModule::Capabilities; using Layer = GlobeBrowsingModule::Layer; ImGui::SetNextWindowCollapsed(_isCollapsed); bool e = _isEnabled; ImGui::SetNextWindowSize(WindowSize, ImGuiCond_FirstUseEver); ImGui::SetNextWindowBgAlpha(0.5f); ImGui::Begin("Globe Browsing", &e); _isEnabled = e; _isCollapsed = ImGui::IsWindowCollapsed(); defer { ImGui::End(); }; // Render the list of planets std::vector nodes = global::renderEngine->scene()->allSceneGraphNodes(); nodes.erase( std::remove_if( nodes.begin(), nodes.end(), [](SceneGraphNode* n) { using namespace globebrowsing; const Renderable* r = n->renderable(); const RenderableGlobe* rg = dynamic_cast(r); return rg == nullptr; } ), nodes.end() ); std::sort( nodes.begin(), nodes.end(), [module](SceneGraphNode* lhs, SceneGraphNode* rhs) { const bool lhsHasUrl = module->hasUrlInfo(lhs->identifier()); const bool rhsHasUrl = module->hasUrlInfo(rhs->identifier()); if (lhsHasUrl && !rhsHasUrl) { return true; } if (!lhsHasUrl && rhsHasUrl) { return false; } return lhs->guiName() < rhs->guiName(); } ); auto firstWithoutUrl = std::find_if( nodes.begin(), nodes.end(), [module](SceneGraphNode* n) { return !module->hasUrlInfo(n->identifier()); } ); nodes.insert(firstWithoutUrl, nullptr); std::string nodeNames; for (SceneGraphNode* n : nodes) { // Add separator between nodes with URL and nodes without if (n) { nodeNames += n->identifier() + '\0'; } else { nodeNames += std::string("===== =====") + '\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 = global::navigationHandler->orbitalNavigator().anchorNode(); const auto it = std::find(nodes.cbegin(), nodes.cend(), focus); if (it != nodes.end()) { _currentNode = focus->identifier(); iNode = static_cast(std::distance(nodes.cbegin(), it)); } } else { const auto it = std::find_if( nodes.cbegin(), nodes.cend(), [this](SceneGraphNode* lhs) { return lhs && (lhs->identifier() == _currentNode); } ); iNode = static_cast(std::distance(nodes.cbegin(), it)); } bool isNodeChanged = ImGui::Combo("Globe", &iNode, nodeNames.c_str()); ImGui::SameLine(); bool selectFocusNode = ImGui::Button("From Focus"); if (selectFocusNode) { const SceneGraphNode* const focus = global::navigationHandler->orbitalNavigator().anchorNode(); const auto it = std::find(nodes.cbegin(), nodes.cend(), focus); if (it != nodes.end()) { _currentNode = focus->identifier(); iNode = static_cast(std::distance(nodes.cbegin(), it)); isNodeChanged = true; } } if (iNode == -1) { // This should only occur if the Focusnode is not a RenderableGlobe // or if there are no nodes return; } if (!nodes[iNode]) { // The user selected the separator return; } _currentNode = nodes[iNode]->identifier(); if (isNodeChanged) { _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(), [](const 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 { const 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 const int InputBufferSize = 512; static char NameInputBuffer[InputBufferSize]; ImGui::InputText("Server Name", NameInputBuffer, InputBufferSize); static char UrlInputBuffer[InputBufferSize]; ImGui::InputText("Server URL", UrlInputBuffer, InputBufferSize); const 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); const bool deleteServer = ImGui::Button("Delete Server"); if (deleteServer) { module->removeWMSServer(_currentServer); _currentServer = ""; iServer = -1; } if (iServer < 0 || urlInfo.empty()) { // There are no server so we have to bail return; } _currentServer = urlInfo[iServer].name; ImGui::Separator(); Capabilities cap = module->capabilities(_currentServer); if (cap.empty()) { LWARNINGC("GlobeBrowsing", fmt::format("Unknown server: '{}'", _currentServer)); } ImGui::Columns(6, nullptr, false); const float width = ImGui::GetWindowWidth(); constexpr const 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::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(); const bool addColor = ImGui::Button("Color", { ButtonWidth, 25.f }); ImGui::NextColumn(); const bool addNight = ImGui::Button("Night", { ButtonWidth, 25.f }); ImGui::NextColumn(); const bool addOverlay = ImGui::Button("Overlay", { ButtonWidth, 25.f }); ImGui::NextColumn(); const bool addHeight = ImGui::Button("Height", { ButtonWidth, 25.f }); ImGui::NextColumn(); const bool addWaterMask = ImGui::Button("Water", { ButtonWidth, 25.f }); ImGui::NextColumn(); auto addFunc = [n = _currentNode, &l](const std::string& type) { std::string layerName = l.name; std::replace(layerName.begin(), layerName.end(), '.', '-'); layerName.erase( std::remove(layerName.begin(), layerName.end(), ' '), layerName.end() ); global::scriptEngine->queueScript( fmt::format( "openspace.globebrowsing.addLayer(\ '{}', \ '{}', \ {{ \ Identifier = '{}',\ Name = '{}',\ FilePath = '{}',\ Enabled = true\ }}\ );", n, type, layerName, l.name, 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