diff --git a/src/codes_handle/iterator.rs b/src/codes_handle/iterator.rs index cd4a4e3..9bc7caa 100644 --- a/src/codes_handle/iterator.rs +++ b/src/codes_handle/iterator.rs @@ -283,12 +283,12 @@ mod tests { // Now unwrap and access the first and only element of resulting vector // Find nearest modifies internal KeyedMessage fields so we need mutable reference - let level = &mut level[0]; + let level = &level[0]; println!("{:?}", level.read_key("shortName")); // Get the four nearest gridpoints of Reykjavik - let nearest_gridpoints = level.find_nearest(64.13, -21.89).unwrap(); + let nearest_gridpoints = level.codes_nearest()?.find_nearest(64.13, -21.89).unwrap(); // Print value and distance of the nearest gridpoint println!( diff --git a/src/codes_handle/keyed_message/mod.rs b/src/codes_handle/keyed_message/mod.rs index 11df9e5..31019c4 100644 --- a/src/codes_handle/keyed_message/mod.rs +++ b/src/codes_handle/keyed_message/mod.rs @@ -1,8 +1,8 @@ mod iterator; +mod nearest; mod read; mod write; -use eccodes_sys::codes_nearest; use log::warn; use std::ptr::null_mut; @@ -10,74 +10,20 @@ use crate::{ codes_handle::KeyedMessage, errors::CodesError, intermediate_bindings::{ - codes_grib_nearest_delete, codes_grib_nearest_find, codes_grib_nearest_new, - codes_handle_clone, codes_handle_delete, codes_keys_iterator_delete, + codes_grib_nearest_new, codes_handle_clone, codes_handle_delete, codes_keys_iterator_delete, }, }; -use super::{KeysIteratorFlags, NearestGridpoint}; +use super::{CodesNearest, KeysIteratorFlags}; impl KeyedMessage { - fn nearest_handle(&mut self) -> Result<*mut codes_nearest, CodesError> { - if let Some(nrst) = self.nearest_handle { - Ok(nrst) - } else { - let nrst; + pub fn codes_nearest(&self) -> Result { + let nearest_handle = unsafe { codes_grib_nearest_new(self.message_handle)? }; - unsafe { - nrst = codes_grib_nearest_new(self.message_handle)?; - } - - self.nearest_handle = Some(nrst); - - Ok(nrst) - } - } - - ///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. - /// - ///In the output gridpoints, the value field refers to parameter held by the `KeyedMessage` - ///for which the function is called in adequate units, - ///coordinates are in degrees north/east, - ///and distance field represents the distance between requested point and output point in kilometers. - /// - ///### Example - /// - ///``` - ///# use eccodes::codes_handle::{ProductKind, CodesHandle, KeyedMessage, KeysIteratorFlags}; - ///# use std::path::Path; - ///# use eccodes::codes_handle::KeyType::Str; - ///# use eccodes::FallibleIterator; - ///let file_path = Path::new("./data/iceland.grib"); - ///let product_kind = ProductKind::GRIB; - /// - ///let mut handle = CodesHandle::new_from_file(file_path, product_kind).unwrap(); - ///let mut msg = handle.next().unwrap().unwrap(); - /// - /// - ///let out = msg.find_nearest(64.13, -21.89).unwrap(); - ///``` - /// - ///### Errors - /// - ///This function returns [`CodesInternal`](crate::errors::CodesInternal) when - ///one of ecCodes function returns the non-zero code. - pub fn find_nearest( - &mut self, - lat: f64, - lon: f64, - ) -> Result<[NearestGridpoint; 4], CodesError> { - let nrst = self.nearest_handle()?; - let output_points; - - unsafe { - output_points = codes_grib_nearest_find(self.message_handle, nrst, lat, lon)?; - } - - Ok(output_points) + Ok(CodesNearest { + nearest_handle, + parent_message: self, + }) } } @@ -115,19 +61,6 @@ impl Drop for KeyedMessage { ///In case of corrupt pointer segmentation fault will occur. ///The pointers are cleared at the end of drop as they are not functional despite the result of delete functions. fn drop(&mut self) { - if let Some(nrst) = self.nearest_handle { - unsafe { - codes_grib_nearest_delete(nrst).unwrap_or_else(|error| { - warn!( - "codes_grib_nearest_delete() returned an error: {:?}", - &error - ); - }); - } - } - - self.nearest_handle = Some(null_mut()); - if let Some(kiter) = self.keys_iterator { unsafe { codes_keys_iterator_delete(kiter).unwrap_or_else(|error| { @@ -220,27 +153,4 @@ mod tests { Ok(()) } - - #[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).unwrap(); - let msg1 = handle1.next()?.unwrap(); - let out1 = msg1.clone().find_nearest(64.13, -21.89).unwrap(); - - let mut handle2 = CodesHandle::new_from_file(file_path2, product_kind).unwrap(); - let msg2 = handle2.next()?.unwrap(); - let out2 = msg2.clone().find_nearest(64.13, -21.89).unwrap(); - - 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(()) - } } diff --git a/src/codes_handle/keyed_message/nearest.rs b/src/codes_handle/keyed_message/nearest.rs new file mode 100644 index 0000000..c51efa2 --- /dev/null +++ b/src/codes_handle/keyed_message/nearest.rs @@ -0,0 +1,107 @@ +use std::ptr::null_mut; + +use log::warn; + +use crate::{ + codes_handle::CodesNearest, + intermediate_bindings::{codes_grib_nearest_delete, codes_grib_nearest_find}, + CodesError, NearestGridpoint, +}; + +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. + /// + ///In the output gridpoints, the value field refers to parameter held by the `KeyedMessage` + ///for which the function is called in adequate units, + ///coordinates are in degrees north/east, + ///and distance field represents the distance between requested point and output point in kilometers. + /// + ///### Example + /// + ///``` + ///# use eccodes::codes_handle::{ProductKind, CodesHandle, KeyedMessage, KeysIteratorFlags}; + ///# use std::path::Path; + ///# use eccodes::codes_handle::KeyType::Str; + ///# use eccodes::FallibleIterator; + ///let file_path = Path::new("./data/iceland.grib"); + ///let product_kind = ProductKind::GRIB; + /// + ///let mut handle = CodesHandle::new_from_file(file_path, product_kind).unwrap(); + ///let mut msg = handle.next().unwrap().unwrap(); + /// + /// + ///let out = msg.find_nearest(64.13, -21.89).unwrap(); + ///``` + /// + ///### 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) + } +} + +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::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).unwrap(); + let msg1 = handle1.next()?.unwrap(); + 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).unwrap(); + let msg2 = handle2.next()?.unwrap(); + 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(()) + } +} diff --git a/src/codes_handle/mod.rs b/src/codes_handle/mod.rs index df33089..627b6be 100644 --- a/src/codes_handle/mod.rs +++ b/src/codes_handle/mod.rs @@ -129,6 +129,12 @@ pub enum ProductKind { GRIB = ProductKind_PRODUCT_GRIB as isize, } +#[derive(Debug)] +pub struct CodesNearest<'a> { + nearest_handle: *mut codes_nearest, + parent_message: &'a KeyedMessage, +} + ///The structure returned by [`KeyedMessage::find_nearest()`]. ///Should always be analysed in relation to the coordinates request in `find_nearest()`. #[derive(Copy, Clone, PartialEq, Debug, Default)]