mirror of
https://github.com/SatDump/SatDump
synced 2026-08-13 17:47:30 -04:00
185 lines
No EOL
6.7 KiB
C++
185 lines
No EOL
6.7 KiB
C++
#include "projection.h"
|
|
#include "logger.h"
|
|
|
|
#include "common/geodetic/vincentys_calculations.h" // TODOREWORK MOVE OUT
|
|
#include "projection/standard/proj_json.h"
|
|
#include "raytrace/gcp_compute.h"
|
|
|
|
namespace satdump
|
|
{
|
|
namespace projection
|
|
{
|
|
Projection::Projection() {}
|
|
|
|
Projection::~Projection()
|
|
{
|
|
if (fwd_type == PROJ_STANDARD || inv_type == PROJ_STANDARD)
|
|
::proj::projection_free(&std_proj);
|
|
}
|
|
|
|
bool Projection::init(bool fwd, bool inv)
|
|
{
|
|
///////////////////////////////////////////////////////////
|
|
// We need image width/height
|
|
///////////////////////////////////////////////////////////
|
|
if (d_cfg.contains("width"))
|
|
width = d_cfg["width"];
|
|
else
|
|
throw satdump_exception("Image width must be present!");
|
|
|
|
if (d_cfg.contains("height"))
|
|
height = d_cfg["height"];
|
|
else
|
|
throw satdump_exception("Image height must be present!");
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Get channel pre-transform, if present
|
|
///////////////////////////////////////////////////////////
|
|
if (d_cfg.contains("transform"))
|
|
transform = d_cfg["transform"];
|
|
else
|
|
transform.init_none();
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Get optional second channel pre-transform, if present
|
|
///////////////////////////////////////////////////////////
|
|
if (d_cfg.contains("transform2"))
|
|
{
|
|
has_2nd_transform = true;
|
|
transform2 = d_cfg["transform2"];
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Attempt to setup a standard projection first
|
|
///////////////////////////////////////////////////////////
|
|
try
|
|
{
|
|
std_proj = d_cfg;
|
|
|
|
if (!::proj::projection_setup(&std_proj))
|
|
{
|
|
fwd_type = inv_type = PROJ_STANDARD;
|
|
if (d_cfg.contains("proj_timestamp"))
|
|
{
|
|
// logger->warn("Using projection timestamps for timestamp feedback. May be inacurate!"); // Disabled usually as it triggers... Everytime the proj is init
|
|
proj_timestamp = d_cfg["proj_timestamp"];
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
catch (std::exception &)
|
|
{
|
|
logger->trace("Not a standard projection!");
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// If this didn't work, we can attempt a raytraced proj
|
|
///////////////////////////////////////////////////////////
|
|
try
|
|
{
|
|
raytracer = get_satellite_raytracer(d_cfg);
|
|
if (raytracer)
|
|
{
|
|
fwd_type = PROJ_INVALID;
|
|
inv_type = PROJ_RAYTRACER;
|
|
|
|
if (fwd)
|
|
{
|
|
logger->critical("Forward on raytrace is imperfect!");
|
|
auto gcps = compute_gcps(d_cfg);
|
|
tps_fwd = std::make_shared<satdump::proj::LatLonTpsProjHelper>(gcps, 1, 0);
|
|
fwd_type = PROJ_THINPLATESPLINE;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
logger->trace("Not a raytraced projection! : %s", e.what());
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// And finally, the special case of simple GCPs
|
|
///////////////////////////////////////////////////////////
|
|
if (d_cfg["type"] == "normal_gcps")
|
|
logger->critical("GCPs ALONE SUPPORT TBD!");
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Projection::forward(geodetic::geodetic_coords_t pos, double &x, double &y, bool except)
|
|
{
|
|
if (fwd_type == PROJ_STANDARD)
|
|
{
|
|
pos.toDegs(); // TODOREWORK?
|
|
if (::proj::projection_perform_fwd(&std_proj, pos.lon, pos.lat, &x, &y))
|
|
return 1;
|
|
}
|
|
else if (fwd_type == PROJ_THINPLATESPLINE)
|
|
{
|
|
// Perform TPS
|
|
tps_fwd->forward(pos, x, y);
|
|
return 0; // We do NOT want to run the ChannelTransform in reverse, TPS takes care of it already!
|
|
}
|
|
else
|
|
{
|
|
if (except)
|
|
throw satdump_exception("Invalid forward projection type! " + d_cfg["type"].get<std::string>());
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
// Run channel transform, might do nothing if no transform is needed
|
|
transform.reverse(&x, &y);
|
|
if (has_2nd_transform)
|
|
transform2.reverse(&x, &y);
|
|
return 0;
|
|
}
|
|
|
|
bool Projection::inverse(double x, double y, geodetic::geodetic_coords_t &pos, double *otime, bool except)
|
|
{
|
|
// Run channel transform, might do nothing if no transform is needed
|
|
if (has_2nd_transform)
|
|
transform2.forward(&x, &y);
|
|
transform.forward(&x, &y);
|
|
|
|
if (inv_type == PROJ_STANDARD)
|
|
{
|
|
pos.toDegs(); // TODOREWORK?
|
|
if (otime != nullptr)
|
|
*otime = proj_timestamp;
|
|
return ::proj::projection_perform_inv(&std_proj, x, y, &pos.lon, &pos.lat);
|
|
}
|
|
else if (inv_type == PROJ_RAYTRACER)
|
|
{
|
|
return raytracer->get_position(x, y, pos, otime);
|
|
}
|
|
else
|
|
{
|
|
if (except)
|
|
throw satdump_exception("Invalid inverse projection type! " + d_cfg["type"].get<std::string>());
|
|
else
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
void Projection::to_json(nlohmann::json &j) const
|
|
{
|
|
j = d_cfg;
|
|
if (height != -1)
|
|
j["height"] = height;
|
|
if (width != -1)
|
|
j["width"] = width;
|
|
}
|
|
|
|
void Projection::from_json(const nlohmann::json &j)
|
|
{
|
|
d_cfg = j; // TODOREWORK de-init?
|
|
height = j.contains("height") ? j["height"].get<int>() : -1;
|
|
width = j.contains("width") ? j["width"].get<int>() : -1;
|
|
fwd_type = PROJ_INVALID;
|
|
inv_type = PROJ_INVALID;
|
|
init(0, 0);
|
|
}
|
|
} // namespace projection
|
|
} // namespace satdump
|