use vaster::inv_geotransform;
pub trait Transformer {
fn transform(
&self,
dst_to_src: bool,
x: &mut [f64],
y: &mut [f64],
) -> Vec<bool>;
fn jacobian(&self, dst_to_src: bool, x: f64, y: f64) -> Option<[[f64; 2]; 2]> {
let h = 1e-5_f64;
let mut xp = [x + h, x - h];
let mut yp = [y, y ];
let okx = self.transform(dst_to_src, &mut xp, &mut yp);
if !okx[0] || !okx[1] { return None; }
let dxdx = (xp[0] - xp[1]) / (2.0 * h);
let dydx = (yp[0] - yp[1]) / (2.0 * h);
let mut xq = [x, x ];
let mut yq = [y + h, y - h];
let oky = self.transform(dst_to_src, &mut xq, &mut yq);
if !oky[0] || !oky[1] { return None; }
let dxdy = (xq[0] - xq[1]) / (2.0 * h);
let dydy = (yq[0] - yq[1]) / (2.0 * h);
Some([[dxdx, dxdy], [dydx, dydy]])
}
}
pub struct GenImgProjTransformer {
src_gt: [f64; 6],
src_inv_gt: [f64; 6],
dst_gt: [f64; 6],
dst_inv_gt: [f64; 6],
proj_dst_to_src: proj::Proj,
proj_src_to_dst: proj::Proj,
}
impl GenImgProjTransformer {
pub fn new(
src_crs: &str,
src_gt: [f64; 6],
dst_crs: &str,
dst_gt: [f64; 6],
) -> Result<Self, String> {
let src_inv_gt = inv_geotransform(&src_gt)
.ok_or_else(|| "Source geotransform not invertible".to_string())?;
let dst_inv_gt = inv_geotransform(&dst_gt)
.ok_or_else(|| "Dest geotransform not invertible".to_string())?;
let proj_dst_to_src = proj::Proj::new_known_crs(dst_crs, src_crs, None)
.map_err(|e| format!("PROJ dst→src failed: {}", e))?;
let proj_src_to_dst = proj::Proj::new_known_crs(src_crs, dst_crs, None)
.map_err(|e| format!("PROJ src→dst failed: {}", e))?;
Ok(Self {
src_gt,
src_inv_gt,
dst_gt,
dst_inv_gt,
proj_dst_to_src,
proj_src_to_dst,
})
}
}
impl Transformer for GenImgProjTransformer {
fn transform(
&self,
dst_to_src: bool,
x: &mut [f64],
y: &mut [f64],
) -> Vec<bool> {
let n = x.len();
if dst_to_src {
for i in 0..n {
let pixel = x[i];
let line = y[i];
x[i] = self.dst_gt[0] + pixel * self.dst_gt[1] + line * self.dst_gt[2];
y[i] = self.dst_gt[3] + pixel * self.dst_gt[4] + line * self.dst_gt[5];
}
let mut success = vec![true; n];
for i in 0..n {
match self.proj_dst_to_src.convert((x[i], y[i])) {
Ok((rx, ry)) => {
x[i] = rx;
y[i] = ry;
}
Err(_) => {
success[i] = false;
}
}
}
for i in 0..n {
if !success[i] {
continue;
}
let geo_x = x[i];
let geo_y = y[i];
x[i] = self.src_inv_gt[0]
+ geo_x * self.src_inv_gt[1]
+ geo_y * self.src_inv_gt[2];
y[i] = self.src_inv_gt[3]
+ geo_x * self.src_inv_gt[4]
+ geo_y * self.src_inv_gt[5];
}
success
} else {
for i in 0..n {
let pixel = x[i];
let line = y[i];
x[i] = self.src_gt[0] + pixel * self.src_gt[1] + line * self.src_gt[2];
y[i] = self.src_gt[3] + pixel * self.src_gt[4] + line * self.src_gt[5];
}
let mut success = vec![true; n];
for i in 0..n {
match self.proj_src_to_dst.convert((x[i], y[i])) {
Ok((rx, ry)) => {
x[i] = rx;
y[i] = ry;
}
Err(_) => {
success[i] = false;
}
}
}
for i in 0..n {
if !success[i] {
continue;
}
let geo_x = x[i];
let geo_y = y[i];
x[i] = self.dst_inv_gt[0]
+ geo_x * self.dst_inv_gt[1]
+ geo_y * self.dst_inv_gt[2];
y[i] = self.dst_inv_gt[3]
+ geo_x * self.dst_inv_gt[4]
+ geo_y * self.dst_inv_gt[5];
}
success
}
}
}
pub fn transform_scanline(
transformer: &impl Transformer,
dst_y: usize,
dst_ncol: usize,
dst_x_off: usize,
dst_y_off: usize,
) -> (Vec<f64>, Vec<f64>, Vec<bool>) {
let mut x: Vec<f64> = (0..dst_ncol)
.map(|col| (col + dst_x_off) as f64 + 0.5)
.collect();
let mut y = vec![(dst_y + dst_y_off) as f64 + 0.5; dst_ncol];
let success = transformer.transform(true, &mut x, &mut y);
(x, y, success)
}
pub fn transform_grid(
transformer: &impl Transformer,
dst_dim: &[usize; 2],
) -> (Vec<f64>, Vec<f64>) {
let n = dst_dim[0] * dst_dim[1];
let mut all_x = Vec::with_capacity(n);
let mut all_y = Vec::with_capacity(n);
for row in 0..dst_dim[1] {
let (sx, sy, ok) = transform_scanline(transformer, row, dst_dim[0], 0, 0);
for col in 0..dst_dim[0] {
if ok[col] {
all_x.push(sx[col]);
all_y.push(sy[col]);
} else {
all_x.push(f64::NAN);
all_y.push(f64::NAN);
}
}
}
(all_x, all_y)
}