diff --git a/src/lib.rs b/src/lib.rs index 7dd3746..e04b4f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ //! It is thus advisable to pay careful attention to the order of the coordinates you use for encoding and decoding. use geo_types::{Coord, LineString}; -use std::{char, cmp}; +use std::char; const MIN_LONGITUDE: f64 = -180.0; const MAX_LONGITUDE: f64 = 180.0; @@ -35,31 +35,18 @@ fn scale(n: f64, factor: i32) -> i64 { scaled.round() as i64 } -// Bounds checking for input values -fn check(to_check: T, bounds: (T, T)) -> Result -where - T: cmp::PartialOrd + Copy, -{ - match to_check { - to_check if bounds.0 <= to_check && to_check <= bounds.1 => Ok(to_check), - _ => Err(to_check), - } -} - -fn encode(current: f64, previous: f64, factor: i32, output: &mut String) -> Result<(), String> { - let current = scale(current, factor); - let previous = scale(previous, factor); - let mut coordinate = (current - previous) << 1; - if (current - previous) < 0 { - coordinate = !coordinate; +fn encode(delta: i64, output: &mut String) -> Result<(), String> { + let mut value = delta << 1; + if value < 0 { + value = !value; } - while coordinate >= 0x20 { - let from_char = char::from_u32(((0x20 | (coordinate & 0x1f)) + 63) as u32) + while value >= 0x20 { + let from_char = char::from_u32(((0x20 | (value & 0x1f)) + 63) as u32) .ok_or("Couldn't convert character")?; output.push(from_char); - coordinate >>= 5; + value >>= 5; } - let from_char = char::from_u32((coordinate + 63) as u32).ok_or("Couldn't convert character")?; + let from_char = char::from_u32((value + 63) as u32).ok_or("Couldn't convert character")?; output.push(from_char); Ok(()) } @@ -83,16 +70,29 @@ where let factor: i32 = base.pow(precision); let mut output = String::new(); - let mut b = Coord { x: 0.0, y: 0.0 }; + let mut previous = Coord { x: 0, y: 0 }; + + for (i, next) in coordinates.into_iter().enumerate() { + if !(MIN_LATITUDE..MAX_LATITUDE).contains(&next.y) { + return Err(format!( + "Invalid latitude: {lat} at position {i}", + lat = next.y + )); + } + if !(MIN_LONGITUDE..MAX_LONGITUDE).contains(&next.x) { + return Err(format!( + "Invalid longitude: {lon} at position {i}", + lon = next.x + )); + } - for (i, a) in coordinates.into_iter().enumerate() { - check(a.y, (MIN_LATITUDE, MAX_LATITUDE)) - .map_err(|e| format!("Latitude error at position {0}: {1}", i, e))?; - check(a.x, (MIN_LONGITUDE, MAX_LONGITUDE)) - .map_err(|e| format!("Longitude error at position {0}: {1}", i, e))?; - encode(a.y, b.y, factor, &mut output)?; - encode(a.x, b.x, factor, &mut output)?; - b = a; + let scaled_next = Coord { + x: scale(next.x, factor), + y: scale(next.y, factor), + }; + encode(scaled_next.y - previous.y, &mut output)?; + encode(scaled_next.x - previous.x, &mut output)?; + previous = scaled_next; } Ok(output) }