#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(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()); 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()); 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() : -1; width = j.contains("width") ? j["width"].get() : -1; fwd_type = PROJ_INVALID; inv_type = PROJ_INVALID; init(0, 0); } } // namespace projection } // namespace satdump