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}