coordinate_transformer/
pixel_ll.rs

1use std::f64::consts::PI;
2use std::str::FromStr;
3
4use num::cast::AsPrimitive;
5use num::Integer;
6
7/// Enumerated type representing the Zoom level.
8///
9/// Zoomレベルを表す列挙型。
10///
11/// The maximum Zoom level shall be 24 for [this](https://github.com/mapbox/geojson-vt/issues/87) reason.
12///
13/// Zoomレベルの最大は[これ](https://github.com/mapbox/geojson-vt/issues/87)を理由に24とする。
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub enum ZoomLv {
16    Lv0,
17    Lv1,
18    Lv2,
19    Lv3,
20    Lv4,
21    Lv5,
22    Lv6,
23    Lv7,
24    Lv8,
25    Lv9,
26    Lv10,
27    Lv11,
28    Lv12,
29    Lv13,
30    Lv14,
31    Lv15,
32    Lv16,
33    Lv17,
34    Lv18,
35    Lv19,
36    Lv20,
37    Lv21,
38    Lv22,
39    Lv23,
40    Lv24,
41}
42
43impl ZoomLv {
44    /// Attempts to convert from a type that implements `Into<u8>` to `LoomLv`.
45    /// Returns `Err` if the value of the argument is outside the range 0 to 24.///
46    ///
47    /// `Into<u8>`を実装する型から`LoomLv`への変換を試みます。
48    /// 引数の値が0から24の範囲外の場合、`Err`を返します。
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use coordinate_transformer::pixel_ll::ZoomLv;
54    ///
55    /// let zoom_lv = ZoomLv::parse(9);
56    /// assert_eq!(zoom_lv, Ok(ZoomLv::Lv9));
57    /// ```
58    /// ```should_panic
59    /// use coordinate_transformer::pixel_ll::ZoomLv;
60    ///
61    /// let zoom_lv = ZoomLv::parse(25);
62    /// assert!(zoom_lv.is_ok());
63    /// ```
64    pub fn parse<T: Integer + AsPrimitive<u8>>(num: T) -> Result<Self, ()> {
65        match num.as_() {
66            0 => Ok(Self::Lv0),
67            1 => Ok(Self::Lv1),
68            2 => Ok(Self::Lv2),
69            3 => Ok(Self::Lv3),
70            4 => Ok(Self::Lv4),
71            5 => Ok(Self::Lv5),
72            6 => Ok(Self::Lv6),
73            7 => Ok(Self::Lv7),
74            8 => Ok(Self::Lv8),
75            9 => Ok(Self::Lv9),
76            10 => Ok(Self::Lv10),
77            11 => Ok(Self::Lv11),
78            12 => Ok(Self::Lv12),
79            13 => Ok(Self::Lv13),
80            14 => Ok(Self::Lv14),
81            15 => Ok(Self::Lv15),
82            16 => Ok(Self::Lv16),
83            17 => Ok(Self::Lv17),
84            18 => Ok(Self::Lv18),
85            19 => Ok(Self::Lv19),
86            20 => Ok(Self::Lv20),
87            21 => Ok(Self::Lv21),
88            22 => Ok(Self::Lv22),
89            23 => Ok(Self::Lv23),
90            24 => Ok(Self::Lv24),
91            _ => Err(()),
92        }
93    }
94}
95
96impl TryFrom<u8> for ZoomLv
97{
98    type Error = ();
99
100    fn try_from(value: u8) -> Result<Self, Self::Error> {
101        Self::parse(value)
102    }
103}
104
105impl TryFrom<u16> for ZoomLv
106{
107    type Error = ();
108
109    fn try_from(value: u16) -> Result<Self, Self::Error> {
110        Self::parse(value)
111    }
112}
113
114impl TryFrom<u32> for ZoomLv
115{
116    type Error = ();
117
118    fn try_from(value: u32) -> Result<Self, Self::Error> {
119        Self::parse(value)
120    }
121}
122
123impl TryFrom<u64> for ZoomLv
124{
125    type Error = ();
126
127    fn try_from(value: u64) -> Result<Self, Self::Error> {
128        Self::parse(value)
129    }
130}
131
132impl TryFrom<usize> for ZoomLv
133{
134    type Error = ();
135
136    fn try_from(value: usize) -> Result<Self, Self::Error> {
137        Self::parse(value)
138    }
139}
140
141impl TryFrom<i8> for ZoomLv
142{
143    type Error = ();
144
145    fn try_from(value: i8) -> Result<Self, Self::Error> {
146        Self::parse(value)
147    }
148}
149
150impl TryFrom<i16> for ZoomLv
151{
152    type Error = ();
153
154    fn try_from(value: i16) -> Result<Self, Self::Error> {
155        Self::parse(value)
156    }
157}
158
159impl TryFrom<i32> for ZoomLv
160{
161    type Error = ();
162
163    fn try_from(value: i32) -> Result<Self, Self::Error> {
164        Self::parse(value)
165    }
166}
167
168impl TryFrom<i64> for ZoomLv
169{
170    type Error = ();
171
172    fn try_from(value: i64) -> Result<Self, Self::Error> {
173        Self::parse(value)
174    }
175}
176
177impl TryFrom<isize> for ZoomLv
178{
179    type Error = ();
180
181    fn try_from(value: isize) -> Result<Self, Self::Error> {
182        Self::parse(value)
183    }
184}
185
186impl FromStr for ZoomLv {
187    type Err = ();
188
189    fn from_str(s: &str) -> Result<Self, Self::Err> {
190        match s.parse::<u8>() {
191            Ok(num) => Self::parse(num),
192            Err(_) => Err(()),
193        }
194    }
195}
196
197/// Function to convert longitude and latitude to pixel coordinates.
198/// Converts (longitude, latitude) given by the arc degree method to pixel coordinates (x, y) according to Zoom level.
199///
200/// 緯度と経度をピクセル座標に変換する関数。
201/// 弧度法で与えられた(経度, 緯度)をZoomレベルに応じたピクセル座標(x, y)に変換する。
202///
203/// # Examples
204///
205/// Convert longitude and latitude to pixel coordinates.
206///
207/// 緯経度をピクセル座標に変換する。
208///
209/// ```
210/// use coordinate_transformer::pixel_ll::{ll2pixel, ZoomLv};
211///
212/// let (x, y) = ll2pixel(
213/// (139.7649308_f64.to_radians(), 35.6812405_f64.to_radians()),
214/// ZoomLv::Lv21,
215/// );
216/// ```
217pub fn ll2pixel(ll: (f64, f64), zoom: ZoomLv) -> (u32, u32) {
218    let (long, lat) = ll;
219    const L: f64 = 85.05112878;
220
221    let x = (2_f64.powf(zoom as i32 as f64 + 7.)) * (long / PI + 1.);
222    let y = (2_f64.powf(zoom as i32 as f64 + 7.) / PI)
223        * (-(lat.sin().atanh()) + (L * PI / 180.).sin().atanh());
224
225    (x as u32, y as u32)
226}
227
228/// Function to convert pixel coordinates to longitude and latitude.
229/// Converts pixel coordinates (x, y) according to Zoom level to (longitude, latitude) expressed in arc degree method.
230///
231/// ピクセル座標を緯度と経度に変換する関数。
232/// Zoomレベルに応じたピクセル座標(x, y)を弧度法で表された(経度, 緯度)に変換する。
233///
234/// # Examples
235///
236/// Convert pixel coordinates to longitude and latitude.
237///
238/// ピクセル座標を緯経度に変換する。
239///
240/// ```
241/// use coordinate_transformer::pixel_ll::{pixel2ll, ZoomLv};
242///
243/// let (long, lat) = pixel2ll((476868027, 211407949), ZoomLv::Lv21);
244/// ```
245pub fn pixel2ll(pixel: (u32, u32), zoom: ZoomLv) -> (f64, f64) {
246    let (x, y) = pixel;
247    const L: f64 = 85.05112878;
248
249    let long = PI * (x as f64 / 2_f64.powf(zoom as i32 as f64 + 7.) - 1.);
250    let lat = ((-PI * y as f64 / (2_f64.powf(zoom as i32 as f64 + 7.))
251        + (PI * L / 180.).sin().atanh())
252        .tanh())
253        .asin();
254
255    (long, lat)
256}
257
258/// Function to return the length per pixel (m) in pixel coordinates according to the latitude and Zoom level of the arc degree method.
259///
260/// 弧度法の緯度とZoomレベルに応じたピクセル座標における1ピクセルあたりの長さ(m)を返す関数。
261///
262/// # Examples
263///
264/// Calculate the length per pixel (m) in pixel coordinates according to the latitude and Zoom level of the arc degree method.
265///
266/// 弧度法の緯度とZoomレベルに応じたピクセル座標における1ピクセルあたりの長さ(m)を計算する。
267/// ```
268/// use coordinate_transformer::pixel_ll::{pixel_resolution, ZoomLv};
269///
270/// let resolution = pixel_resolution(0_f64.to_radians(), ZoomLv::Lv17);
271///```
272pub fn pixel_resolution(lat: f64, zoom: ZoomLv) -> f64 {
273    156543.04 * lat.cos() / 2_f64.powf(zoom as i32 as f64)
274}
275
276/// Function to convert pixel coordinates to tile coordinates.
277///
278/// ピクセル座標をタイル座標に変換する関数。
279///
280/// # Examples
281///
282/// Calculate tile coordinates from pixel coordinates.
283///
284/// ピクセル座標からタイル座標を計算する。
285///
286/// ```
287/// use coordinate_transformer::pixel_ll::pixel2tile;
288///
289/// let (tile_x, tile_y) = pixel2tile((476868027, 211407949));
290/// ```
291pub fn pixel2tile(pixel: (u32, u32)) -> (u32, u32) {
292    let (x, y) = pixel;
293    (x / 256, y / 256)
294}
295
296#[cfg(test)]
297mod tests {
298    use close_to::assert_close_to;
299
300    use super::*;
301
302    #[test]
303    fn ll2pixel_works() {
304        let (x, y) = ll2pixel(
305            (139.7649308_f64.to_radians(), (35.6812405_f64).to_radians()),
306            ZoomLv::Lv21,
307        );
308
309        assert_eq!((x, y), (476868027, 211407949));
310    }
311
312    #[test]
313    fn pixel2ll_works() {
314        let (long, lat) = pixel2ll((476868027, 211407949), ZoomLv::Lv21);
315
316        println!("{}, {}", long.to_degrees(), lat.to_degrees());
317
318        assert_close_to(139.7649308_f64.to_radians(), long, 5);
319        assert_close_to(35.6812405_f64.to_radians(), lat, 5);
320    }
321
322    #[test]
323    fn pixel_resolution_works() {
324        let equator_length_m = 40075_f64 * 1000_f64;
325        let zoom_lv = ZoomLv::Lv17;
326        let resolution = pixel_resolution(0_f64.to_radians(), zoom_lv);
327
328        assert_close_to(
329            resolution,
330            equator_length_m / (2_f64.powf(zoom_lv as i32 as f64) * 256.),
331            5,
332        );
333    }
334}