mirror of
https://github.com/SatDump/SatDump
synced 2026-08-13 17:47:30 -04:00
307 lines
No EOL
13 KiB
C++
307 lines
No EOL
13 KiB
C++
#include "viewer.h"
|
|
#include "error.h"
|
|
#include "common/map/map_drawer.h"
|
|
#include "common/projection/reprojector.h"
|
|
#include "logger.h"
|
|
#include "resources.h"
|
|
#include "core/style.h"
|
|
#include "main_ui.h"
|
|
#include "common/image/image_utils.h"
|
|
|
|
namespace satdump
|
|
{
|
|
void ViewerApplication::drawProjectionPanel()
|
|
{
|
|
if (ImGui::CollapsingHeader("Projection"))
|
|
{
|
|
ImGui::Combo("##targetproj", &projections_current_selected_proj, "Equirectangular\0"
|
|
"Mercator\0"
|
|
"Stereo\0"
|
|
"Satellite\0");
|
|
ImGui::InputInt("Image Width", &projections_image_width);
|
|
ImGui::InputInt("Image Height", &projections_image_height);
|
|
|
|
if (projections_current_selected_proj == 0)
|
|
{
|
|
ImGui::Text("Top Left :");
|
|
ImGui::InputFloat("Lon##tl", &projections_equirectangular_tl_lon);
|
|
ImGui::InputFloat("Lat##tl", &projections_equirectangular_tl_lat);
|
|
ImGui::Text("Bottom Right :");
|
|
ImGui::InputFloat("Lon##br", &projections_equirectangular_br_lon);
|
|
ImGui::InputFloat("Lat##br", &projections_equirectangular_br_lat);
|
|
}
|
|
else if (projections_current_selected_proj == 1)
|
|
{
|
|
}
|
|
else if (projections_current_selected_proj == 2)
|
|
{
|
|
ImGui::Text("Center :");
|
|
ImGui::InputFloat("Lon##stereo", &projections_stereo_center_lon);
|
|
ImGui::InputFloat("Lat##stereo", &projections_stereo_center_lat);
|
|
ImGui::InputFloat("Scale##stereo", &projections_stereo_scale);
|
|
}
|
|
else if (projections_current_selected_proj == 3)
|
|
{
|
|
ImGui::Text("Center :");
|
|
ImGui::InputFloat("Lon##tpers", &projections_tpers_lon);
|
|
ImGui::InputFloat("Lat##tpers", &projections_tpers_lat);
|
|
ImGui::InputFloat("Alt (kM)##tpers", &projections_tpers_alt);
|
|
ImGui::InputFloat("Angle##tpers", &projections_tpers_ang);
|
|
ImGui::InputFloat("Azimuth##tpers", &projections_tpers_azi);
|
|
}
|
|
|
|
if (ImGui::Button("Save"))
|
|
{
|
|
std::string default_name = "projection.png";
|
|
|
|
#ifndef __ANDROID__
|
|
auto result = pfd::save_file("Save Projection", default_name, {"*.png"});
|
|
while (!result.ready(1000))
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
if (result.result().size() > 0)
|
|
{
|
|
std::string path = result.result();
|
|
logger->info("Saving current projection at {:s}", path.c_str());
|
|
projected_image_result.save_png(path);
|
|
}
|
|
#else
|
|
std::string path = "/storage/emulated/0/" + default_name;
|
|
logger->info("Saving current projection at {:s}", path.c_str());
|
|
projected_image_result.save_png("" + path);
|
|
#endif
|
|
}
|
|
}
|
|
if (ImGui::CollapsingHeader("Layers"))
|
|
{
|
|
ImGui::Text("Mode :");
|
|
ImGui::RadioButton("Blend", &projections_mode_radio, 0);
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton("Overlay", &projections_mode_radio, 1);
|
|
|
|
ImGui::Separator(); //////////////////////////////////////////////////////
|
|
|
|
ImGui::RadioButton("Equirectangular", &selected_external_type, 0);
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton("Other", &selected_external_type, 1);
|
|
|
|
ImGui::InputText("Name", &projection_new_layer_name);
|
|
projection_new_layer_file.draw();
|
|
if (selected_external_type == 1)
|
|
projection_new_layer_cfg.draw();
|
|
|
|
if (ImGui::Button("Add layer") && projection_new_layer_file.file_valid)
|
|
{
|
|
// TMP
|
|
ExternalProjSource new_layer_cfg;
|
|
new_layer_cfg.name = projection_new_layer_name;
|
|
if (selected_external_type == 1)
|
|
new_layer_cfg.path = projection_new_layer_cfg.getPath();
|
|
else
|
|
new_layer_cfg.cfg = nlohmann::json::parse("{\"type\":\"equirectangular\",\"tl_lon\":-180,\"tl_lat\":90,\"br_lon\":180,\"br_lat\":-90}");
|
|
new_layer_cfg.img.load_png(projection_new_layer_file.getPath());
|
|
projections_external_sources.push_back(new_layer_cfg);
|
|
|
|
refreshProjectionLayers();
|
|
}
|
|
|
|
ImGui::Separator(); ///////////////////////////////////////////////////
|
|
|
|
ImGui::Text("Layers :");
|
|
|
|
bool select = false;
|
|
if (ImGui::BeginListBox("##pipelineslistbox"))
|
|
{
|
|
for (int i = 0; i < (int)projection_layers.size(); i++)
|
|
{
|
|
ProjectionLayer &layer = projection_layers[i];
|
|
ImGui::Selectable(layer.name.c_str(), &select);
|
|
ImGui::DragFloat(std::string("Opacity##opacitylayer" + layer.name + std::to_string(i)).c_str(), &layer.opacity, 1.0, 0, 100);
|
|
ImGui::Checkbox(std::string("Show##enablelayer" + layer.name + std::to_string(i)).c_str(), &layer.enabled);
|
|
ImGui::ProgressBar(layer.progress);
|
|
if (layer.type == 1)
|
|
{
|
|
if (ImGui::Button("Delete"))
|
|
{
|
|
projection_layers.erase(projection_layers.begin() + i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ImGui::EndListBox();
|
|
}
|
|
|
|
if (projections_are_generating || projection_layers.size() == 0)
|
|
style::beginDisabled();
|
|
if (ImGui::Button("GENERATE"))
|
|
{
|
|
ui_thread_pool.push([this](int)
|
|
{
|
|
logger->info("Update projection...");
|
|
generateProjectionImage();
|
|
logger->info("Done"); });
|
|
}
|
|
if (projections_are_generating || projection_layers.size() == 0)
|
|
style::endDisabled();
|
|
}
|
|
if (ImGui::CollapsingHeader("Overlay##viewerpojoverlay"))
|
|
{
|
|
ImGui::Checkbox("Map Overlay##Projs", &projections_draw_map_overlay);
|
|
ImGui::Checkbox("Cities Overlay##Projs", &projections_draw_cities_overlay);
|
|
ImGui::InputFloat("Cities Scale##Projs", &projections_cities_scale);
|
|
}
|
|
}
|
|
|
|
void ViewerApplication::generateProjectionImage()
|
|
{
|
|
projections_are_generating = true;
|
|
|
|
nlohmann::json cfg; //= nlohmann::json::parse("{\"type\":\"equirectangular\",\"tl_lon\":-180,\"tl_lat\":90,\"br_lon\":180,\"br_lat\":-90}");
|
|
|
|
if (projections_current_selected_proj == 0)
|
|
{
|
|
cfg["type"] = "equirectangular";
|
|
cfg["tl_lon"] = projections_equirectangular_tl_lon;
|
|
cfg["tl_lat"] = projections_equirectangular_tl_lat;
|
|
cfg["br_lon"] = projections_equirectangular_br_lon;
|
|
cfg["br_lat"] = projections_equirectangular_br_lat;
|
|
}
|
|
else if (projections_current_selected_proj == 1)
|
|
{
|
|
cfg["type"] = "mercator";
|
|
// cfg["tl_lon"] = projections_equirectangular_tl_lon;
|
|
// cfg["tl_lat"] = projections_equirectangular_tl_lat;
|
|
// cfg["br_lon"] = projections_equirectangular_br_lon;
|
|
// cfg["br_lat"] = projections_equirectangular_br_lat;
|
|
}
|
|
else if (projections_current_selected_proj == 2)
|
|
{
|
|
cfg["type"] = "stereo";
|
|
cfg["center_lon"] = projections_stereo_center_lon;
|
|
cfg["center_lat"] = projections_stereo_center_lat;
|
|
cfg["scale"] = projections_stereo_scale;
|
|
}
|
|
else if (projections_current_selected_proj == 3)
|
|
{
|
|
cfg["type"] = "tpers";
|
|
cfg["lon"] = projections_tpers_lon;
|
|
cfg["lat"] = projections_tpers_lat;
|
|
cfg["alt"] = projections_tpers_alt;
|
|
cfg["ang"] = projections_tpers_ang;
|
|
cfg["azi"] = projections_tpers_azi;
|
|
}
|
|
|
|
// Setup final image
|
|
projected_image_result.init(projections_image_width, projections_image_height, 3);
|
|
|
|
// Generate all layers
|
|
std::vector<image::Image<uint16_t>> layers_images;
|
|
for (ProjectionLayer &layer : projection_layers)
|
|
{
|
|
if (!layer.enabled)
|
|
continue;
|
|
|
|
if (layer.type == 0)
|
|
{
|
|
layer.viewer_prods->handler->updateProjection(projections_image_width, projections_image_height, cfg, &layer.progress);
|
|
layer.viewer_prods->handler->getProjection().to_rgb();
|
|
layers_images.push_back(layer.viewer_prods->handler->getProjection());
|
|
}
|
|
else
|
|
{
|
|
auto img = projectExternal(projections_image_width, projections_image_height, cfg, *layer.external, &layer.progress);
|
|
img.to_rgb();
|
|
layers_images.push_back(img);
|
|
}
|
|
}
|
|
|
|
logger->info("Combining images...");
|
|
if (projections_mode_radio == 0) // Blend
|
|
{
|
|
projected_image_result = layers_images[0];
|
|
for (int i = 1; i < (int)layers_images.size(); i++)
|
|
projected_image_result = image::blend_images(projected_image_result, layers_images[i]);
|
|
}
|
|
else if (projections_mode_radio == 1)
|
|
{
|
|
projected_image_result = layers_images[0];
|
|
for (int i = 1; i < (int)layers_images.size(); i++)
|
|
projected_image_result = image::merge_images_opacity(projected_image_result,
|
|
layers_images[i],
|
|
projection_layers[i].opacity / 100.0f);
|
|
}
|
|
else
|
|
{
|
|
// This sucks but temporary
|
|
for (auto &img : layers_images)
|
|
projected_image_result.draw_image(0, img);
|
|
}
|
|
|
|
// Free up memory
|
|
layers_images.clear();
|
|
|
|
// Setup projection to draw stuff on top
|
|
auto proj_func = satdump::reprojection::setupProjectionFunction(projections_image_width, projections_image_height, cfg);
|
|
|
|
// Draw map borders
|
|
if (projections_draw_map_overlay)
|
|
{
|
|
logger->info("Drawing map overlay...");
|
|
unsigned short color[3] = {0, 65535, 0};
|
|
map::drawProjectedMapShapefile({resources::getResourcePath("maps/ne_10m_admin_0_countries.shp")},
|
|
projected_image_result,
|
|
color,
|
|
proj_func);
|
|
}
|
|
|
|
// Draw cities points
|
|
if (projections_draw_cities_overlay)
|
|
{
|
|
logger->info("Drawing map overlay...");
|
|
unsigned short color[3] = {65535, 0, 0};
|
|
map::drawProjectedCapitalsGeoJson({resources::getResourcePath("maps/ne_10m_populated_places_simple.json")},
|
|
projected_image_result,
|
|
color,
|
|
proj_func,
|
|
projections_cities_scale);
|
|
}
|
|
|
|
// Update ImageView
|
|
projection_image_widget.update(projected_image_result);
|
|
|
|
projections_are_generating = false;
|
|
}
|
|
|
|
image::Image<uint16_t> ViewerApplication::projectExternal(int width, int height, nlohmann::json tcfg, ExternalProjSource &ep, float *progress)
|
|
{
|
|
reprojection::ReprojectionOperation op;
|
|
if (ep.path.size() > 0)
|
|
op.source_prj_info = loadJsonFile(ep.path);
|
|
else
|
|
op.source_prj_info = ep.cfg;
|
|
op.target_prj_info = tcfg;
|
|
op.img = ep.img;
|
|
op.output_width = width;
|
|
op.output_height = height;
|
|
op.use_draw_algorithm = false;
|
|
reprojection::ProjectionResult res = reprojection::reproject(op, progress);
|
|
return res.img;
|
|
}
|
|
|
|
void ViewerApplication::refreshProjectionLayers()
|
|
{
|
|
projection_layers.clear();
|
|
|
|
for (ExternalProjSource &prjext : projections_external_sources)
|
|
projection_layers.push_back({prjext.name, 1, nullptr, &prjext});
|
|
|
|
for (int i = 0; i < (int)products_and_handlers.size(); i++)
|
|
{
|
|
if (products_and_handlers[i].handler->canBeProjected() && products_and_handlers[i].handler->shouldProject())
|
|
{
|
|
std::string label = products_and_handlers[i].products->instrument_name;
|
|
projection_layers.push_back({label, 0, &products_and_handlers[i], nullptr});
|
|
}
|
|
}
|
|
}
|
|
} |