use crate::authoring::*;
fn fwd(op: &Op, ctx: &dyn Context, operands: &mut dyn CoordinateSet) -> usize {
let grids = &op.params.grids;
let ellps = op.params.ellps(0);
let ctx = Some(ctx);
let mut successes = 0_usize;
let n = operands.len();
if grids.is_empty() {
return n;
}
for i in 0..n {
let mut coord = operands.get_coord(i);
let lat = coord[0].to_radians();
let lon = coord[1].to_radians();
coord[0] = lon;
coord[1] = lat;
let lat_dist = ellps.meridian_latitude_to_distance(lat);
let dlat = ellps.meridian_distance_to_latitude(lat_dist + 1.0) - lat;
let dlon = (lat.cos() * ellps.prime_vertical_radius_of_curvature(lat)).recip();
let Some(origin) = grids_at(ctx, grids, coord, false) else {
operands.set_coord(i, &Coor4D::nan());
continue;
};
coord[1] += dlat;
let Some(lat_1) = grids_at(ctx, grids, coord, false) else {
operands.set_coord(i, &Coor4D::nan());
continue;
};
coord[1] = lat;
coord[0] += dlon;
let Some(lon_1) = grids_at(ctx, grids, coord, false) else {
operands.set_coord(i, &Coor4D::nan());
continue;
};
coord[0] = (lat_1[0] - origin[0]).atan2(1.0); coord[1] = (lon_1[0] - origin[0]).atan2(1.0); operands.set_coord(i, &coord.to_arcsec());
successes += 1;
}
successes
}
#[rustfmt::skip]
pub const GAMUT: [OpParameter; 2] = [
OpParameter::Texts { key: "grids", default: None },
OpParameter::Text { key: "ellps", default: Some("GRS80") }
];
pub fn new(parameters: &RawParameters, ctx: &dyn Context) -> Result<Op, Error> {
let def = ¶meters.instantiated_as;
let mut params = ParsedParameters::new(parameters, &GAMUT)?;
for mut grid_name in params.texts("grids")?.clone() {
let optional = grid_name.starts_with('@');
if optional {
grid_name = grid_name.trim_start_matches('@').to_string();
}
if grid_name == "null" {
params.boolean.insert("null_grid");
break; }
match ctx.get_grid(&grid_name) {
Ok(grid) => params.grids.push(grid),
Err(e) => {
if !optional {
return Err(e);
}
}
}
}
let fwd = InnerOp(fwd);
let descriptor = OpDescriptor::new(def, fwd, None);
Ok(Op {
descriptor,
params,
steps: None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deflection() -> Result<(), Error> {
let mut ctx = Plain::default();
let op = ctx.op("deflection grids=test.geoid")?;
let cph = Coor4D::raw(55., 12., 0., 0.);
let mut data = [cph];
ctx.apply(op, Fwd, &mut data)?;
assert!((data[0][0] - 1.8527755901425906).abs() < 1e-6);
assert!((data[0][1] - 0.032238719594433175).abs() < 1e-6);
Ok(())
}
}