gistools/readers/gpx/
mod.rs

1/// 1.1 Specification for the GPX format
2pub mod spec;
3
4use crate::parsers::FeatureReader;
5use alloc::{string::String, vec::Vec};
6use s2json::{MValueCompatible, VectorFeature};
7pub use spec::*;
8
9/// Represents a route, which is an ordered list of waypoints leading to a destination.
10#[derive(Debug, Default, Clone, PartialEq, MValueCompatible)]
11pub struct GPXProperties {
12    /// Route name
13    pub name: Option<String>,
14    /// Route comment
15    pub cmt: Option<String>,
16    /// Route description
17    pub desc: Option<String>,
18    /// Source of data
19    pub src: Option<String>,
20    /// Links to external information
21    pub link: Option<Vec<GPXLink>>,
22    /// Route number
23    pub number: Option<usize>,
24    /// Classification type of the route
25    pub route_type: Option<String>,
26    /// Classification type of the track
27    pub track_type: Option<String>,
28}
29
30/// A GPX Shaped Vector Feature
31pub type GPXVectorFeature = VectorFeature<(), GPXProperties, GPXWaypoint>;
32
33/// # GPX Reader
34///
35/// ## Description
36/// The GPX Reader is an XML-based GPS Exchange Format (GPX) reader.
37///
38/// GPX (the GPS Exchange Format) is a light-weight XML data format for the interchange of GPS data
39/// (waypoints, routes, and tracks) between applications and Web services on the Internet.
40///
41/// Implements the [`FeatureReader`] trait
42///
43/// ## Usage
44///
45/// The methods you have access to:
46/// - [`GPXReader::new`]: Create a new GPXReader
47/// - [`GPXReader::metadata`]: Get the metadata
48/// - [`GPXReader::iter`]: Iterate over the features
49/// - [`GPXReader::par_iter`]: Iterate over the features in parallel
50///
51/// ```rust
52/// use gistools::{
53///     parsers::{FeatureReader, FileReader},
54///     readers::{GPXReader},
55/// };
56/// use std::path::PathBuf;
57///
58/// let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
59/// path.push("tests/readers/gpx/fixtures/gpx-test-short.gpx");
60/// let gpx_string = std::fs::read_to_string(path).unwrap();
61///
62/// let gpx_data = GPXReader::new(&gpx_string);
63///
64/// let features: Vec<_> = gpx_data.iter().collect();
65/// assert_eq!(features.len(), 4);
66/// ```
67///
68/// ## Links
69/// - <https://www.topografix.com/gpx.asp>
70#[derive(Debug, Clone)]
71pub struct GPXReader {
72    /// GPX object
73    pub gpx: GPX,
74}
75impl GPXReader {
76    /// Create a new GPX Reader
77    pub fn new(input: &str) -> Self {
78        Self { gpx: GPX::new(input) }
79    }
80    /// Grab the metadata
81    pub fn metadata(&self) -> GPXMetadata {
82        self.gpx.metadata.clone().unwrap_or_default()
83    }
84}
85/// The GPX Iterator tool
86#[derive(Debug)]
87pub struct GPXIterator<'a> {
88    reader: &'a GPXReader,
89    wpt_offset: usize,
90    wpt_count: usize,
91    rte_offset: usize,
92    rte_count: usize,
93    trk_offset: usize,
94    trk_count: usize,
95}
96impl Iterator for GPXIterator<'_> {
97    type Item = GPXVectorFeature;
98
99    fn next(&mut self) -> Option<Self::Item> {
100        let gpx = &self.reader.gpx;
101        if self.wpt_offset < self.wpt_count {
102            self.wpt_offset += 1;
103            return gpx.wpt.as_ref().map(|w| w[self.wpt_offset - 1].feature());
104        }
105        if self.rte_offset < self.rte_count {
106            self.rte_offset += 1;
107            return gpx.rte.as_ref().map(|w| w[self.rte_offset - 1].feature());
108        }
109        if self.trk_offset < self.trk_count {
110            self.trk_offset += 1;
111            return gpx.trk.as_ref().map(|w| w[self.trk_offset - 1].feature());
112        }
113        None
114    }
115}
116/// A feature reader trait with a callback-based approach
117impl FeatureReader<(), GPXProperties, GPXWaypoint> for GPXReader {
118    type FeatureIterator<'a> = GPXIterator<'a>;
119
120    fn iter(&self) -> Self::FeatureIterator<'_> {
121        GPXIterator {
122            reader: self,
123            wpt_offset: 0,
124            wpt_count: self.gpx.wpt.as_ref().map(|w| w.len()).unwrap_or_default(),
125            rte_offset: 0,
126            rte_count: self.gpx.rte.as_ref().map(|r| r.len()).unwrap_or_default(),
127            trk_offset: 0,
128            trk_count: self.gpx.trk.as_ref().map(|t| t.len()).unwrap_or_default(),
129        }
130    }
131
132    // GPX is so simple that there is no reason to make paralle work
133    fn par_iter(&self, pool_size: usize, thread_id: usize) -> Self::FeatureIterator<'_> {
134        let mut wpt_count = self.gpx.wpt.as_ref().map(|w| w.len()).unwrap_or_default();
135        let mut rte_count = self.gpx.rte.as_ref().map(|r| r.len()).unwrap_or_default();
136        let mut trk_count = self.gpx.trk.as_ref().map(|t| t.len()).unwrap_or_default();
137        let wpt_offset = wpt_count * thread_id / pool_size;
138        wpt_count = wpt_count * (thread_id + 1) / pool_size;
139        let rte_offset = rte_count * thread_id / pool_size;
140        rte_count = rte_count * (thread_id + 1) / pool_size;
141        let trk_offset = trk_count * thread_id / pool_size;
142        trk_count = trk_count * (thread_id + 1) / pool_size;
143        GPXIterator {
144            reader: self,
145            wpt_offset,
146            wpt_count,
147            rte_offset,
148            rte_count,
149            trk_offset,
150            trk_count,
151        }
152    }
153}