satdump/src-interface/viewer/viewer.cpp

406 lines
19 KiB
C++
Raw Normal View History

2022-04-12 20:18:44 +02:00
#include "viewer.h"
#include "image_handler.h"
2022-04-16 08:10:25 +02:00
#include "radiation_handler.h"
2022-09-29 00:46:17 +02:00
#include "scatterometer_handler.h"
2022-04-12 20:18:44 +02:00
#include "core/config.h"
2022-04-16 08:10:25 +02:00
#include "products/dataset.h"
#include "common/utils.h"
2023-04-13 21:05:19 +02:00
#include "resources.h"
#include "main_ui.h"
2022-04-12 20:18:44 +02:00
void SelectableColor(ImU32 color) // Colors a cell in the table with the specified color in RGBA
2022-09-08 20:52:57 +02:00
{
ImVec2 p_min = ImGui::GetItemRectMin();
ImVec2 p_max = ImGui::GetItemRectMax();
ImGui::GetWindowDrawList()->AddRectFilled(p_min, p_max, color);
}
2022-04-12 20:18:44 +02:00
namespace satdump
{
ViewerApplication::ViewerApplication()
: Application("viewer")
{
projection_overlay_handler.draw_map_overlay = true;
projection_overlay_handler.draw_cities_overlay = true;
2022-09-08 22:59:30 +02:00
if (config::main_cfg["user"].contains("viewer_state"))
2022-12-08 23:39:14 +01:00
{
2022-09-08 22:59:30 +02:00
if (config::main_cfg["user"]["viewer_state"].contains("panel_ratio"))
panel_ratio = config::main_cfg["user"]["viewer_state"]["panel_ratio"].get<float>();
2023-10-04 21:40:08 +02:00
if (config::main_cfg["user"]["viewer_state"].contains("save_type"))
save_type = config::main_cfg["user"]["viewer_state"]["save_type"].get<std::string>();
else
save_type = satdump::config::main_cfg["satdump_general"]["image_format"]["value"].get<std::string>();
2022-12-08 23:39:14 +01:00
if (config::main_cfg["user"]["viewer_state"].contains("projections"))
deserialize_projections_config(config::main_cfg["user"]["viewer_state"]["projections"]);
}
std::string default_dir = config::main_cfg["satdump_directories"]["default_input_directory"]["value"].get<std::string>();
projection_new_layer_file.setDefaultDir(default_dir);
projection_new_layer_cfg.setDefaultDir(default_dir);
select_dataset_dialog.setDefaultDir(default_dir);
select_products_dialog.setDefaultDir(default_dir);
2022-04-12 20:18:44 +02:00
// pro.load("/home/alan/Documents/SatDump_ReWork/build/metop_ahrpt_new/AVHRR/product.cbor");
// pro.load("/home/alan/Documents/SatDump_ReWork/build/aqua_test_new/MODIS/product.cbor");
2022-04-26 15:07:18 +02:00
// loadDatasetInViewer("/home/alan/Documents/SatDump_ReWork/build/metop_ahrpt_new/dataset.json");
2022-09-13 00:20:18 +02:00
// loadDatasetInViewer("/home/alan/Documents/SatDump_ReWork/build/metop_idk_damnit/dataset.json");
2022-04-16 08:10:25 +02:00
2023-01-22 18:21:08 +01:00
// loadDatasetInViewer("/tmp/hirs/dataset.json");
2022-09-13 22:57:28 +02:00
// loadDatasetInViewer("/home/zbyszek/Downloads/metopC_15-04_1125/dataset.json");
2022-09-08 20:01:12 +02:00
2022-04-16 08:10:25 +02:00
// loadProductsInViewer("/home/alan/Documents/SatDump_ReWork/build/noaa_mhs_test/AMSU/product.cbor", "NOAA-19 HRPT");
// loadProductsInViewer("/home/alan/Documents/SatDump_ReWork/build/metop_ahrpt_new/AVHRR/product.cbor", "MetOp-B AHRPT");
// loadProductsInViewer("/home/alan/Documents/SatDump_ReWork/build/noaa_mhs_test/MHS/product.cbor", "NOAA-19 HRPT");
// loadProductsInViewer("/home/alan/Documents/SatDump_ReWork/build/noaa_mhs_test_gac/MHS/product.cbor", "NOAA-19 GAC");
// loadProductsInViewer("/home/alan/Documents/SatDump_ReWork/build/noaa_mhs_test_gac/HIRS/product.cbor", "NOAA-19 GAC");
// loadProductsInViewer("/home/alan/Documents/SatDump_ReWork/build/noaa_mhs_test_gac/AVHRR/product.cbor", "NOAA-19 GAC");
2022-04-26 15:07:18 +02:00
// loadProductsInViewer("/home/alan/Documents/SatDump_ReWork/build/npp_hrd_new/VIIRS/product.cbor", "JPSS-1 HRD");
// loadProductsInViewer("/home/alan/Documents/SatDump_ReWork/build/m2_lrpt_test/MSU-MR/product.cbor", "METEOR-M2 LRPT");
2022-04-13 17:51:30 +02:00
}
2023-10-16 11:14:21 -04:00
ViewerApplication::~ViewerApplication()
{
}
void ViewerApplication::save_settings()
{
config::main_cfg["user"]["viewer_state"]["panel_ratio"] = panel_ratio;
config::main_cfg["user"]["viewer_state"]["save_type"] = save_type;
2023-10-16 11:14:21 -04:00
config::main_cfg["user"]["viewer_state"]["projections"] = serialize_projections_config();
}
2022-04-16 08:10:25 +02:00
void ViewerApplication::loadDatasetInViewer(std::string path)
{
ProductDataSet dataset;
dataset.load(path);
2022-09-15 14:39:01 +02:00
std::string dataset_name = dataset.satellite_name + " " + timestamp_to_string(dataset.timestamp);
int i = -1;
bool contains = false;
do
{
contains = false;
std::string curr_name = ((i + 1) == 0 ? dataset_name : (dataset_name + " #" + std::to_string(i + 1)));
for (int i = 0; i < (int)products_and_handlers.size(); i++)
2022-09-16 13:45:20 +02:00
if (products_and_handlers[i]->dataset_name == curr_name)
2022-09-15 14:39:01 +02:00
contains = true;
i++;
} while (contains);
dataset_name = (i == 0 ? dataset_name : (dataset_name + " #" + std::to_string(i)));
2022-05-02 08:11:47 -07:00
std::string pro_directory = std::filesystem::path(path).parent_path().string();
2022-04-16 08:10:25 +02:00
for (std::string pro_path : dataset.products_list)
{
try
{
loadProductsInViewer(pro_directory + "/" + pro_path, dataset_name);
}
catch (std::exception &e)
{
logger->error("Could not open " + pro_path + " in viewer! : %s", e.what());
}
}
2022-04-16 08:10:25 +02:00
}
void ViewerApplication::loadProductsInViewer(std::string path, std::string dataset_name)
2022-04-13 17:51:30 +02:00
{
if (std::filesystem::exists(path))
{
std::shared_ptr<Products> products = loadProducts(path);
// Get instrument settings
nlohmann::ordered_json instrument_viewer_settings;
if (config::main_cfg["viewer"]["instruments"].contains(products->instrument_name))
instrument_viewer_settings = config::main_cfg["viewer"]["instruments"][products->instrument_name];
else
2023-05-08 23:18:15 +02:00
logger->error("Unknown instrument : %s!", products->instrument_name.c_str());
2022-04-13 17:51:30 +02:00
// Init Handler
2022-04-29 13:00:28 +02:00
std::string handler_id;
if (instrument_viewer_settings.contains("handler"))
2022-05-07 18:16:24 +02:00
handler_id = instrument_viewer_settings["handler"].get<std::string>();
2022-04-29 13:00:28 +02:00
else if (products->contents["type"] == "image")
handler_id = "image_handler";
2022-09-29 00:46:17 +02:00
else if (products->contents["type"] == "radiation")
handler_id = "radiation_handler";
else if (products->contents["type"] == "scatterometer")
handler_id = "scatterometer_handler";
2023-05-08 23:08:34 +02:00
logger->debug("Using handler %s for instrument %s", handler_id.c_str(), products->instrument_name.c_str());
2022-04-13 17:51:30 +02:00
std::shared_ptr<ViewerHandler> handler = viewer_handlers_registry[handler_id]();
handler->products = products.get();
handler->instrument_cfg = instrument_viewer_settings;
handler->init();
2022-04-16 08:10:25 +02:00
if (dataset_name != "")
{
bool dataset_exists = false;
for (std::string dataset_name2 : opened_datasets)
2022-05-12 18:19:01 +02:00
dataset_exists = dataset_exists | (dataset_name2 == dataset_name);
2022-04-16 08:10:25 +02:00
if (!dataset_exists)
opened_datasets.push_back(dataset_name);
}
2022-04-13 17:51:30 +02:00
// Push products and handler
product_handler_mutex.lock();
2022-09-16 13:45:20 +02:00
products_and_handlers.push_back(std::make_shared<ProductsHandler>(products, handler, dataset_name));
product_handler_mutex.unlock();
2022-04-13 17:51:30 +02:00
}
2022-04-12 20:18:44 +02:00
}
2022-04-16 08:10:25 +02:00
ImRect ViewerApplication::renderHandler(ProductsHandler &ph, int index)
{
std::string label = ph.products->instrument_name;
if (ph.handler->instrument_cfg.contains("name"))
label = ph.handler->instrument_cfg["name"].get<std::string>();
ImGui::TreeNodeEx(label.c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | (index == current_handler_id ? ImGuiTreeNodeFlags_Selected : 0));
if (ImGui::IsItemClicked())
current_handler_id = index;
2022-09-13 00:19:39 +02:00
if (index == current_handler_id && ph.dataset_name == "")
{ // Closing button
ImGui::SameLine();
ImGui::Text(" ");
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 0, 0, 255));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
2022-09-13 15:06:09 -07:00
if (ImGui::SmallButton(std::string(u8"\uf00d##" + ph.dataset_name + label).c_str()))
2022-09-13 00:19:39 +02:00
{
logger->info("Closing products " + label);
2022-09-13 00:19:39 +02:00
ph.marked_for_close = true;
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
2022-04-16 08:10:25 +02:00
ImRect rect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
if (index == current_handler_id)
{
2023-10-04 21:40:08 +02:00
ImGui::TreePush(std::string("##HandlerTree" + std::to_string(current_handler_id)).c_str());
2022-09-16 13:45:20 +02:00
products_and_handlers[current_handler_id]->handler->drawTreeMenu();
2022-04-16 08:10:25 +02:00
ImGui::TreePop();
}
// ImGui::InputInt("Current Product", &current_handler_id);
return rect;
}
2022-04-12 20:18:44 +02:00
void ViewerApplication::drawPanel()
{
2022-09-07 16:20:16 +02:00
if (ImGui::BeginTabBar("Viewer Prob Tabbar", ImGuiTabBarFlags_None))
2022-04-12 20:18:44 +02:00
{
2022-09-08 17:24:54 +02:00
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() / 2);
if (ImGui::BeginTabItem("Products###productsviewertab"))
2022-04-13 17:51:30 +02:00
{
2022-09-08 17:24:54 +02:00
if (current_selected_tab != 0)
current_selected_tab = 0;
2022-09-07 22:20:48 +02:00
if (ImGui::CollapsingHeader("General", ImGuiTreeNodeFlags_DefaultOpen))
2022-04-13 17:51:30 +02:00
{
2022-09-07 16:20:16 +02:00
for (std::string dataset_name : opened_datasets)
{
if (products_cnt_in_dataset(dataset_name))
{
ImGui::TreeNodeEx(dataset_name.c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen);
2023-10-04 21:40:08 +02:00
ImGui::TreePush(std::string("##HandlerTree" + dataset_name).c_str());
2022-09-08 20:52:57 +02:00
for (int i = 0; i < (int)products_and_handlers.size(); i++)
2022-09-16 13:45:20 +02:00
if (products_and_handlers[i]->dataset_name == dataset_name && products_and_handlers[i]->handler->shouldProject())
2022-09-14 18:13:27 +02:00
{
2022-09-08 20:52:57 +02:00
SelectableColor(IM_COL32(186, 153, 38, 65));
break;
}
2022-09-14 18:13:27 +02:00
2022-09-13 00:19:39 +02:00
{ // Closing button
ImGui::SameLine();
ImGui::Text(" ");
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 0, 0, 255));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
2022-09-13 15:06:09 -07:00
if (ImGui::SmallButton(std::string(u8"\uf00d##dataset" + dataset_name).c_str()))
2022-09-13 00:19:39 +02:00
{
logger->info("Closing datset " + dataset_name);
2022-09-13 00:19:39 +02:00
for (int i = 0; i < (int)products_and_handlers.size(); i++)
2022-09-16 13:45:20 +02:00
if (products_and_handlers[i]->dataset_name == dataset_name)
{
if (products_and_handlers[i]->handler->shouldProject() && projections_are_generating)
logger->warn("%s is currently being projected and will not close",
products_and_handlers[i]->products->instrument_name.c_str());
else
products_and_handlers[i]->marked_for_close = true;
}
2022-09-13 00:19:39 +02:00
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
2022-09-08 20:52:57 +02:00
2022-09-07 16:20:16 +02:00
const ImColor TreeLineColor = ImColor(128, 128, 128, 255); // ImGui::GetColorU32(ImGuiCol_Text);
const float SmallOffsetX = 11.0f; // for now, a hardcoded value; should take into account tree indent size
ImDrawList *drawList = ImGui::GetWindowDrawList();
2022-04-16 08:10:25 +02:00
2022-09-07 16:20:16 +02:00
ImVec2 verticalLineStart = ImGui::GetCursorScreenPos();
verticalLineStart.x += SmallOffsetX; // to nicely line up with the arrow symbol
ImVec2 verticalLineEnd = verticalLineStart;
2022-04-16 08:10:25 +02:00
2022-09-07 16:20:16 +02:00
for (int i = 0; i < (int)products_and_handlers.size(); i++)
2022-09-08 20:52:57 +02:00
{
2022-09-16 13:45:20 +02:00
if (products_and_handlers[i]->dataset_name == dataset_name)
2022-09-07 16:20:16 +02:00
{
2022-09-16 13:45:20 +02:00
const float HorizontalTreeLineSize = 8.0f * ui_scale; // chosen arbitrarily
const ImRect childRect = renderHandler(*products_and_handlers[i], i); // RenderTree(child);
2022-09-07 16:20:16 +02:00
const float midpoint = (childRect.Min.y + childRect.Max.y) / 2.0f;
drawList->AddLine(ImVec2(verticalLineStart.x, midpoint), ImVec2(verticalLineStart.x + HorizontalTreeLineSize, midpoint), TreeLineColor);
verticalLineEnd.y = midpoint;
2022-09-16 13:45:20 +02:00
if (products_and_handlers[i]->handler->shouldProject())
2022-09-13 21:40:12 +02:00
SelectableColor(IM_COL32(186, 153, 38, 65));
2022-09-07 16:20:16 +02:00
}
2022-09-08 20:52:57 +02:00
}
2022-04-16 08:10:25 +02:00
2022-09-07 16:20:16 +02:00
drawList->AddLine(verticalLineStart, verticalLineEnd, TreeLineColor);
2022-04-16 08:10:25 +02:00
2022-09-07 16:20:16 +02:00
ImGui::TreePop();
}
}
2022-04-16 08:10:25 +02:00
2022-09-07 16:20:16 +02:00
// Render unclassified
if (products_cnt_in_dataset(""))
{
ImGui::TreeNodeEx("Others", ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen);
2023-10-04 21:40:08 +02:00
ImGui::TreePush("##HandlerTreeOthers");
2022-09-07 16:20:16 +02:00
for (int i = 0; i < (int)products_and_handlers.size(); i++)
2022-09-16 13:45:20 +02:00
if (products_and_handlers[i]->dataset_name == "")
renderHandler(*products_and_handlers[i], i);
2022-09-07 16:20:16 +02:00
ImGui::TreePop();
}
2022-09-13 00:19:39 +02:00
// Handle deletion if required
for (int i = 0; i < (int)products_and_handlers.size(); i++)
{
2022-09-16 13:45:20 +02:00
if (products_and_handlers[i]->marked_for_close)
2022-09-13 00:19:39 +02:00
{
product_handler_mutex.lock();
2022-09-13 00:19:39 +02:00
products_and_handlers.erase(products_and_handlers.begin() + i);
product_handler_mutex.unlock();
2022-09-13 22:54:11 +02:00
if (current_handler_id >= (int)products_and_handlers.size())
2022-09-13 00:19:39 +02:00
current_handler_id = 0;
break;
}
}
2022-09-07 16:20:16 +02:00
ImGui::Separator();
ImGui::Text("Load Dataset :");
if (select_dataset_dialog.draw())
{
ui_thread_pool.push([this](int)
{
try
{
loadDatasetInViewer(select_dataset_dialog.getPath());
}
catch (std::exception &e)
{
logger->error("Error opening dataset - %s", e.what());
} });
2022-09-07 16:20:16 +02:00
}
ImGui::Text("Load Products :");
if (select_products_dialog.draw())
{
2023-10-14 22:25:43 +02:00
ui_thread_pool.push([this](int)
{
try
{
loadProductsInViewer(select_products_dialog.getPath());
}
catch (std::exception &e)
{
logger->error("Error opening products - %s", e.what());
} });
2022-09-07 16:20:16 +02:00
}
}
2022-09-08 17:24:54 +02:00
if (products_and_handlers.size() > 0)
2022-09-16 13:45:20 +02:00
products_and_handlers[current_handler_id]->handler->drawMenu();
2022-09-08 17:24:54 +02:00
2022-09-07 16:20:16 +02:00
ImGui::EndTabItem();
}
2022-09-08 17:24:54 +02:00
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() / 2);
if (ImGui::BeginTabItem("Projections###projssviewertab"))
{
2022-09-08 17:24:54 +02:00
if (current_selected_tab != 1)
{
2022-09-08 17:24:54 +02:00
current_selected_tab = 1;
2022-09-09 15:35:48 +02:00
refreshProjectionLayers();
}
2022-09-08 17:24:54 +02:00
drawProjectionPanel();
2022-09-07 16:20:16 +02:00
ImGui::EndTabItem();
}
2022-09-07 16:20:16 +02:00
ImGui::EndTabBar();
2022-04-12 20:18:44 +02:00
}
}
void ViewerApplication::drawContent()
{
}
void ViewerApplication::drawUI()
{
ImVec2 viewer_size = ImGui::GetContentRegionAvail();
2023-01-22 20:39:51 +01:00
if (ImGui::BeginTable("##wiever_table", 2, ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchProp))
2023-01-22 19:35:16 +01:00
{
ImGui::TableSetupColumn("##panel_v", ImGuiTableColumnFlags_None, viewer_size.x * panel_ratio);
ImGui::TableSetupColumn("##view", ImGuiTableColumnFlags_None, viewer_size.x * (1.0f - panel_ratio));
2023-01-22 20:39:51 +01:00
ImGui::TableNextColumn();
float left_width = ImGui::GetColumnWidth(0);
float right_width = viewer_size.x - left_width;
if (left_width != last_width && last_width != -1)
panel_ratio = left_width / viewer_size.x;
last_width = left_width;
ImGui::BeginChild("ViewerChildPanel", {left_width, float(viewer_size.y - 10)});
drawPanel();
ImGui::EndChild();
2023-01-22 20:39:51 +01:00
ImGui::TableNextColumn();
ImGui::BeginGroup();
if (current_selected_tab == 0)
{
if (products_and_handlers.size() > 0)
products_and_handlers[current_handler_id]->handler->drawContents({float(right_width - 4), float(viewer_size.y)});
2023-01-22 20:39:51 +01:00
else
ImGui::GetWindowDrawList()
->AddRectFilled(ImGui::GetCursorScreenPos(),
ImVec2(ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x,
ImGui::GetCursorScreenPos().y + ImGui::GetContentRegionAvail().y),
ImColor::HSV(0, 0, 0));
}
else if (current_selected_tab == 1)
{
projection_image_widget.draw({float(right_width - 4), float(viewer_size.y)});
2023-01-22 20:39:51 +01:00
}
ImGui::EndGroup();
ImGui::EndTable();
2023-01-22 19:35:16 +01:00
}
2022-04-12 20:18:44 +02:00
}
std::map<std::string, std::function<std::shared_ptr<ViewerHandler>()>> viewer_handlers_registry;
void registerViewerHandlers()
{
viewer_handlers_registry.emplace(ImageViewerHandler::getID(), ImageViewerHandler::getInstance);
2022-04-16 08:10:25 +02:00
viewer_handlers_registry.emplace(RadiationViewerHandler::getID(), RadiationViewerHandler::getInstance);
2022-09-29 00:46:17 +02:00
viewer_handlers_registry.emplace(ScatterometerViewerHandler::getID(), ScatterometerViewerHandler::getInstance);
2022-04-12 20:18:44 +02:00
}
2023-09-24 10:10:21 -04:00
};