use crate::mesh::MeshUnit;
use crate::{Format, ParData, ParseParError};
use std::hash::RandomState;
fn ksum(vs: &[f64]) -> f64 {
let mut sum = 0.0;
let mut c = 0.0;
for v in vs {
let t = sum + *v;
c += if sum.ge(v) {
(sum - t) + v
} else {
(v - t) + sum
};
sum = t
}
sum + c
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Parameter {
pub latitude: f64,
pub longitude: f64,
pub altitude: f64,
}
impl From<(f64, f64, f64)> for Parameter {
#[inline]
fn from(value: (f64, f64, f64)) -> Self {
Self {
latitude: value.0,
longitude: value.1,
altitude: value.2,
}
}
}
impl From<[f64; 3]> for Parameter {
#[inline]
fn from(value: [f64; 3]) -> Self {
Self {
latitude: value[0],
longitude: value[1],
altitude: value[2],
}
}
}
impl Parameter {
#[inline]
#[must_use]
pub const fn new(latitude: f64, longitude: f64, altitude: f64) -> Self {
Self {
latitude,
longitude,
altitude,
}
}
#[inline]
#[must_use]
pub fn horizontal(&self) -> f64 {
f64::hypot(self.latitude, self.longitude)
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Correction {
pub latitude: f64,
pub longitude: f64,
pub altitude: f64,
}
impl Correction {
#[inline]
#[must_use]
pub const fn new(latitude: f64, longitude: f64, altitude: f64) -> Self {
Self {
latitude,
longitude,
altitude,
}
}
#[inline]
#[must_use]
pub fn horizontal(&self) -> f64 {
f64::hypot(self.latitude, self.longitude)
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StatisticData {
pub count: Option<usize>,
pub mean: Option<f64>,
pub std: Option<f64>,
pub abs: Option<f64>,
pub min: Option<f64>,
pub max: Option<f64>,
}
impl StatisticData {
fn from_array(vs: &[f64]) -> Self {
if vs.is_empty() {
return Self {
count: None,
mean: None,
std: None,
abs: None,
min: None,
max: None,
};
}
let sum = ksum(vs);
let count = vs.len();
if sum.is_nan() {
return Self {
count: Some(count),
mean: Some(f64::NAN),
std: Some(f64::NAN),
abs: Some(f64::NAN),
min: Some(f64::NAN),
max: Some(f64::NAN),
};
}
let mut max = f64::MIN;
let mut min = f64::MAX;
let mut std: Vec<f64> = Vec::with_capacity(count);
let mut abs: Vec<f64> = Vec::with_capacity(count);
for v in vs.iter() {
max = v.max(max);
min = v.min(min);
std.push((sum - *v).powi(2));
abs.push(v.abs());
}
let length = count as f64;
Self {
count: Some(count),
mean: Some(sum / length),
std: Some((ksum(&std) / length).sqrt()),
abs: Some(ksum(&abs) / length),
min: Some(min),
max: Some(max),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Statistics {
pub latitude: StatisticData,
pub longitude: StatisticData,
pub altitude: StatisticData,
pub horizontal: StatisticData,
}
pub trait ParameterSet {
fn mesh_unit(&self) -> MeshUnit;
fn get(&self, meshcode: &u32) -> Option<&Parameter>;
}
pub trait ParameterData {
fn to_vec(&self) -> Vec<(&u32, &Parameter)>;
}
#[derive(Debug)]
pub struct Transformer<T> {
inner: T,
}
impl<T> Transformer<T> {
pub const MAX_ERROR: f64 = 5e-14;
#[inline]
#[must_use]
pub const fn new(parameter: T) -> Self {
Self { inner: parameter }
}
}
impl Transformer<ParData<RandomState>> {
#[inline]
pub fn from_str(s: &str, format: Format) -> Result<Self, ParseParError> {
let data = ParData::from_str(s, format)?;
Ok(Self::new(data))
}
#[inline]
pub fn from_str_with_description(
s: &str,
format: Format,
description: String,
) -> Result<Self, ParseParError> {
let mut data = ParData::from_str(s, format)?;
data.description = Some(description);
Ok(Self::new(data))
}
}
impl<T> Transformer<T>
where
T: ParameterSet,
{
#[must_use]
pub fn mesh_unit(&self) -> MeshUnit {
self.inner.mesh_unit()
}
#[must_use]
pub fn get(&self, meshcode: &u32) -> Option<&Parameter> {
self.inner.get(meshcode)
}
}
impl<T> Transformer<T>
where
T: ParameterData,
{
#[must_use]
pub fn statistics(&self) -> Statistics {
let mut params = self.inner.to_vec();
params.sort_by_key(|(k, _)| *k);
let temp: Vec<_> = params.iter().map(|(_, p)| p.latitude).collect();
let latitude = StatisticData::from_array(&temp);
let temp: Vec<_> = params.iter().map(|(_, p)| p.longitude).collect();
let longitude = StatisticData::from_array(&temp);
let temp: Vec<_> = params.iter().map(|(_, p)| p.altitude).collect();
let altitude = StatisticData::from_array(&temp);
let temp: Vec<_> = params.iter().map(|(_, p)| p.horizontal()).collect();
let horizontal = StatisticData::from_array(&temp);
Statistics {
latitude,
longitude,
altitude,
horizontal,
}
}
}
impl<T> PartialEq for Transformer<T>
where
T: PartialEq,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner.eq(&other.inner)
}
}
impl<T> Clone for Transformer<T>
where
T: Clone,
{
#[inline]
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.inner.clone_from(&source.inner);
}
}
#[cfg(test)]
mod test {
use crate::TransformerBuilder;
use super::*;
mod test_ksum {
use super::*;
#[test]
fn test_nan() {
let actual = ksum(&[1., f64::NAN, 1.]);
assert!(actual.is_nan());
}
}
mod test_transformer {
use super::*;
#[allow(non_upper_case_globals)]
const SemiDynaEXE: [(u32, (f64, f64, f64)); 4] = [
(54401005, (-0.00622, 0.01516, 0.0946)),
(54401055, (-0.0062, 0.01529, 0.08972)),
(54401100, (-0.00663, 0.01492, 0.10374)),
(54401150, (-0.00664, 0.01506, 0.10087)),
];
#[test]
fn test_stats() {
let stats = TransformerBuilder::new()
.format(Format::SemiDynaEXE)
.parameters(SemiDynaEXE)
.build()
.statistics();
assert_eq!(
stats.latitude,
StatisticData {
count: Some(4),
mean: Some(-0.0064225),
std: Some(0.019268673410486777),
abs: Some(0.006422499999999999),
min: Some(-0.00664),
max: Some(-0.0062)
}
);
assert_eq!(
stats.longitude,
StatisticData {
count: Some(4),
mean: Some(0.0151075),
std: Some(0.045322702644480496),
abs: Some(0.0151075),
min: Some(0.01492),
max: Some(0.01529)
}
);
assert_eq!(
stats.altitude,
StatisticData {
count: Some(4),
mean: Some(0.0972325),
std: Some(0.29174846730531423),
abs: Some(0.0972325),
min: Some(0.08972),
max: Some(0.10374)
}
);
assert_eq!(
stats.horizontal,
StatisticData {
count: Some(4),
mean: Some(if cfg!(target_os = "linux") {
0.016417802947905496
} else {
0.0164178029479055
}),
std: Some(if cfg!(target_os = "linux") {
0.04925345347374167
} else {
0.04925345347374168
}),
abs: Some(if cfg!(target_os = "linux") {
0.016417802947905496
} else {
0.0164178029479055
}),
min: Some(0.016326766366920303),
max: Some(0.016499215132847987)
}
);
let stats = TransformerBuilder::new()
.format(Format::TKY2JGD)
.build()
.statistics();
assert_eq!(
stats.latitude,
StatisticData {
count: None,
mean: None,
std: None,
abs: None,
min: None,
max: None
}
);
assert_eq!(
stats.longitude,
StatisticData {
count: None,
mean: None,
std: None,
abs: None,
min: None,
max: None
}
);
assert_eq!(
stats.altitude,
StatisticData {
count: None,
mean: None,
std: None,
abs: None,
min: None,
max: None
}
);
assert_eq!(
stats.horizontal,
StatisticData {
count: None,
mean: None,
std: None,
abs: None,
min: None,
max: None
}
);
let stats = TransformerBuilder::new()
.format(Format::SemiDynaEXE)
.parameters([(54401005, (1., 0.0, f64::NAN))])
.build()
.statistics();
assert_eq!(
stats.latitude,
StatisticData {
count: Some(1),
mean: Some(1.0),
std: Some(0.0),
abs: Some(1.0),
min: Some(1.0),
max: Some(1.0)
}
);
assert_eq!(
stats.longitude,
StatisticData {
count: Some(1),
mean: Some(0.0),
std: Some(0.0),
abs: Some(0.0),
min: Some(0.0),
max: Some(0.0)
}
);
assert_eq!(stats.altitude.count, Some(1));
assert!(stats.altitude.mean.unwrap().is_nan());
assert!(stats.altitude.std.unwrap().is_nan());
assert!(stats.altitude.abs.unwrap().is_nan());
assert!(stats.altitude.min.unwrap().is_nan());
assert!(stats.altitude.max.unwrap().is_nan());
assert_eq!(
stats.horizontal,
StatisticData {
count: Some(1),
mean: Some(1.0),
std: Some(0.0),
abs: Some(1.0),
min: Some(1.0),
max: Some(1.0)
}
);
}
}
}