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}