diff options
Diffstat (limited to 'src/gpx.rs')
-rw-r--r-- | src/gpx.rs | 30 |
1 files changed, 27 insertions, 3 deletions
@@ -3,6 +3,9 @@ //! We *could* use the [gpx](https://github.com/georust/gpx) crate, but we don't care about much //! other than the coordinates of the tracks. By implementing the little functionality ourselves, //! we can use a fast XML parser ([roxmltree](https://github.com/RazrFalcon/roxmltree)). +//! +//! Note that we throw away all information that we don't care about. Since we need only the +//! coordinates of a track, we simply use a `Vec<Coordinates>` to represent a track. use std::{ f64::consts::PI, ffi::OsStr, @@ -15,6 +18,7 @@ use color_eyre::eyre::{eyre, Result}; use flate2::bufread::GzDecoder; use roxmltree::{Document, Node, NodeType}; +/// World coordinates. #[derive(Debug, Copy, Clone, PartialEq)] pub struct Coordinates { longitude: f64, @@ -24,13 +28,17 @@ pub struct Coordinates { impl Coordinates { /// Calculates the [Web Mercator /// projection](https://en.wikipedia.org/wiki/Web_Mercator_projection) of the coordinates. - /// Returns the `(x, y)` coordinates. + /// + /// Returns the `(x, y)` projection, where both are in the range `[0, 256 * 2^zoom)`. pub fn web_mercator(self, zoom: u32) -> (u64, u64) { + const WIDTH: f64 = super::layer::TILE_WIDTH as f64; + const HEIGHT: f64 = super::layer::TILE_HEIGHT as f64; + let lambda = self.longitude.to_radians(); let phi = self.latitude.to_radians(); - let x = 2u64.pow(zoom) as f64 / (2.0 * PI) * 256.0 * (lambda + PI); + let x = 2u64.pow(zoom) as f64 / (2.0 * PI) * WIDTH * (lambda + PI); let y = - 2u64.pow(zoom) as f64 / (2.0 * PI) * 256.0 * (PI - (PI / 4.0 + phi / 2.0).tan().ln()); + 2u64.pow(zoom) as f64 / (2.0 * PI) * HEIGHT * (PI - (PI / 4.0 + phi / 2.0).tan().ln()); (x.floor() as u64, y.floor() as u64) } } @@ -47,6 +55,7 @@ fn is_track_point(node: &Node) -> bool { node.node_type() == NodeType::Element && node.tag_name().name() == "trkpt" } +/// Extracts a track from the given string. pub fn extract_from_str(input: &str) -> Result<Vec<Coordinates>> { let mut result = Vec::new(); let document = Document::parse(input)?; @@ -71,14 +80,26 @@ pub fn extract_from_str(input: &str) -> Result<Vec<Coordinates>> { Ok(result) } +/// Compression format of the data. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Compression { + /// Indicates that no compression is applied, and the file is plain GPX. None, + /// Indicates that the file is gzip compressed. Gzip, + /// Indicates that the file is brotli compressed. Brotli, } impl Compression { + /// Suggests a [`Compression`] from the given path name. + /// + /// This will suggest [`Compression::Brotli`] for files ending in `.br`, [`Compression::Gzip`] + /// for files ending with `.gz` or `.gzip`, and [`Compression::None`] for files ending with + /// `.gpx`. + /// + /// If the file does not end with any of the aforementioned extensions, an error is returned + /// instead. pub fn suggest_from_path<P: AsRef<Path>>(path: P) -> Option<Compression> { let Some(ext) = path.as_ref().extension() else { return None }; if OsStr::new("br") == ext { @@ -93,6 +114,9 @@ impl Compression { } } +/// Extracts the relevant GPX data from the given file. +/// +/// Note that the content must be valid UTF-8, as that is what our parser expects. pub fn extract_from_file<P: AsRef<Path>>( path: P, compression: Compression, |