proj4rs/nadgrids/files/
ntv2.rs1use crate::errors::{Error, Result};
5use crate::log::trace;
6use crate::math::consts::SEC_TO_RAD;
7use crate::nadgrids::grid::{REL_TOLERANCE_HGRIDSHIFT, Grid, GridId, Lp};
8use crate::nadgrids::header::error_str::*;
9use crate::nadgrids::header::{Endianness, Header};
10use crate::nadgrids::Catalog;
11use std::io::Read;
12
13const NTV2_HEADER_SIZE: usize = 11 * 16;
14
15pub(super) fn read_ntv2<R: Read>(catalog: &Catalog, key: &str, read: &mut R) -> Result<()> {
17 let mut head = Header::<NTV2_HEADER_SIZE>::new();
18
19 trace!("Reading ntv2 {}", key);
20
21 head.read(read)?;
23 head.endian = if head.get_u8(8) == 11 {
25 Endianness::native()
26 } else {
27 Endianness::other()
28 };
29
30 let nsubgrids = head.get_u32(40) as usize;
31
32 trace!("Reading ntv2 {} subgrids {}", key, nsubgrids);
33
34 (0..nsubgrids).try_for_each(|_| read_ntv2_grid(catalog, key, head.read(read)?, read))
36}
37
38fn read_ntv2_grid<R: Read>(
40 catalog: &Catalog,
41 key: &str,
42 head: &Header<NTV2_HEADER_SIZE>,
43 read: &mut R,
44) -> Result<()> {
45 match head.get_str(0, 8) {
46 Ok("SUB_NAME") => Ok(()),
47 _ => Err(Error::InvalidNtv2GridFormat(ERR_INVALID_HEADER)),
48 }?;
49
50 let id = head.get_id(8);
51 let mut lineage = head.get_id(24);
52 if lineage.as_str().trim() == "NONE" {
53 lineage = GridId::root();
54 }
55
56 let mut ll = Lp {
57 lam: -head.get_f64(120), phi: head.get_f64(72), };
60
61 let mut ur = Lp {
62 lam: -head.get_f64(104), phi: head.get_f64(88), };
65
66 let mut del = Lp {
67 lam: head.get_f64(152), phi: head.get_f64(136), };
70
71 let lim = Lp {
72 lam: (((ur.lam - ll.lam).abs() / del.lam + 0.5) + 1.).floor(),
73 phi: (((ur.phi - ll.phi).abs() / del.phi + 0.5) + 1.).floor(),
74 };
75
76 ll.lam *= SEC_TO_RAD;
78 ll.phi *= SEC_TO_RAD;
79 ur.lam *= SEC_TO_RAD;
80 ur.phi *= SEC_TO_RAD;
81 del.lam *= SEC_TO_RAD;
82 del.phi *= SEC_TO_RAD;
83
84 let nrows = lim.phi as usize;
86 let rowsize = lim.lam as usize;
87
88 let gs_count = head.get_u32(168) as usize;
89 if gs_count != nrows * rowsize {
90 return Err(Error::InvalidNtv2GridFormat(ERR_GSCOUNT_NOT_MATCHING));
91 }
92
93 trace!(
95 "Reading data for grid {}:{}:{}",
96 key,
97 id.as_str(),
98 lineage.as_str()
99 );
100
101 let mut buf = head.rebind::<16>();
102 let mut cvs: Vec<Lp> = (0..gs_count)
103 .map(|_| {
104 buf.read(read)?;
105 Ok(Lp {
107 phi: SEC_TO_RAD * (buf.get_f32(0) as f64),
108 lam: SEC_TO_RAD * (buf.get_f32(4) as f64) * -1.0, })
110 })
111 .collect::<Result<Vec<_>>>()?;
112
113 for i in 0..nrows {
117 let offs = i * rowsize;
118 cvs[offs..(offs + rowsize)].reverse();
119 }
120
121 let epsilon = (del.lam.abs() + del.phi.abs()) * REL_TOLERANCE_HGRIDSHIFT;
122
123 catalog.add_grid(
124 key.into(),
125 Grid {
126 id,
127 lineage,
128 ll,
129 ur,
130 del,
131 lim,
132 epsilon,
133 cvs: cvs.into_boxed_slice(),
134 },
135 )
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::nadgrids::Catalog;
142 use crate::tests::setup;
143 use std::env;
144 use std::fs::File;
145 use std::io::BufReader;
146 use std::path::Path;
147
148 macro_rules! fixture {
149 ($name:expr) => {
150 Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
151 .join("fixtures")
152 .as_path()
153 .join($name)
154 .as_path()
155 };
156 }
157
158 macro_rules! load_ntv2 {
159 ($cat:expr, $name:expr) => {
160 let file = File::open(fixture!($name)).unwrap();
162 let mut read = BufReader::new(file);
163 read_ntv2($cat, $name, &mut read).unwrap();
164 };
165 }
166
167 #[test]
168 fn ntv2_100800401_gsb() {
169 setup();
170
171 let catalog = Catalog::default();
172 load_ntv2!(&catalog, "100800401.gsb");
173
174 let grids = catalog.find("100800401.gsb").unwrap().collect::<Vec<_>>();
175 assert_eq!(grids.len(), 1);
176
177 let grid = grids[0];
178 assert!(grid.is_root());
179 assert_eq!(grid.id.as_str(), "0INT2GRS");
180 assert_eq!(grid.cvs.len(), 1591);
181 }
182
183 #[test]
184 #[cfg(feature = "local_tests")]
185 fn ntv2_bwta2017_gsb() {
186 setup();
187
188 let catalog = Catalog::default();
189 load_ntv2!(&catalog, "BWTA2017.gsb");
190
191 let grids = catalog.find("BWTA2017.gsb").unwrap().collect::<Vec<_>>();
192 assert_eq!(grids.len(), 1);
193
194 let grid = grids[0];
195 assert!(grid.is_root());
196 assert_eq!(grid.id.as_str(), "DHDN90 ");
197 assert_eq!(grid.cvs.len(), 24514459);
198 }
199}