diff options
Diffstat (limited to 'src/gpx.rs')
-rw-r--r-- | src/gpx.rs | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/src/gpx.rs b/src/gpx.rs new file mode 100644 index 0000000..7760ca5 --- /dev/null +++ b/src/gpx.rs @@ -0,0 +1,69 @@ +//! GPX data extraction functions. +//! +//! 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)). +use std::{f64::consts::PI, fs, path::Path}; + +use color_eyre::eyre::{eyre, Result}; +use roxmltree::{Document, Node, NodeType}; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Coordinates { + longitude: f64, + latitude: f64, +} + +impl Coordinates { + /// Calculates the [Web Mercator + /// projection](https://en.wikipedia.org/wiki/Web_Mercator_projection) of the coordinates. + /// Returns the `(x, y)` coordinates. + pub fn web_mercator(self, zoom: u32) -> (u64, u64) { + 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 y = 2u64.pow(zoom) as f64 / (2.0 * PI) * 256.0 * (PI - (PI / 4.0 + phi / 2.0).tan().ln()); + (x.floor() as u64, y.floor() as u64) + } +} + +fn is_track_node(node: &Node) -> bool { + node.node_type() == NodeType::Element && node.tag_name().name() == "trk" +} + +fn is_track_segment(node: &Node) -> bool { + node.node_type() == NodeType::Element && node.tag_name().name() == "trkseg" +} + +fn is_track_point(node: &Node) -> bool { + node.node_type() == NodeType::Element && node.tag_name().name() == "trkpt" +} + +pub fn extract_from_str(input: &str) -> Result<Vec<Coordinates>> { + let mut result = Vec::new(); + let document = Document::parse(input)?; + for node in document.root_element().children().filter(is_track_node) { + for segment in node.children().filter(is_track_segment) { + for point in segment.children().filter(is_track_point) { + let latitude = point.attribute("lat") + .and_then(|l| l.parse::<f64>().ok()) + .ok_or_else(|| eyre!("Invalid latitude"))?; + let longitude = point.attribute("lon") + .and_then(|l| l.parse::<f64>().ok()) + .ok_or_else(|| eyre!("Invalid longitude"))?; + result.push(Coordinates { latitude, longitude }); + } + } + } + Ok(result) +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Compression { + None, +} + +pub fn extract_from_file<P: AsRef<Path>>(path: P, _compression: Compression) -> Result<Vec<Coordinates>> { + let content = fs::read_to_string(path)?; + extract_from_str(&content) +} |