1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Definition and associated functions of `CodesNearest`
//! used for finding nearest gridpoints in `KeyedMessage`

use std::ptr::null_mut;

use eccodes_sys::codes_nearest;
use log::warn;

use crate::{
    intermediate_bindings::{
        codes_grib_nearest_delete, codes_grib_nearest_find, codes_grib_nearest_new,
    },
    CodesError, KeyedMessage,
};

/// The structure used to find nearest gridpoints in `KeyedMessage`.
#[derive(Debug)]
pub struct CodesNearest<'a> {
    nearest_handle: *mut codes_nearest,
    parent_message: &'a KeyedMessage,
}

/// The structure returned by [`CodesNearest::find_nearest()`].
/// Should always be analysed in relation to the coordinates requested in `find_nearest()`.
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct NearestGridpoint {
    ///Index of this gridpoint
    pub index: i32,
    ///Latitude of this gridpoint in degrees north
    pub lat: f64,
    ///Longitude of this gridpoint in degrees east
    pub lon: f64,
    /// Distance between requested point and this gridpoint in kilometers
    pub distance: f64,
    ///Value of parameter at this gridpoint contained by `KeyedMessage` in corresponding units
    pub value: f64,
}

impl KeyedMessage {
    /// Creates a new instance of [`CodesNearest`] for the `KeyedMessage`.
    /// [`CodesNearest`] can be used to find nearest gridpoints for given coordinates in the `KeyedMessage`
    /// by calling [`find_nearest()`](crate::CodesNearest::find_nearest).
    /// 
    /// # Errors
    /// 
    /// This function returns [`CodesInternal`](crate::errors::CodesInternal) when
    /// internal nearest handle cannot be created.
    pub fn codes_nearest(&self) -> Result<CodesNearest, CodesError> {
        let nearest_handle = unsafe { codes_grib_nearest_new(self.message_handle)? };

        Ok(CodesNearest {
            nearest_handle,
            parent_message: self,
        })
    }
}

impl CodesNearest<'_> {
    ///Function to get four [`NearestGridpoint`]s of a point represented by requested coordinates.
    ///
    ///The inputs are latitude and longitude of requested point in respectively degrees north and
    ///degreed east.
    ///
    ///### Example
    ///
    ///```
    ///  use eccodes::{ProductKind, CodesHandle, KeyedMessage, KeysIteratorFlags};
    /// # use std::path::Path;
    /// use eccodes::FallibleStreamingIterator;
    /// # use anyhow::Context;
    /// # fn main() -> anyhow::Result<()> {
    /// let file_path = Path::new("./data/iceland.grib");
    /// let product_kind = ProductKind::GRIB;
    ///
    /// let mut handle = CodesHandle::new_from_file(file_path, product_kind)?;
    /// let msg = handle.next()?.context("no message")?;
    ///
    /// let c_nearest = msg.codes_nearest()?;
    /// let out = c_nearest.find_nearest(64.13, -21.89)?;
    /// # Ok(())
    /// # }
    ///```
    ///
    ///### Errors
    ///
    ///This function returns [`CodesInternal`](crate::errors::CodesInternal) when
    ///one of ecCodes function returns the non-zero code.
    pub fn find_nearest(&self, lat: f64, lon: f64) -> Result<[NearestGridpoint; 4], CodesError> {
        let output_points;

        unsafe {
            output_points = codes_grib_nearest_find(
                self.parent_message.message_handle,
                self.nearest_handle,
                lat,
                lon,
            )?;
        }

        Ok(output_points)
    }
}

#[doc(hidden)]
impl Drop for CodesNearest<'_> {
    fn drop(&mut self) {
        unsafe {
            codes_grib_nearest_delete(self.nearest_handle).unwrap_or_else(|error| {
                warn!(
                    "codes_grib_nearest_delete() returned an error: {:?}",
                    &error
                );
            });
        }

        self.nearest_handle = null_mut();
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;

    use anyhow::{Context, Result};
    use fallible_streaming_iterator::FallibleStreamingIterator;

    use crate::{CodesHandle, ProductKind};

    #[test]
    fn find_nearest() -> Result<()> {
        let file_path1 = Path::new("./data/iceland.grib");
        let file_path2 = Path::new("./data/iceland-surface.grib");
        let product_kind = ProductKind::GRIB;

        let mut handle1 = CodesHandle::new_from_file(file_path1, product_kind)?;
        let msg1 = handle1.next()?.context("Message not some")?;
        let nrst1 = msg1.codes_nearest()?;
        let out1 = nrst1.find_nearest(64.13, -21.89)?;

        let mut handle2 = CodesHandle::new_from_file(file_path2, product_kind)?;
        let msg2 = handle2.next()?.context("Message not some")?;
        let nrst2 = msg2.codes_nearest()?;
        let out2 = nrst2.find_nearest(64.13, -21.89)?;

        assert!(out1[0].value > 10000.0);
        assert!(out2[3].index == 551);
        assert!(out1[1].lat == 64.0);
        assert!(out2[2].lon == -21.75);
        assert!(out1[0].distance > 15.0);

        Ok(())
    }
}