smhi/
get_closest_station.rs

1use crate::{Error, Gateway, Parameter, Station, BASE_URL};
2use serde::Deserialize;
3
4impl Gateway {
5    pub async fn get_closest_station<'a>(
6        &self,
7        latitude: f64,
8        longitude: f64,
9        parameter: &'a Parameter,
10        exclude: Option<&Vec<u32>>,
11    ) -> Result<(Station, f64), Error> {
12        #[derive(Deserialize)]
13        #[serde(rename_all = "camelCase")]
14        struct Response {
15            station: Vec<Station>,
16        }
17
18        //https://opendata-download-metobs.smhi.se/api/version/1.0/parameter/26.json
19
20        let url = format!(
21            "{}/parameter/{}.json",
22            BASE_URL,
23            serde_json::to_string(&parameter).unwrap()
24        );
25        let res: Response = self.get(&url).await?;
26
27        // Find closest.
28        let mut closest = None;
29        for station in res.station {
30            if station.active {
31                // Skip excluded stations.
32                if let Some(exclude) = exclude {
33                    if exclude.contains(&station.id) {
34                        continue;
35                    }
36                }
37
38                // NOTE: We are using flat earth approximation, as the distances are expected to be \
39                // small, otherwise the data from the station will not be relevant anyway.
40                let distance =
41                    distance_meters(latitude, longitude, station.latitude, station.longitude);
42                match &closest {
43                    Some((_, closest_distance)) => {
44                        if &distance < closest_distance {
45                            closest = Some((station.clone(), distance));
46                        }
47                    }
48                    None => closest = Some((station.clone(), distance)),
49                }
50            }
51        }
52
53        match closest {
54            Some((station, distance)) => Ok((station, distance)),
55            None => Err(Error::NotFound),
56        }
57    }
58}
59
60fn distance_meters(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 {
61    let x = deg2rad(lon1 - lon2) * f64::cos(deg2rad((lat1 + lat2) / 2.0));
62    let y = deg2rad(lat1 - lat2);
63    let dist = 6371000.0 * f64::sqrt(x * x + y * y);
64    return dist;
65}
66
67fn deg2rad(degrees: f64) -> f64 {
68    let pi = std::f64::consts::PI;
69    return degrees * (pi / 180.0);
70}