Skip to main content

kasane_logic/spatial_id/single_id/
impls.rs

1use crate::{
2    Coordinate, Ecef, Error, F_MAX, F_MIN, SingleId, SpatialId, SpatialIdError, TemporalId, XY_MAX,
3    spatial_id::helpers,
4};
5use std::fmt;
6
7impl fmt::Display for SingleId {
8    /// `SingleId` を文字列形式で表示する。
9    ///
10    /// 形式は `"{z}/{f}/{x}/{y}"`。
11    ///
12    /// ```no_run
13    /// # use kasane_logic::SingleId;
14    /// # use std::fmt::Write;
15    /// let id = SingleId::new(4, 6, 9, 10).unwrap();
16    /// let s = format!("{}", id);
17    /// assert_eq!(s, "4/6/9/10");
18    /// ```
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        write!(f, "{}/{}/{}/{}", self.z, self.f, self.x, self.y)?;
21        //時間の情報があれば書き込み
22        if !self.temporal_id.is_whole() {
23            write!(f, "_{}", self.temporal_id)?;
24        }
25        Ok(())
26    }
27}
28
29impl SpatialId for SingleId {
30    fn f_min(&self) -> i32 {
31        F_MIN[self.z() as usize]
32    }
33
34    fn f_max(&self) -> i32 {
35        F_MAX[self.z() as usize]
36    }
37
38    fn x_max(&self) -> u32 {
39        XY_MAX[self.z() as usize]
40    }
41
42    fn y_max(&self) -> u32 {
43        XY_MAX[self.z() as usize]
44    }
45
46    /// 指定したインデックス差 `by` に基づき、この `SingleId` を垂直上下方向に動かします。
47    ///
48    /// # パラメータ
49    /// * `by` — インデックス差
50    ///
51    /// # バリデーション
52    /// - Fインデックスが範囲外になる場合は[`SpatialIdError::FOutOfRange`]を返します
53    ///
54    /// 移動
55    /// ```
56    /// # use kasane_logic::SingleId;
57    /// # use kasane_logic::SpatialId;
58    /// let mut id = SingleId::new(4, 6, 9, 10).unwrap();
59    /// assert_eq!(id.f(), 6);
60    ///
61    /// let _ = id.move_f(-10).unwrap();
62    /// assert_eq!(id.f(), -4);
63    /// ```
64    ///
65    /// 範囲外の検知によるエラー
66    /// ```
67    /// # use kasane_logic::SingleId;
68    /// # use kasane_logic::SpatialId;
69    /// # use kasane_logic::SpatialIdError;
70    /// let mut id = SingleId::new(4, 6, 9, 10).unwrap();
71    /// assert_eq!(id.f(), 6);
72    /// assert_eq!(id.move_f(50), Err(SpatialIdError::FOutOfRange { z: 4, f: 56 }.into()));
73    /// ```
74    fn move_f(&mut self, by: i32) -> Result<(), Error> {
75        let new = self.f.checked_add(by).ok_or_else(|| {
76            Error::from(SpatialIdError::FOutOfRange {
77                f: if by >= 0 { i32::MAX } else { i32::MIN },
78                z: self.z,
79            })
80        })?;
81
82        if new < self.f_min() || new > self.f_max() {
83            return Err(SpatialIdError::FOutOfRange { f: new, z: self.z }.into());
84        }
85
86        self.f = new;
87
88        Ok(())
89    }
90
91    /// 指定したインデックス差 `by` に基づき、この `SingleId` を東西方向に動かします。WEBメルカトル図法において、東西方向は循環しているためどのような値を指定してもエラーは発生しません。
92    ///
93    /// # パラメータ
94    /// * `by` — インデックス差
95    ///
96    /// 移動
97    /// ```
98    /// # use kasane_logic::SingleId;
99    /// # use kasane_logic::SpatialId;
100    /// let mut id = SingleId::new(4, 6, 9, 10).unwrap();
101    /// assert_eq!(id.x(), 9);
102    ///
103    /// let _ = id.move_x(-3);
104    /// assert_eq!(id.x(), 6);
105    /// ```
106    ///
107    /// 循環による移動
108    /// ```
109    /// # use kasane_logic::SingleId;
110    /// # use kasane_logic::SpatialId;
111    /// let mut id = SingleId::new(4, 6, 9, 10).unwrap();
112    /// assert_eq!(id.x(), 9);
113    ///
114    /// let _ = id.move_x(100);
115    /// assert_eq!(id.x(), 13);
116    /// ```
117    fn move_x(&mut self, by: i32) {
118        let max_len = (self.x_max() + 1) as i32;
119        let new = (self.x as i32 + by).rem_euclid(max_len);
120        self.x = new as u32;
121    }
122
123    /// 指定したインデックス差 `by` に基づき、この `SingleId` を南北方向に動かします。
124    ///
125    /// # パラメータ
126    /// * `by` — インデックス差
127    ///
128    /// # バリデーション
129    /// - Yインデックスが範囲外になる場合は[`SpatialIdError::YOutOfRange`]を返します
130    ///
131    /// 移動
132    /// ```
133    /// # use kasane_logic::SingleId;
134    /// # use kasane_logic::SpatialId;
135    /// let mut id = SingleId::new(4, 6, 9, 10).unwrap();
136    /// assert_eq!(id.y(), 10);
137    ///
138    /// let _ = id.move_y(-3).unwrap();
139    /// assert_eq!(id.y(), 7);
140    /// ```
141    ///
142    /// 範囲外の検知によるエラー
143    /// ```
144    /// # use kasane_logic::SingleId;
145    /// # use kasane_logic::SpatialId;
146    /// # use kasane_logic::SpatialIdError;
147    /// let mut id = SingleId::new(4, 6, 9, 10).unwrap();
148    /// assert_eq!(id.y(), 10);
149    /// assert_eq!(id.move_y(-20), Err(SpatialIdError::YOutOfRange { z: 4, y: 0 }.into()));
150    /// ```
151    fn move_y(&mut self, by: i32) -> Result<(), Error> {
152        let new = if by >= 0 {
153            self.y.checked_add(by as u32).ok_or_else(|| {
154                Error::from(SpatialIdError::YOutOfRange {
155                    y: u32::MAX,
156                    z: self.z,
157                })
158            })?
159        } else {
160            self.y
161                .checked_sub(by.unsigned_abs())
162                .ok_or(SpatialIdError::YOutOfRange {
163                    y: self.y_min(),
164                    z: self.z,
165                })?
166        };
167
168        if new > self.y_max() {
169            return Err(SpatialIdError::YOutOfRange { y: new, z: self.z }.into());
170        }
171
172        self.y = new;
173
174        Ok(())
175    }
176
177    /// `SingleId` の中心座標を[`Coordinate`]型で返します。
178    ///
179    /// 中心座標は空間IDの最も外側の頂点の8点の平均座標です。現実空間における空間IDは完全な直方体ではなく、緯度や高度によって歪みが発生していることに注意する必要があります。
180    ///
181    /// ```
182    /// # use kasane_logic::SpatialId;
183    /// # use kasane_logic::SingleId;
184    /// # use kasane_logic::Coordinate;
185    /// let id = SingleId::new(4, 6, 9, 14).unwrap();
186    /// let center: Coordinate = id.spatial_center();
187    /// println!("{:?}", center);
188    /// // Coordinate { latitude: -81.09321385260839, longitude: 33.75, altitude: 13631488.0 }
189    /// ```
190    fn spatial_center(&self) -> Coordinate {
191        unsafe {
192            Coordinate::new_unchecked(
193                helpers::latitude(self.y as f64 + 0.5, self.z),
194                helpers::longitude(self.x as f64 + 0.5, self.z),
195                helpers::altitude(self.f as f64 + 0.5, self.z),
196            )
197        }
198    }
199
200    /// `SingleId` の最も外側の頂点の8点の座標を[`Coordinate`]型の配列として返します。
201    ///
202    /// 現実空間における空間IDは完全な直方体ではなく、緯度や高度によって歪みが発生していることに注意する必要があります。
203    ///
204    /// ```
205    /// # use kasane_logic::SpatialId;
206    /// # use kasane_logic::SingleId;
207    /// # use kasane_logic::Coordinate;
208    /// let id = SingleId::new(4, 6, 9, 14).unwrap();
209    /// let vertices: [Coordinate; 8] = id.spatial_vertices();
210    /// println!("{:?}", vertices);
211    ///
212    ///  //[Coordinate { latitude: -79.17133464081945, longitude: 22.5, altitude: 12582912.0 }, Coordinate { latitude: -79.17133464081945, longitude: 45.0, altitude: 12582912.0 }, Coordinate { latitude: -82.67628497834903, longitude: 22.5, altitude: 12582912.0 }, Coordinate { latitude: -82.67628497834903, longitude: 45.0, altitude: 12582912.0 }, Coordinate { latitude: -79.17133464081945, longitude: 22.5, altitude: 14680064.0 }, Coordinate { latitude: -79.17133464081945, longitude: 45.0, altitude: 14680064.0 }, Coordinate { latitude: -82.67628497834903, longitude: 22.5, altitude: 14680064.0 }, Coordinate { latitude: -82.67628497834903, longitude: 45.0, altitude: 14680064.0 }]
213    /// ```
214    fn spatial_vertices(&self) -> [Coordinate; 8] {
215        let xs = [self.x as f64, self.x as f64 + 1.0];
216        let ys = [self.y as f64, self.y as f64 + 1.0];
217        let fs = [self.f as f64, self.f as f64 + 1.0];
218
219        // 各端点の値を前計算しておく
220        let lon2 = [
221            helpers::longitude(xs[0], self.z),
222            helpers::longitude(xs[1], self.z),
223        ];
224        let lat2 = [
225            helpers::latitude(ys[0], self.z),
226            helpers::latitude(ys[1], self.z),
227        ];
228        let alt2 = [
229            helpers::altitude(fs[0], self.z),
230            helpers::altitude(fs[1], self.z),
231        ];
232
233        // 結果配列
234        let mut out = [Coordinate::default(); 8];
235
236        let mut i = 0;
237        for f_i in 0..2 {
238            for y_i in 0..2 {
239                for x_i in 0..2 {
240                    out[i]
241                        .set_longitude(lon2[x_i])
242                        .expect("longitude must be within valid range");
243                    out[i]
244                        .set_latitude(lat2[y_i])
245                        .expect("latitude must be within valid range");
246                    out[i]
247                        .set_altitude(alt2[f_i])
248                        .expect("altitude must be within valid range");
249                    i += 1;
250                }
251            }
252        }
253
254        out
255    }
256
257    ///その空間IDのF方向の長さをメートル単位で計算する関数
258    fn length_f_meters(&self) -> f64 {
259        //Z=25のとき、ちょうど高さが1mとなる
260        2_f64.powi(25 - self.z() as i32)
261    }
262
263    ///その空間IDのX方向の長さをメートル単位で計算する関数
264    fn length_x_meters(&self) -> f64 {
265        let ecef: Ecef = self.spatial_center().into();
266        let r = (ecef.x() * ecef.x() + ecef.y() * ecef.y()).sqrt();
267        r * 2.0 * std::f64::consts::PI / (2_i32.pow(self.z() as u32) as f64)
268    }
269
270    ///その空間IDのY方向の長さをメートル単位で計算する関数
271    fn length_y_meters(&self) -> f64 {
272        let ecef: Ecef = self.spatial_center().into();
273        let r = (ecef.x() * ecef.x() + ecef.y() * ecef.y()).sqrt();
274        r * 2.0 * std::f64::consts::PI / (2_i32.pow(self.z() as u32) as f64)
275    }
276
277    fn temporal(&self) -> &TemporalId {
278        &self.temporal_id
279    }
280
281    fn temporal_mut(&mut self) -> &mut TemporalId {
282        &mut self.temporal_id
283    }
284}