Skip to main content

kasane_logic/geometry/point/ecef/
mod.rs

1pub mod impls;
2
3use crate::{RangeId, SingleId, error::Error, geometry::point::coordinate::Coordinate};
4
5/// 地心直交座標系(ECEF: Earth-Centered, Earth-Fixed)における座標を表す。
6///
7/// 原点は地球の重心にあり、
8/// * X 軸は赤道面上で本初子午線方向
9/// * Y 軸は赤道面上で東経 90 度方向
10/// * Z 軸は北極方向
11///
12/// 単位はすべてメートル。
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
15#[derive(Clone, Copy, PartialEq, PartialOrd)]
16pub struct Ecef {
17    x: f64,
18    y: f64,
19    z: f64,
20}
21
22impl Ecef {
23    /// 指定された XYZ 成分から [`Ecef`] を生成する。
24    ///
25    /// # Examples
26    /// ```no_run
27    /// # use kasane_logic::Ecef;
28    ///
29    /// let ecef = Ecef::new(10.0, 20.0, 30.0);
30    ///
31    /// assert_eq!(ecef.x(), 10.0);
32    /// assert_eq!(ecef.y(), 20.0);
33    /// assert_eq!(ecef.z(), 30.0);
34    /// ```
35    pub fn new(x: f64, y: f64, z: f64) -> Ecef {
36        Ecef { x, y, z }
37    }
38    /// X 成分を返す。
39    ///
40    /// # Examples
41    /// ```
42    /// # use kasane_logic::Ecef;
43    ///
44    /// let ecef = Ecef::new(1.0, 0.0, 0.0);
45    /// assert_eq!(ecef.x(), 1.0);
46    /// ```
47    pub fn x(&self) -> f64 {
48        self.x
49    }
50
51    /// Y 成分を返す。
52    ///
53    /// # Examples
54    /// ```
55    /// # use kasane_logic::Ecef;
56    ///
57    /// let ecef = Ecef::new(0.0, 2.0, 0.0);
58    /// assert_eq!(ecef.y(), 2.0);
59    /// ```
60    pub fn y(&self) -> f64 {
61        self.y
62    }
63
64    /// Z 成分を返す。
65    ///
66    /// # Examples
67    /// ```
68    /// # use kasane_logic::Ecef;
69    ///
70    /// let ecef = Ecef::new(0.0, 0.0, 3.0);
71    /// assert_eq!(ecef.z(), 3.0);
72    /// ```
73    pub fn z(&self) -> f64 {
74        self.z
75    }
76
77    /// X 成分を設定する。
78    ///
79    /// # Examples
80    /// ```
81    /// # use kasane_logic::Ecef;
82    ///
83    /// let mut ecef = Ecef::new(0.0, 0.0, 0.0);
84    /// ecef.set_x(5.0);
85    ///
86    /// assert_eq!(ecef.x(), 5.0);
87    /// ```
88    pub fn set_x(&mut self, x: f64) {
89        self.x = x;
90    }
91
92    /// Y 成分を設定する。
93    ///
94    /// # Examples
95    /// ```
96    /// # use kasane_logic::Ecef;
97    ///
98    /// let mut ecef = Ecef::new(0.0, 0.0, 0.0);
99    /// ecef.set_y(6.0);
100    ///
101    /// assert_eq!(ecef.y(), 6.0);
102    /// ```
103    pub fn set_y(&mut self, y: f64) {
104        self.y = y;
105    }
106
107    /// Z 成分を設定する。
108    ///
109    /// # Examples
110    /// ```
111    /// # use kasane_logic::Ecef;
112    ///
113    /// let mut ecef = Ecef::new(0.0, 0.0, 0.0);
114    /// ecef.set_z(7.0);
115    ///
116    /// assert_eq!(ecef.z(), 7.0);
117    /// ```
118    pub fn set_z(&mut self, z: f64) {
119        self.z = z;
120    }
121
122    /// この ECEF 座標を、指定されたズームレベルの [`SingleId`] に変換する。
123    pub fn single_id(&self, z: u8) -> Result<SingleId, Error> {
124        let coordinate: Coordinate = (*self).try_into()?;
125        coordinate.single_id(z)
126    }
127
128    /// この ECEF 座標を、指定されたズームレベルの [`RangeId`] に変換する。
129    pub fn range_id(&self, z: u8) -> Result<RangeId, Error> {
130        let coordinate: Coordinate = (*self).try_into()?;
131        Ok(RangeId::from(coordinate.single_id(z)?))
132    }
133
134    /// 他の [`Ecef`] 座標との距離をメートル単位で返す。
135    ///
136    /// # Examples
137    /// ```
138    /// # use kasane_logic::Ecef;
139    ///
140    /// let a = Ecef::new(0.0, 0.0, 0.0);
141    /// let b = Ecef::new(3.0, 4.0, 0.0);
142    ///
143    /// assert_eq!(a.distance(&b), 5.0);
144    /// ```
145    pub fn distance(&self, other: &Ecef) -> f64 {
146        ((self.x() - other.x()).powi(2)
147            + (self.y() - other.y()).powi(2)
148            + (self.z() - other.z()).powi(2))
149        .sqrt()
150    }
151
152    ///他の[Ecef]型との外積を取る。
153    pub fn cross(&self, other: &Ecef) -> Ecef {
154        Ecef {
155            x: self.y * other.z - self.z * other.y,
156            y: self.z * other.x - self.x * other.z,
157            z: self.x * other.y - self.y * other.x,
158        }
159    }
160
161    // 原点からの距離の2乗を取得する。
162    pub fn norm_squared(&self) -> f64 {
163        self.x * self.x + self.y * self.y + self.z * self.z
164    }
165
166    /// Ecefが同じ位置にあるかを判定します
167    /// 2点間の直線距離が epsilon 以内にあるかを判定します
168    pub fn eq_epsilon(&self, other: &Ecef, epsilon: f64) -> bool {
169        let distance_squared = self.distance(other);
170        distance_squared < epsilon * epsilon
171    }
172
173    /// 内積(ドット積)を計算する。
174    pub fn dot(&self, other: &Ecef) -> f64 {
175        self.x * other.x + self.y * other.y + self.z * other.z
176    }
177
178    /// 指定された軸インデックス (0=x, 1=y, 2=z) の成分を返す。
179    /// 投影計算などで軸を動的に扱いたい場合に有用。
180    pub fn get_component(&self, index: usize) -> f64 {
181        match index {
182            0 => self.x,
183            1 => self.y,
184            _ => self.z,
185        }
186    }
187
188    /// 指定された2つの軸(u, v)に基づいて 2D 平面に投影した座標を返す。
189    pub fn project_2d(&self, u_axis: usize, v_axis: usize) -> (f64, f64) {
190        (self.get_component(u_axis), self.get_component(v_axis))
191    }
192}