use std::io::Read;
use thisenum::Const;
use crate::parsers;
use crate::primitives::{self, Angle, AxisElement};
use crate::Error as DTEDError;
pub const DT2_UHL_LENGTH: u64 = 80;
pub const DT2_DSI_RECORD_LENGTH: usize = 648;
pub const DT2_ACC_RECORD_LENGTH: usize = 2700;
#[derive(Const)]
#[armtype(&[u8])]
pub enum RecognitionSentinel {
#[value = b"UHL1"] UHL,
#[value = b"DSIU"] DSI,
#[value = b"ACC"] ACC,
#[value = b"\xAA"] DATA,
#[value = b"NA"] NA,
}
#[derive(Debug, PartialEq)]
pub struct RawDTEDHeader {
pub origin: AxisElement<Angle>,
pub interval_secs_x_10: AxisElement<u16>,
pub accuracy: Option<u16>,
pub count: AxisElement<u16>,
}
#[derive(Clone)]
pub struct DTEDMetadata {
pub filename: String,
pub origin: AxisElement<f64>,
pub origin_angle: AxisElement<Angle>,
pub interval: AxisElement<f64>,
pub interval_secs: AxisElement<f32>,
pub accuracy: Option<u16>,
pub count: AxisElement<u16>,
}
impl DTEDMetadata {
pub fn from_header(raw: &RawDTEDHeader, fname: &str) -> DTEDMetadata {
DTEDMetadata {
filename: fname.to_string(),
origin: raw.origin.into(),
origin_angle: raw.origin,
interval: raw.interval_secs_x_10 / (primitives::SEC2DEG * 10.0),
interval_secs: raw.interval_secs_x_10 / 10.0,
accuracy: raw.accuracy,
count: raw.count,
}
}
}
pub struct DTEDData {
pub metadata: DTEDMetadata,
pub min: AxisElement<f64>,
pub max: AxisElement<f64>,
pub data: Vec<RawDTEDRecord>,
}
impl DTEDData {
pub fn read(path: &str) -> Result<DTEDData, DTEDError> {
let mut file = std::fs::File::open(path)?;
let mut content = Vec::new();
file.read_to_end(&mut content)?;
match parsers::dted_file_parser(&content) {
Ok((_, data)) => {
let metadata = DTEDMetadata::from_header(&data.header, path);
let interval = metadata.interval;
let origin_f64: AxisElement<f64> = data.header.origin.into();
Ok(DTEDData {
metadata,
min: origin_f64,
max: origin_f64 + ((data.header.count - 1) * interval),
data: data.data,
})
}
Err(e) => match e {
nom::Err::Incomplete(e) => Err(e.into()),
nom::Err::Error(e) | nom::Err::Failure(e) => Err(e.code.into()),
},
}
}
pub fn read_header(path: &str) -> Result<DTEDMetadata, DTEDError> {
let mut file = std::fs::File::open(path)?;
let mut content = Vec::new();
file.read_to_end(&mut content)?;
match parsers::dted_uhl_parser(&content) {
Ok((_, header)) => Ok(DTEDMetadata::from_header(&header, path)),
Err(e) => match e {
nom::Err::Incomplete(e) => Err(e.into()),
nom::Err::Error(e) | nom::Err::Failure(e) => Err(e.code.into()),
},
}
}
pub fn get_elevation<T: Into<f64>, U: Into<f64>>(&self, lat: T, lon: U) -> Option<f64> {
let (lat_idx, lon_idx) = self.get_indices(lat, lon)?;
let mut lat_int = lat_idx as usize;
let mut lon_int = lon_idx as usize;
let mut lat_frac = lat_idx - lat_int as f64;
let mut lon_frac = lon_idx - lon_int as f64;
if lat_int == self.metadata.count.lat as usize - 1 {
lat_int -= 1;
lat_frac += 1.0;
}
if lon_int == self.metadata.count.lon as usize - 1 {
lon_int -= 1;
lon_frac += 1.0;
}
let elev00 = self.data[lon_int].elevations[lat_int] as f64;
let elev01 = self.data[lon_int].elevations[lat_int + 1] as f64;
let elev10 = self.data[lon_int + 1].elevations[lat_int] as f64;
let elev11 = self.data[lon_int + 1].elevations[lat_int + 1] as f64;
let result = 0.0
+ elev00 * (1.0 - lon_frac) * (1.0 - lat_frac)
+ elev01 * (1.0 - lon_frac) * lat_frac
+ elev10 * lon_frac * (1.0 - lat_frac)
+ elev11 * lon_frac * lat_frac;
Some(result)
}
pub fn get_indices<T: Into<f64>, U: Into<f64>>(&self, lat: T, lon: U) -> Option<(f64, f64)> {
let lat: f64 = lat.into();
let lon: f64 = lon.into();
if lat < self.min.lat || lat > self.max.lat || lon < self.min.lon || lon > self.max.lon {
return None;
}
let lat_idx = (lat - self.min.lat) / self.metadata.interval.lat;
let lon_idx = (lon - self.min.lon) / self.metadata.interval.lon;
Some((lat_idx, lon_idx))
}
}
pub struct DTEDRecordDSI {
pub security_release: Option<String>,
pub security_handling: Option<String>,
pub version: String,
pub edition: u8,
pub mm_version: char,
pub maintenance_data: u16,
pub mm_date: u16,
pub maintenance_code: u16,
pub product_specs_desc: String,
pub product_specs_code: u8,
pub product_specs_date: u16,
pub compilation_date: u16,
pub lat_origin: Angle,
pub lon_origin: Angle,
pub lat_sw: Angle,
pub lon_sw: Angle,
pub lat_nw: Angle,
pub lon_nw: Angle,
pub lat_ne: Angle,
pub lon_ne: Angle,
pub lat_se: Angle,
pub lon_se: Angle,
pub clockwise_orientation: u32,
pub lat_interval_s: u16,
pub lon_interval_s: u16,
pub lat_count: u16,
pub lon_count: u16,
pub partial_cell_flag: f64,
pub coverage: f64,
}
pub struct DTEDRecordACC {}
pub struct RawDTEDFile {
pub header: RawDTEDHeader,
pub data: Vec<RawDTEDRecord>,
pub dsi_record: Option<u8>,
pub acc_record: Option<u8>,
}
pub struct RawDTEDRecord {
pub blk_count: u32,
pub lon_count: u16,
pub lat_count: u16,
pub elevations: Vec<i16>,
}