utiles_core/gdal.rs
1//! GDAL related functions/utils
2
3/// A geotransform is an affine transformation from the image coordinate space
4/// (row, column), also known as (pixel, line) to the georeferenced coordinate
5/// space (projected or geographic coordinates).
6///
7/// A geotransform consists in a set of 6 coefficients:
8///
9/// GT(0) x-coordinate of the upper-left corner of the upper-left pixel.
10/// GT(1) w-e pixel resolution / pixel width.
11/// GT(2) row rotation (typically zero).
12/// GT(3) y-coordinate of the upper-left corner of the upper-left pixel.
13/// GT(4) column rotation (typically zero).
14/// GT(5) n-s pixel resolution / pixel height (negative value for a north-up image).
15pub struct GeoTransform {
16 /// x-coordinate of the upper-left corner of the upper-left pixel.
17 pub gt0: f64,
18 /// w-e pixel resolution / pixel width.
19 pub gt1: f64,
20 /// row rotation (typically zero).
21 pub gt2: f64,
22 /// y-coordinate of the upper-left corner of the upper-left pixel.
23 pub gt3: f64,
24 /// column rotation (typically zero).
25 pub gt4: f64,
26 /// n-s pixel resolution / pixel height (negative value for a north-up image).
27 pub gt5: f64,
28}
29
30impl GeoTransform {
31 /// Create a new `GeoTransform` with given coefficients.
32 #[must_use]
33 pub const fn new(
34 gt0: f64,
35 gt1: f64,
36 gt2: f64,
37 gt3: f64,
38 gt4: f64,
39 gt5: f64,
40 ) -> Self {
41 Self {
42 gt0,
43 gt1,
44 gt2,
45 gt3,
46 gt4,
47 gt5,
48 }
49 }
50
51 /// Compute the optimal zoom level for a geo-transform.
52 #[expect(clippy::cast_sign_loss)]
53 #[expect(clippy::cast_possible_truncation)]
54 #[must_use]
55 pub fn optzoom(&self) -> u8 {
56 let equator = 2.0 * std::f64::consts::PI * 6_378_137.0; // 2 * pi * radius of earth in meters
57 let resolution = self.gt1 * (equator / 360.0);
58 let zoom_level = (equator / 256.0) / resolution; // Assuming pixel_size is 256 as in the previous example
59 (zoom_level.log2().min(20.0).floor() + 0.5) as u8
60 }
61}
62
63impl From<(f64, f64, f64, f64, f64, f64)> for GeoTransform {
64 fn from(gt: (f64, f64, f64, f64, f64, f64)) -> Self {
65 Self::new(gt.0, gt.1, gt.2, gt.3, gt.4, gt.5)
66 }
67}
68
69/// Compute the optimal zoom level for a geo-transform.
70#[must_use]
71pub fn geotransform2optzoom(geotransform: (f64, f64, f64, f64, f64, f64)) -> u8 {
72 let gt = GeoTransform::from(geotransform);
73 gt.optzoom()
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_geotransform2optzoom() {
82 let optz = geotransform2optzoom((
83 -77.000_138,
84 0.000_278,
85 0.0,
86 26.000_138_9,
87 0.0,
88 -0.000_278,
89 ));
90 assert_eq!(optz, 12);
91 }
92}