irox_gpx/
lib.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2023 IROX Contributors
3
4#![forbid(unsafe_code)]
5
6use std::fmt::{Display, Formatter};
7
8use irox_carto::altitude::Altitude;
9use irox_carto::coordinate::{EllipticalCoordinate, Latitude, Longitude};
10use irox_carto::gps::DilutionOfPrecision;
11use irox_time::datetime::UTCDateTime;
12use irox_time::epoch::UnixTimestamp;
13use irox_units::units::angle::Angle;
14use irox_units::units::duration::Duration;
15pub use writer::*;
16
17pub mod error;
18mod reader;
19mod writer;
20
21pub const NAMESPACE: &str = "http://www.topografix.com/GPX/1/1";
22
23///
24/// Main top-level file element
25///
26/// GPX documents contain a metadata header, followed by waypoints, routes, and
27/// tracks.  You can add your own elements to the extensions section of the GPX
28/// document.
29pub struct GPX {
30    /// Metadata about the file.
31    pub metadata: Option<Metadata>,
32
33    /// A list of waypoints.
34    pub wpt: Vec<Waypoint>,
35
36    /// A list of routes.
37    pub rte: Vec<Route>,
38
39    /// A list of tracks.
40    pub trk: Vec<Track>,
41
42    /// You can add extend GPX by adding your own elements from another schema
43    /// here.
44    pub extensions: Option<Extensions>,
45
46    /// You must include the version number in your GPX document.
47    pub version: String,
48
49    /// You must include the name or URL of the software that created your GPX
50    /// document.  This allows others to inform the creator of a GPX instance
51    /// document that fails to validate.
52    pub creator: String,
53}
54impl Default for GPX {
55    fn default() -> Self {
56        GPX::new()
57    }
58}
59impl GPX {
60    pub fn new() -> GPX {
61        GPX {
62            metadata: None,
63            wpt: vec![],
64            rte: vec![],
65            trk: vec![],
66            extensions: None,
67            version: "1.1".to_string(),
68            creator: "irox-gpx https://github.com/spmadden/irox".to_string(),
69        }
70    }
71}
72
73///
74/// Information about the GPX file, author, and copyright restrictions goes in
75/// the metadata section.  Providing rich, meaningful information about your
76/// GPX files allows others to search for and use your GPS data.
77pub struct Metadata {
78    /// The name of the GPX file.
79    pub name: Option<String>,
80
81    /// A description of the contents of the GPX file.
82    pub desc: Option<String>,
83
84    /// The person or organization who created the GPX file.
85    pub author: Option<Person>,
86
87    /// Copyright and license information governing use of the file.
88    pub copyright: Option<Copyright>,
89
90    /// URLs associated with the location described in the file.
91    pub link: Vec<Link>,
92
93    /// The creation date of the file.
94    pub time: Option<UnixTimestamp>,
95
96    /// Keywords associated with the file.  Search engines or databases can use
97    /// this information to classify the data.
98    pub keywords: Vec<String>,
99
100    /// Minimum and maximum coordinates which describe the extent of the
101    /// coordinates in the file.
102    pub bounds: Option<Bounds>,
103
104    /// You can add extend GPX by adding your own elements from another schema
105    /// here.
106    pub extensions: Option<Extensions>,
107}
108
109///
110/// wpt represents a waypoint, point of interest, or named feature on a map.
111pub struct Waypoint {
112    /// Elevation (in meters) of the point.
113    pub ele: Option<f64>,
114
115    /// Creation/modification timestamp for element. Date and time in are in
116    /// Univeral Coordinated Time (UTC), not local time! Conforms to ISO 8601
117    /// specification for date/time representation. Fractional seconds are
118    /// allowed for millisecond timing in tracklogs.
119    pub time: Option<UTCDateTime>,
120
121    /// Magnetic variation (in degrees) at the point
122    pub magvar: Option<Angle>,
123
124    /// Height (in meters) of geoid (mean sea level) above WGS84 earth
125    /// ellipsoid.  As defined in NMEA GGA message.
126    pub geoidheight: Option<Altitude>,
127
128    /// The GPS name of the waypoint. This field will be transferred to and
129    /// from the GPS. GPX does not place restrictions on the length of this
130    /// field or the characters contained in it. It is up to the receiving
131    /// application to validate the field before sending it to the GPS.
132    pub name: Option<String>,
133
134    /// GPS waypoint comment. Sent to GPS as comment.
135    pub cmt: Option<String>,
136
137    /// A text description of the element. Holds additional information about
138    /// the element intended for the user, not the GPS.
139    pub desc: Option<String>,
140
141    /// Source of data. Included to give user some idea of reliability and
142    /// accuracy of data.  "Garmin eTrex", "USGS quad Boston North", e.g.
143    pub src: Option<String>,
144
145    /// Link to additional information about the waypoint.
146    pub link: Vec<Link>,
147
148    /// Text of GPS symbol name. For interchange with other programs, use the
149    /// exact spelling of the symbol as displayed on the GPS.  If the GPS
150    /// abbreviates words, spell them out.
151    pub sym: Option<String>,
152
153    /// Type (classification) of the waypoint.
154    pub wpt_type: Option<String>,
155
156    /// Type of GPX fix.
157    pub fix: Option<Fix>,
158
159    /// Number of satellites used to calculate the GPX fix.
160    pub sat: Option<u32>,
161
162    /// Horizontal dilution of precision.
163    pub hdop: Option<DilutionOfPrecision>,
164
165    /// Vertical dilution of precision.
166    pub vdop: Option<DilutionOfPrecision>,
167
168    /// Position dilution of precision.
169    pub pdop: Option<DilutionOfPrecision>,
170
171    /// Number of seconds since last DGPS update.
172    pub ageofdgpsdata: Option<Duration>,
173
174    /// ID of DGPS station used in differential correction.
175    pub dgpsid: Option<DGPSStationType>,
176
177    /// You can add extend GPX by adding your own elements from another schema
178    /// here.
179    pub extensions: Option<Extensions>,
180
181    /// The latitude of the point.  This is always in decimal degrees, and
182    /// always in WGS84 datum.
183    pub lat: Latitude,
184
185    /// The longitude of the point.  This is always in decimal degrees, and
186    /// always in WGS84 datum.
187    pub lon: Longitude,
188}
189impl Waypoint {
190    pub fn new(lat: Latitude, lon: Longitude) -> Waypoint {
191        Waypoint {
192            ele: None,
193            time: None,
194            magvar: None,
195            geoidheight: None,
196            name: None,
197            cmt: None,
198            desc: None,
199            src: None,
200            link: vec![],
201            sym: None,
202            wpt_type: None,
203            fix: None,
204            sat: None,
205            hdop: None,
206            vdop: None,
207            pdop: None,
208            ageofdgpsdata: None,
209            dgpsid: None,
210            extensions: None,
211            lat,
212            lon,
213        }
214    }
215}
216impl From<EllipticalCoordinate> for Waypoint {
217    fn from(value: EllipticalCoordinate) -> Self {
218        let mut wpt = Waypoint::new(*value.get_latitude(), *value.get_longitude());
219        wpt.time = *value.get_timestamp();
220        wpt.ele = value.get_altitude().map(|v| v.value().as_meters().value());
221
222        wpt
223    }
224}
225
226///
227/// rte represents route - an ordered list of waypoints representing a series of
228/// turn points leading to a destination.
229pub struct Route {
230    /// GPS name of route.
231    pub name: Option<String>,
232
233    /// GPS comment for route.
234    pub cmt: Option<String>,
235
236    /// Text description of route for user.  Not sent to GPS.
237    pub desc: Option<String>,
238
239    /// Source of data. Included to give user some idea of reliability and
240    /// accuracy of data.
241    pub src: Option<String>,
242
243    /// Links to external information about the route.
244    pub link: Vec<Link>,
245
246    /// GPS route number.
247    pub number: Option<u32>,
248
249    /// Type (classification) of route.
250    pub rte_type: Option<String>,
251
252    /// You can add extend GPX by adding your own elements from another schema
253    /// here.
254    pub extensions: Option<Extensions>,
255
256    /// A list of route points
257    pub waypoints: Vec<Waypoint>,
258}
259
260#[derive(Default)]
261pub struct Track {
262    /// GPS name of track.
263    pub name: Option<String>,
264
265    /// GPS comment for track.
266    pub cmt: Option<String>,
267
268    /// Text description of track for user.  Not sent to GPS.
269    pub desc: Option<String>,
270
271    /// Source of data. Included to give user some idea of reliability and
272    /// accuracy of data.
273    pub src: Option<String>,
274
275    /// Links to external information about the track.
276    pub link: Vec<Link>,
277
278    /// GPS track number.
279    pub number: Option<u32>,
280
281    /// Type (classification) of track.
282    pub trk_type: Option<String>,
283
284    /// You can add extend GPX by adding your own elements from another schema
285    /// here.
286    pub extensions: Option<Extensions>,
287
288    /// A Track Segment holds a list of Track Points which are logically
289    /// connected in order. To represent a single GPS track where GPS reception
290    /// was lost, or the GPS receiver was turned off, start a new Track Segment
291    /// for each continuous span of track data.
292    pub trkseg: Vec<TrackSegment>,
293}
294
295impl Track {
296    pub fn new() -> Track {
297        Track::default()
298    }
299}
300
301pub struct Extensions;
302
303/// A person or organization.
304pub struct Person {
305    /// Name of person or organization.
306    pub name: Option<String>,
307
308    /// Email address.
309    pub email: Option<Email>,
310
311    /// Link to Web site or other external information about person.
312    pub link: Option<Link>,
313}
314
315/// An email address.  Broken into two parts (id and domain) to help prevent
316/// email harvesting.
317pub struct Email {
318    /// id half of email address (billgates2004)
319    pub id: String,
320
321    /// domain half of email address (hotmail.com)
322    pub domain: String,
323}
324
325/// Information about the copyright holder and any license governing use of
326/// this file.  By linking to an appropriate license, you may place your data
327/// into the public domain or grant additional usage rights.
328pub struct Copyright {
329    /// Year of copyright.
330    pub year: Option<u16>,
331
332    /// Link to external file containing license text.
333    pub license: Option<String>,
334
335    /// Copyright holder (TopoSoft, Inc.)
336    pub author: String,
337}
338
339/// A link to an external resource (Web page, digital photo, video clip, etc)
340/// with additional information.
341pub struct Link {
342    /// Text of hyperlink.
343    pub text: Option<String>,
344
345    /// Mime type of content (image/jpeg)
346    pub link_type: Option<String>,
347
348    /// URL of hyperlink.
349    pub href: String,
350}
351
352/// Two lat/lon pairs defining the extent of an element.
353pub struct Bounds {
354    /// The minimum latitude.
355    pub min_lat: Latitude,
356    /// The minimum longitude.
357    pub min_lon: Longitude,
358    /// The maximum latitude.
359    pub max_lat: Latitude,
360    /// The maximum longitude.
361    pub max_lon: Longitude,
362}
363
364/// Type of GPS fix.  none means GPS had no fix.  To signify "the fix info is
365/// unknown, leave out fixType entirely. pps = military signal used
366pub enum Fix {
367    None,
368    TwoD,
369    ThreeD,
370    DGPS,
371    PPS,
372}
373impl Fix {
374    fn name(&self) -> &'static str {
375        match self {
376            Fix::None => "none",
377            Fix::TwoD => "2d",
378            Fix::ThreeD => "3d",
379            Fix::DGPS => "dgps",
380            Fix::PPS => "pps",
381        }
382    }
383}
384impl Display for Fix {
385    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
386        f.write_fmt(format_args!("{}", self.name()))
387    }
388}
389
390/// Represents a differential GPS station.  Valid range `[0,1023]`
391pub struct DGPSStationType(u16);
392
393/// A Track Segment holds a list of Track Points which are logically connected
394/// in order. To represent a single GPS track where GPS reception was lost, or
395/// the GPS receiver was turned off, start a new Track Segment for each
396/// continuous span of track data.
397#[derive(Default)]
398pub struct TrackSegment {
399    /// A Track Point holds the coordinates, elevation, timestamp, and metadata
400    /// for a single point in a track.
401    pub track_point: Vec<Waypoint>,
402
403    /// You can add extend GPX by adding your own elements from another schema
404    /// here.
405    pub extensions: Option<Extensions>,
406}
407impl TrackSegment {
408    pub fn new() -> TrackSegment {
409        TrackSegment::default()
410    }
411}