use crate::errors::{Error, Result};
use crate::log::trace;
use crate::math::consts::SEC_TO_RAD;
use crate::nadgrids::header::error_str::*;
use crate::nadgrids::header::{Endianness, Header};
use crate::nadgrids::{Catalog, Grid, GridId, Lp};
use std::io::Read;
const NTV2_HEADER_SIZE: usize = 11 * 16;
pub(super) fn read_ntv2<R: Read>(catalog: &Catalog, key: &str, read: &mut R) -> Result<()> {
let mut head = Header::<NTV2_HEADER_SIZE>::new();
trace!("Reading ntv2 {}", key);
head.read(read)?;
head.endian = if head.get_u8(8) == 11 {
Endianness::native()
} else {
Endianness::other()
};
let nsubgrids = head.get_u32(40) as usize;
trace!("Reading ntv2 {} subgrids {}", key, nsubgrids);
(0..nsubgrids).try_for_each(|_| read_ntv2_grid(catalog, key, head.read(read)?, read))
}
fn read_ntv2_grid<R: Read>(
catalog: &Catalog,
key: &str,
head: &Header<NTV2_HEADER_SIZE>,
read: &mut R,
) -> Result<()> {
match head.get_str(0, 8) {
Ok("SUB_NAME") => Ok(()),
_ => Err(Error::InvalidNtv2GridFormat(ERR_INVALID_HEADER)),
}?;
let id = head.get_id(8);
let mut lineage = head.get_id(24);
if lineage.as_str().trim() == "NONE" {
lineage = GridId::root();
}
let mut ll = Lp {
lam: -head.get_f64(120), phi: head.get_f64(72), };
let mut ur = Lp {
lam: -head.get_f64(104), phi: head.get_f64(88), };
let mut del = Lp {
lam: head.get_f64(152), phi: head.get_f64(136), };
let lim = Lp {
lam: (((ur.lam - ll.lam).abs() / del.lam + 0.5) + 1.).floor(),
phi: (((ur.phi - ll.phi).abs() / del.phi + 0.5) + 1.).floor(),
};
ll.lam *= SEC_TO_RAD;
ll.phi *= SEC_TO_RAD;
ur.lam *= SEC_TO_RAD;
ur.phi *= SEC_TO_RAD;
del.lam *= SEC_TO_RAD;
del.phi *= SEC_TO_RAD;
let nrows = lim.phi as usize;
let rowsize = lim.lam as usize;
let gs_count = head.get_u32(168) as usize;
if gs_count != nrows * rowsize {
return Err(Error::InvalidNtv2GridFormat(ERR_GSCOUNT_NOT_MATCHING));
}
trace!(
"Reading data for grid {}:{}:{}",
key,
id.as_str(),
lineage.as_str()
);
let mut buf = head.rebind::<16>();
let mut cvs: Vec<Lp> = (0..gs_count)
.map(|_| {
buf.read(read)?;
Ok(Lp {
lam: SEC_TO_RAD * (buf.get_f32(0) as f64),
phi: SEC_TO_RAD * (buf.get_f32(4) as f64),
})
})
.collect::<Result<Vec<_>>>()?;
for i in 0..nrows {
let offs = i * rowsize;
cvs[offs..(offs + rowsize)].reverse();
}
let epsilon = (del.lam.abs() + del.phi.abs()) / 10_000.;
catalog.add_grid(
key.into(),
Grid {
id,
lineage,
ll,
ur,
del,
lim,
epsilon,
cvs: cvs.into_boxed_slice(),
},
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nadgrids::Catalog;
use crate::tests::setup;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
macro_rules! fixture {
($name:expr) => {
Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
.join("fixtures")
.as_path()
.join($name)
.as_path()
};
}
macro_rules! load_ntv2 {
($cat:expr, $name:expr) => {
let file = File::open(fixture!($name)).unwrap();
let mut read = BufReader::new(file);
read_ntv2($cat, $name, &mut read).unwrap();
};
}
#[test]
fn ntv2_100800401_gsb() {
setup();
let catalog = Catalog::default();
load_ntv2!(&catalog, "100800401.gsb");
let grids = catalog.find("100800401.gsb").unwrap().collect::<Vec<_>>();
assert_eq!(grids.len(), 1);
let grid = grids[0];
assert!(grid.is_root());
assert_eq!(grid.id.as_str(), "0INT2GRS");
assert_eq!(grid.cvs.len(), 1591);
}
#[test]
#[cfg(feature = "local_tests")]
fn ntv2_bwta2017_gsb() {
setup();
let catalog = Catalog::default();
load_ntv2!(&catalog, "BWTA2017.gsb");
let grids = catalog.find("BWTA2017.gsb").unwrap().collect::<Vec<_>>();
assert_eq!(grids.len(), 1);
let grid = grids[0];
assert!(grid.is_root());
assert_eq!(grid.id.as_str(), "DHDN90 ");
assert_eq!(grid.cvs.len(), 24514459);
}
}