diff --git a/terrain/src/math.rs b/terrain/src/math.rs index b99e011..5578be5 100644 --- a/terrain/src/math.rs +++ b/terrain/src/math.rs @@ -5,37 +5,33 @@ use crate::constants::MEAN_EARTH_RADIUS; use geo::{CoordFloat, Point}; -use num_traits::{Float, FloatConst, FromPrimitive}; +use num_traits::{AsPrimitive, Float, FloatConst, FromPrimitive}; pub struct HaversineIter { - start: Option>, - end: Option>, params: HaversineParams, step_size_m: T, - interval: T, - current_step: T, + total_points: usize, + current_point: usize, + inverse: T, } impl HaversineIter { pub fn new(start: Point, max_step_size: T, end: Point) -> Self where - T: FromPrimitive, + T: FromPrimitive + AsPrimitive, { let params = get_params(&start, &end); let HaversineParams { d, .. } = params; let total_distance = d * T::from(MEAN_EARTH_RADIUS).unwrap(); let number_of_points = (total_distance / max_step_size).ceil(); let step_size_m = total_distance / number_of_points; - let interval = T::one() / number_of_points; - let current_step = T::zero(); Self { - start: Some(start), - end: Some(end), params, step_size_m, - interval, - current_step, + total_points: number_of_points.as_() + 1, + current_point: 0, + inverse: T::one() / number_of_points, } } @@ -49,17 +45,25 @@ impl Iterator for HaversineIter { type Item = Point; fn next(&mut self) -> Option { - if self.start.is_some() { - self.current_step = self.current_step + self.interval; - self.start.take() - } else if self.current_step < T::one() { - let ret = Some(get_point(&self.params, self.current_step)); - self.current_step = self.current_step + self.interval; - ret + if self.current_point < self.total_points { + let factor = T::from(self.current_point).unwrap() * self.inverse; + self.current_point += 1; + Some(get_point(&self.params, factor)) } else { - self.end.take() + None } } + + fn size_hint(&self) -> (usize, Option) { + let remaining = self.total_points - self.current_point; + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for HaversineIter { + fn len(&self) -> usize { + self.total_points - self.current_point + } } #[allow(clippy::many_single_char_names)] @@ -201,19 +205,20 @@ mod tests { let end = point!(x: 0.5, y: 0.5); let step_size_m = 17472.510284442324; let haversine = HaversineIter::new(start, step_size_m, end); + assert_eq!(haversine.len(), 10); assert_eq!(haversine.step_size_m(), step_size_m); let points = haversine.collect::>(); let expected = vec![ - point!(x:-0.5,y:-0.5), - point!(x:-0.38888498879915234,y:-0.3888908388952553), - point!(x:-0.2777729026876084,y:-0.2777802152664852), - point!(x:-0.1666629058941368,y:-0.16666854700519793), - point!(x:-0.05555416267893612,y:-0.055556251975400386), - point!(x:0.05555416267893612,y:0.055556251975400386), - point!(x:0.1666629058941368,y:0.16666854700519793), - point!(x:0.2777729026876085,y:0.27778021526648533), - point!(x:0.38888498879915245,y:0.3888908388952555), - point!(x:0.5,y:0.5), + point!(x: -0.5, y: -0.5), + point!(x: -0.38888498879915234, y: -0.3888908388952553), + point!(x: -0.2777729026876084, y: -0.2777802152664852), + point!(x: -0.1666629058941368, y: -0.16666854700519793), + point!(x: -0.05555416267893612, y: -0.055556251975400386), + point!(x: 0.05555416267893612, y: 0.055556251975400386), + point!(x: 0.1666629058941367, y: 0.16666854700519784), + point!(x: 0.27777290268760824, y: 0.2777802152664851), + point!(x: 0.3888849887991523, y: 0.3888908388952552), + point!(x: 0.5, y: 0.5), ]; assert_eq!(points, expected); } diff --git a/terrain/src/profile.rs b/terrain/src/profile.rs index 31743b3..80122cf 100644 --- a/terrain/src/profile.rs +++ b/terrain/src/profile.rs @@ -8,7 +8,7 @@ use geo::{ CoordFloat, }; use log::debug; -use num_traits::{FloatConst, FromPrimitive}; +use num_traits::{AsPrimitive, FloatConst, FromPrimitive}; #[derive(Debug, Clone, PartialEq)] pub struct Profile { @@ -120,7 +120,7 @@ where pub fn build(&self, tiles: &Tiles) -> Result, TerrainError> where - C: Atan2 + FloatConst, + C: Atan2 + FloatConst + AsPrimitive, { let start = self.start.ok_or(TerrainError::Builder("start"))?; let max_step_m = self.max_step_m.ok_or(TerrainError::Builder("max_step"))?;