Skip to content

Commit

Permalink
impl Ellipsoid
Browse files Browse the repository at this point in the history
  • Loading branch information
p4ken committed Jun 15, 2024
1 parent 3b50e70 commit 4a1be46
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 54 deletions.
71 changes: 48 additions & 23 deletions src/coord.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
use std::ops::{Add, Mul, Sub};

/// 測地座標。
/// Geodetic coordinate.
// pub trait Geodetic: Sized {
// fn with_lat_lon(lat_lon: (f64, f64)) -> Self;
// fn lat_lon(&self) -> (f64, f64);
// fn with_lon_lat(lon_lat: (f64, f64)) -> Self {
// let (lon, lat) = lon_lat;
// Self::with_lat_lon((lat, lon))
// }
// fn lon_lat(&self) -> (f64, f64) {
// let (lat, lon) = self.lat_lon();
// (lon, lat)
// }
// }

// mod seal {
// pub trait Sealed {}
// impl Sealed for super::LatLon {}
// impl Sealed for super::LonLat {}
// }

/// 度単位の緯度経度。
/// Latitude and longitude in degrees.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
Expand Down Expand Up @@ -60,6 +39,7 @@ impl LatLon {
pub fn from_micro_secs<T: Into<f64>>(lat: T, lon: T) -> Self {
Self::from_milli_secs(lat, lon) * 0.001
}

// pub fn new(lat_lon: impl Into<(f64, f64)>) -> Self {
// let (lat, lon) = lat_lon.into();
// Self(lat, lon)
Expand All @@ -68,18 +48,22 @@ impl LatLon {
// let (lon, lat) = lon_lat.into();
// Self(lat, lon)
// }

pub fn lat(&self) -> f64 {
self.0
}

pub fn lon(&self) -> f64 {
self.1
}

// pub fn rev(&self) -> (f64, f64) {
// (self.lon(), self.lat())
// }
pub fn abs(self) -> Self {
self.map(f64::abs)
}

pub fn map(mut self, f: fn(f64) -> f64) -> Self {
self.0 = f(self.0);
self.1 = f(self.1);
Expand Down Expand Up @@ -133,7 +117,48 @@ impl<D: Into<i32>, M: Into<i32>, S: Into<f64>> From<(D, M, S)> for Dms {
}
}

pub struct ECEF {}
/// 三次元直交座標。
#[derive(Debug, Clone, Copy)]
pub struct ECEF {
x: f64,
y: f64,
z: f64,
}
impl ECEF {
pub fn to_geodetic() {}
/// コンストラクタ。
pub const fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}

pub fn x(&self) -> f64 {
self.x
}

pub fn y(&self) -> f64 {
self.y
}

pub fn z(&self) -> f64 {
self.z
}
}
impl Add for ECEF {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl Sub for ECEF {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
66 changes: 39 additions & 27 deletions src/crs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::LatLon;
use crate::{
coord::ECEF,
earth::{BESSEL, GRS80},
LatLon,
};

#[cfg(feature = "tky2jgd")]
use crate::TKY2JGD;
Expand Down Expand Up @@ -79,7 +83,7 @@ impl Tokyo {
/// たとえ陸地であっても、無人島や、後年に埋め立てられた沿岸部などで、パラメータグリッドが欠損している。
/// 複数の座標で表される形状が、パラメータグリッドの範囲内外をまたがっていると、変換後の形状が大きく変わる可能性がある。
///
/// 国土地理院によるオリジナルの実装の精度は、一定条件下で「緯度, 経度の標準偏差はそれぞれ9cm, 8cm」[(飛田, 2002)](crate#references) とされている。
/// 国土地理院によるオリジナルの実装の精度は、一定条件下で「緯度, 経度の標準偏差はそれぞれ9cm, 8cm」[(飛田, 2001)](crate#references) とされている。
#[cfg(feature = "tky2jgd")]
pub fn to_jgd2000(&self) -> Jgd2000 {
match TKY2JGD.interpolate(self.degree) {
Expand All @@ -90,22 +94,33 @@ impl Tokyo {

/// 離島位置の補正量 [(飛田, 2003)](crate#references) を用いて [`Tokyo97`] へ変換する。
pub fn to_tokyo97(&self) -> Tokyo97 {
// https://www.drm.jp/jisx0410/JisGridSystem_1_Geoid.html
todo!()
// TODO
Tokyo97::new(self.degree)
}
}

/// 旧日本測地系。Tokyo 97, The older Japanese Datum.
///
/// 世界測地系を基準に、3パラメータによる変換式で定義された測地系 [(飛田, 1997)](crate#references)。
pub struct Tokyo97 {
lat_lon: LatLon,
degree: LatLon,
}
impl Tokyo97 {
/// 3パラメータによる変換式 [(飛田, 1997)](crate#references) を用いて [`Jgd2000`] へ変換する。
/// ITRF94系へ変換する3パラメータ [(飛田, 2001)](crate#references)。
const TO_ITRF94: ECEF = ECEF::new(-146.414, 507.337, 680.507);

/// ...
pub fn new(degree: LatLon) -> Self {
Self { degree }
}

/// 3パラメータ [(飛田, 2001)](crate#references) を用いて [`Jgd2000`] へ変換する。
/// Transform to JGD2000.
pub fn to_jgd2000(&self) -> Jgd2000 {
todo!()
// https://www.gsi.go.jp/LAW/G2000-g2000faq-1.htm
// > 測地成果2000での経度・緯度は、世界測地系であるITRF94座標系とGRS80の楕円体を使用して表します
let itrf94 = BESSEL.to_ecef(self.degree) + Self::TO_ITRF94;
Jgd2000::new(GRS80.to_geodetic(itrf94))
}

/// 離島位置の補正量 [(飛田, 2003)](crate#references) を用いて [`Tokyo`] へ逆変換する。
Expand All @@ -117,15 +132,12 @@ impl Tokyo97 {
///
/// EPSG: 4612
pub struct Jgd2000 {
lat_lon: LatLon,
degree: LatLon,
}

impl Jgd2000 {
// GRS80楕円体

fn new(lat_lon: impl Into<LatLon>) -> Self {
let lat_lon = lat_lon.into();
Self { lat_lon }
/// ...
fn new(degree: LatLon) -> Self {
Self { degree }
}

/// [`TOUHOKUTAIHEIYOUOKI2011`] を用いて [`Jgd2011`] へ変換する。
Expand All @@ -137,42 +149,42 @@ impl Jgd2000 {
/// いずれにしても ...
pub fn to_jgd2011(&self) -> Jgd2011 {
// todo
Jgd2011::new(self.lat_lon)
Jgd2011::new(self.degree)
}

/// [`TKY2JGD`] を用いて [`Tokyo`] へ逆変換する。
/// Inverse of [`Tokyo::to_jgd2000`].
///
/// 今後、あらゆるデータが世界測地系で測量、作成されるため、なるべく本メソッドは使わない方が良い。
/// 新旧の測地系の座標を重ねる用途であれば、原則として旧日本測地系から世界測地系へ変換した方が良い。
pub fn to_tokyo(&self) {}
// /// [`TKY2JGD`] を用いて [`Tokyo`] へ逆変換する。
// /// Inverse of [`Tokyo::to_jgd2000`].
// ///
// /// 今後、あらゆるデータが世界測地系で測量、作成されるため、なるべく本メソッドは使わない方が良い。
// /// 新旧の測地系の座標を重ねる用途であれば、原則として旧日本測地系から世界測地系へ変換した方が良い。
// pub fn to_tokyo(&self) {}

/// 3パラメータを用いて [`Tokyo97`] へ逆変換する。
/// Inverse of [`Tokyo97::to_jgd2000`].
pub fn to_tokyo97(&self) {}

/// ...
pub fn inner(&self) -> LatLon {
self.lat_lon
self.degree
}
}

/// 世界測地系。Japanese Geodetic Datum 2011 (JGD2011).
///
/// EPSG: 6668
pub struct Jgd2011 {
lat_lon: LatLon,
degree: LatLon,
}
impl Jgd2011 {
fn new(lat_lon: impl Into<LatLon>) -> Self {
let lat_lon = lat_lon.into();
Self { lat_lon }
/// ...
fn new(degree: LatLon) -> Self {
Self { degree }
}

/// 度単位の緯度経度。
/// Latitude and longitude in degrees.
pub fn lat_lon(&self) -> (f64, f64) {
self.lat_lon.into()
self.degree.into()
}

// pub fn lon_lat(&self) -> (f64, f64) {
Expand Down
81 changes: 81 additions & 0 deletions src/earth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use crate::{coord::ECEF, LatLon};

/// GRS80楕円体
pub const GRS80: Ellipsoid = Ellipsoid {
equatorial_radius: 6378137.0,
polar_radius: 6356752.31424518,
};

/// Bessel楕円体
pub const BESSEL: Ellipsoid = Ellipsoid {
equatorial_radius: 6377397.155,
polar_radius: 6356078.963,
};

/// 回転楕円体。
#[derive(Debug, Clone)]
pub struct Ellipsoid {
// 赤道半径 (メートル)
equatorial_radius: f64,

// 極半径 (メートル)
polar_radius: f64,
}
impl Ellipsoid {
/// 測地座標から変換する
pub fn to_ecef(&self, degree: LatLon) -> ECEF {
let lat = degree.lat().to_radians();
let lon = degree.lon().to_radians();
let geoid = self.equatorial_radius
/ (1.0 - self.equatorial_eccentricity() * lat.sin().powi(2)).sqrt();
ECEF::new(
geoid * lat.cos() * lon.cos(),
geoid * lat.cos() * lon.sin(),
geoid * (1.0 - self.equatorial_eccentricity()) * lat.sin(),
)
}

/// 測地座標に変換する
pub fn to_geodetic(&self, ecef: ECEF) -> LatLon {
let p = ecef.x().hypot(ecef.y());
let theta = ((ecef.z() * self.equatorial_radius) / (p * self.polar_radius)).atan();
let lat = (ecef.z()
+ self.polar_eccentricity() * self.polar_radius * (theta.sin().powi(3)))
.atan2(p - self.equatorial_eccentricity() * self.equatorial_radius * (theta.cos().powi(3)));
let lon = ecef.y().atan2(ecef.x());
LatLon(lat.to_degrees(), lon.to_degrees())
}

/// 赤道離心率 = (赤道半径^2 - 極半径^2) / 赤道半径^2
fn equatorial_eccentricity(&self) -> f64 {
let e2 = self.equatorial_radius.powi(2);
let p2 = self.polar_radius.powi(2);
(e2 - p2) / e2
}

// 極離心率 = (赤道半径^2 - 極半径^2) / 極半径^2
fn polar_eccentricity(&self) -> f64 {
let e2 = self.equatorial_radius.powi(2);
let p2 = self.polar_radius.powi(2);
(e2 - p2) / p2
}
}

#[cfg(test)]
mod tests {
use approx::assert_ulps_eq;

use super::{BESSEL, GRS80};

#[test]
fn grs80() {
assert_ulps_eq!(GRS80.equatorial_eccentricity(), 0.006694379990141124);
assert_ulps_eq!(GRS80.polar_eccentricity(), 0.006739496742276239);
}

#[test]
fn bessel() {
assert_ulps_eq!(BESSEL.equatorial_eccentricity(), 0.006674372174974933);
assert_ulps_eq!(BESSEL.polar_eccentricity(), 0.006719218741581313);
}
}
2 changes: 1 addition & 1 deletion src/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl<'a> Grid<'a> {
/// 指定された座標が属する3次メッシュの四隅すべてのパラメータがグリッド内に存在しなければならない。
/// 一つでも欠けていた場合は `None` を返す。
pub fn interpolate(&self, p: LatLon) -> Option<LatLon> {
// > 地域毎の変換パラメータの格子点は,3 次メッシュの中央ではなく,南西隅に対応する (飛田, 2002)
// > 地域毎の変換パラメータの格子点は,3 次メッシュの中央ではなく,南西隅に対応する (飛田, 2001)
let mesh = Mesh3::floor(p);
let i = self.search_after(0, mesh)?;
let sw_shift = self.dots[i].shift;
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@
//! # References
//!
//! - 飛田幹男 [最近の測地座標系と座標変換についての考察](https://www.jstage.jst.go.jp/article/sokuchi1954/43/4/43_4_231/_pdf) (測地学会誌 43巻 4号 (1997) pp231-235)
//! - 飛田幹男 [世界測地系移行のための座標変換ソフトウェア "TKY2JGD"](https://www.gsi.go.jp/common/000063173.pdf) (国土地理院時報 97集 (2002) pp31-51)
//! - 飛田幹男 [世界測地系移行のための座標変換ソフトウェア "TKY2JGD"](https://www.gsi.go.jp/common/000063173.pdf) (国土地理院時報 97集 (2001) pp31-51)
//! - 飛田幹男ほか [日本測地系における離島位置の補正量](https://www.jstage.jst.go.jp/article/sokuchi1954/49/3/49_3_181/_pdf) (測地学会誌 49巻 3号 (2003) pp181-192)
//! - 飛田幹男 [地震時地殻変動に伴う座標値の変化を補正するソフトウェア "PatchJGD"](https://www.jstage.jst.go.jp/article/sokuchi/55/4/55_4_355/_pdf/-char/ja) (測地学会誌 55巻 4号 (2009) pp355-367)
mod coord;
mod crs;
mod earth;
mod grid;
mod island;
mod par;
Expand Down
Loading

0 comments on commit 4a1be46

Please sign in to comment.