use std::f64::consts::PI;
use std::str::FromStr;
use num::cast::AsPrimitive;
use num::Integer;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum JprOrigin {
One = 1,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Eleven,
Twelve,
Thirteen,
Fourteen,
Fifteen,
Sixteen,
Seventeen,
Eighteen,
Nineteen,
}
impl JprOrigin {
pub fn parse<T: Integer + AsPrimitive<u8>>(num: T) -> Result<Self, ()> {
match num.as_() {
1 => Ok(Self::One),
2 => Ok(Self::Two),
3 => Ok(Self::Three),
4 => Ok(Self::Four),
5 => Ok(Self::Five),
6 => Ok(Self::Six),
7 => Ok(Self::Seven),
8 => Ok(Self::Eight),
9 => Ok(Self::Nine),
10 => Ok(Self::Ten),
11 => Ok(Self::Eleven),
12 => Ok(Self::Twelve),
13 => Ok(Self::Thirteen),
14 => Ok(Self::Fourteen),
15 => Ok(Self::Fifteen),
16 => Ok(Self::Sixteen),
17 => Ok(Self::Seventeen),
18 => Ok(Self::Eighteen),
19 => Ok(Self::Nineteen),
_ => Err(()),
}
}
}
impl TryFrom<u8> for JprOrigin {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<u16> for JprOrigin {
type Error = ();
fn try_from(value: u16) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<u32> for JprOrigin {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<u64> for JprOrigin {
type Error = ();
fn try_from(value: u64) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<usize> for JprOrigin {
type Error = ();
fn try_from(value: usize) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<i8> for JprOrigin {
type Error = ();
fn try_from(value: i8) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<i16> for JprOrigin {
type Error = ();
fn try_from(value: i16) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<i32> for JprOrigin {
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<i64> for JprOrigin {
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl TryFrom<isize> for JprOrigin {
type Error = ();
fn try_from(value: isize) -> Result<Self, Self::Error> {
JprOrigin::parse(value)
}
}
impl FromStr for JprOrigin {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.parse::<u8>() {
Ok(num) => JprOrigin::parse(num),
Err(_) => Err(()),
}
}
}
const DEG2RAD: f64 = PI / 180.;
const LAT0: [f64; 20] = [
0.,
33. * DEG2RAD,
33. * DEG2RAD,
36. * DEG2RAD,
33. * DEG2RAD,
36. * DEG2RAD,
36. * DEG2RAD,
36. * DEG2RAD,
36. * DEG2RAD,
36. * DEG2RAD,
40. * DEG2RAD,
44. * DEG2RAD,
44. * DEG2RAD,
44. * DEG2RAD,
26. * DEG2RAD,
26. * DEG2RAD,
26. * DEG2RAD,
26. * DEG2RAD,
20. * DEG2RAD,
26. * DEG2RAD,
];
const LONG0: [f64; 20] = [
0.,
7770. / 60. * DEG2RAD,
7860. / 60. * DEG2RAD,
7930. / 60. * DEG2RAD,
8010. / 60. * DEG2RAD,
8060. / 60. * DEG2RAD,
8160. / 60. * DEG2RAD,
8230. / 60. * DEG2RAD,
8310. / 60. * DEG2RAD,
8390. / 60. * DEG2RAD,
8450. / 60. * DEG2RAD,
8415. / 60. * DEG2RAD,
8535. / 60. * DEG2RAD,
8655. / 60. * DEG2RAD,
8520. / 60. * DEG2RAD,
7650. / 60. * DEG2RAD,
7440. / 60. * DEG2RAD,
7860. / 60. * DEG2RAD,
8160. / 60. * DEG2RAD,
9240. / 60. * DEG2RAD,
];
pub fn jpr2ll(yx: (f64, f64), origin: JprOrigin) -> (f64, f64) {
let (y, x) = yx;
const A0: f64 = 1.0000007049454078;
const A_ARR: [f64; 5] = [
-0.0025188297041239312,
2.6435429493240994e-6,
-3.4526259073074147e-9,
4.891830424387949e-12,
-7.228726045813916e-15,
];
const BETA_ARR: [f64; 5] = [
0.0008377321681620316,
5.905870211016955e-8,
1.6734826761541112e-10,
2.1648237311010893e-13,
3.79409187887551e-16,
];
const DELTA_ARR: [f64; 6] = [
0.003356551485604312,
6.571873263127177e-6,
1.7646404372866207e-8,
5.3877538900094696e-11,
1.7640075159133883e-13,
6.056074055207582e-16,
];
const M0: f64 = 0.9999;
const A: f64 = 6378137.;
const F: f64 = 298.257222101;
const N: f64 = 1. / (2. * F - 1.);
const A_: f64 = M0 * A * A0 / (1. + N);
let lat0 = LAT0[origin as usize];
let long0 = LONG0[origin as usize];
let s_ = ((M0 * A) / (1. + N))
* (A0 * lat0
+ A_ARR.iter().enumerate().fold(0., |acc, (i, &a)| {
acc + a * (2. * (i as f64 + 1.) * lat0).sin()
}));
let xi = (x + s_) / A_;
let eta = y / A_;
let xi2 = xi
- BETA_ARR.iter().enumerate().fold(0., |acc, (i, &b)| {
acc + b * (2. * (i as f64 + 1.) * xi).sin() * (2. * (i as f64 + 1.) * eta).cosh()
});
let eta2 = eta
- BETA_ARR.iter().enumerate().fold(0., |acc, (i, &b)| {
acc + b * (2. * (i as f64 + 1.) * xi).cos() * (2. * (i as f64 + 1.) * eta).sinh()
});
let chi = (xi2.sin() / eta2.cosh()).asin();
let lat = chi
+ DELTA_ARR.iter().enumerate().fold(0., |acc, (i, &d)| {
acc + d * (2. * (i as f64 + 1.) * chi).sin()
});
let long = long0 + (eta2.sinh() / xi2.cos()).atan();
(long, lat)
}
pub fn ll2jpr(ll: (f64, f64), origin: JprOrigin) -> (f64, f64) {
let (long, lat) = ll;
let lat0 = LAT0[origin as usize];
let long0 = LONG0[origin as usize];
const A0: f64 = 1.0000007049454078;
const A_ARR: [f64; 5] = [
-0.0025188297041239312,
2.6435429493240994e-6,
-3.4526259073074147e-9,
4.891830424387949e-12,
-7.228726045813916e-15,
];
const ALPHA_ARR: [f64; 5] = [
0.0008377318247285465,
7.608527848379248e-7,
1.1976455002315586e-9,
2.4291502606542468e-12,
5.750164384091974e-15,
];
const M0: f64 = 0.9999;
const A: f64 = 6378137.;
const F: f64 = 298.257222101;
const N: f64 = 1. / (2. * F - 1.);
const A_: f64 = ((M0 * A) / (1. + N)) * A0;
let s_ = ((M0 * A) / (1. + N))
* (A0 * lat0
+ A_ARR.iter().enumerate().fold(0., |acc, (i, &a)| {
acc + a * (2. * (i as f64 + 1.) * lat0).sin()
}));
let lambda_c = (long - long0).cos();
let lambda_s = (long - long0).sin();
let t = (lat.sin().atanh()
- ((2. * N.sqrt()) / (1. + N)) * (((2. * N.sqrt()) / (1. + N)) * lat.sin()).atanh())
.sinh();
let t_ = (1. + t.powf(2.)).sqrt();
let xi2 = (t / lambda_c).atan();
let eta2 = (lambda_s / t_).atanh();
let x = A_
* (xi2
+ ALPHA_ARR.iter().enumerate().fold(0., |acc, (i, &a)| {
acc + a * (2. * (i as f64 + 1.) * xi2).sin() * (2. * (i as f64 + 1.) * eta2).cosh()
}))
- s_;
let y = A_
* (eta2
+ ALPHA_ARR.iter().enumerate().fold(0., |acc, (i, &a)| {
acc + a * (2. * (i as f64 + 1.) * xi2).cos() * (2. * (i as f64 + 1.) * eta2).sinh()
}));
(y, x)
}
#[cfg(test)]
mod tests {
use close_to::assert_close_to;
use super::*;
#[test]
fn jpr2ll_works() {
let (long, lat) = jpr2ll((22694.980, 11573.375), JprOrigin::Nine);
assert_eq!(
((long * 1000.).floor(), (lat * 1000.).floor()),
(
((140.0_f64 + 5. / 60. + 8. / 3600.) * 1000.)
.to_radians()
.floor(),
((36.0_f64 + 6. / 60. + 15. / 3600.) * 1000.)
.to_radians()
.floor()
)
);
assert_close_to(long, 140.085555556_f64.to_radians(), 4);
assert_close_to(lat, 36.1041666667_f64.to_radians(), 4);
}
#[test]
fn ll2jpr_works() {
let (y, x) = ll2jpr(
(
140.08785504166664_f64.to_radians(),
36.103774791666666_f64.to_radians(),
),
JprOrigin::Nine,
);
assert_close_to(y, 22916.2436, 4);
assert_close_to(x, 11543.6883, 4);
}
}