aboutsummaryrefslogtreecommitdiff
path: root/src/gpx.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpx.rs')
-rw-r--r--src/gpx.rs138
1 files changed, 0 insertions, 138 deletions
diff --git a/src/gpx.rs b/src/gpx.rs
deleted file mode 100644
index fb9e00e..0000000
--- a/src/gpx.rs
+++ /dev/null
@@ -1,138 +0,0 @@
-//! 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)).
-//!
-//! 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,
- fs::{self, File},
- io::{BufReader, Read},
- path::Path,
-};
-
-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,
- latitude: f64,
-}
-
-impl Coordinates {
- /// Calculates the [Web Mercator
- /// projection](https://en.wikipedia.org/wiki/Web_Mercator_projection) of the 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) * WIDTH * (lambda + PI);
- let y =
- 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)
- }
-}
-
-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"
-}
-
-/// 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)?;
- 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)
-}
-
-/// 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 {
- Some(Compression::Brotli)
- } else if [OsStr::new("gz"), OsStr::new("gzip")].contains(&ext) {
- Some(Compression::Gzip)
- } else if OsStr::new("gpx") == ext {
- Some(Compression::None)
- } else {
- None
- }
- }
-}
-
-/// 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,
-) -> Result<Vec<Coordinates>> {
- let content = match compression {
- Compression::None => fs::read_to_string(path)?,
- Compression::Gzip => {
- let mut result = String::new();
- GzDecoder::new(BufReader::new(File::open(path)?)).read_to_string(&mut result)?;
- result
- }
- Compression::Brotli => {
- let mut result = Vec::new();
- brotli::BrotliDecompress(&mut BufReader::new(File::open(path)?), &mut result)?;
- String::from_utf8(result)?
- }
- };
- extract_from_str(&content)
-}