1use super::GPXProperties;
2use crate::{
3 data_structures::HasLayer,
4 parsers::{XMLTagItem, xml_find_tag_by_name, xml_find_tags_by_name, xml_get_attribute},
5};
6use alloc::{
7 string::{String, ToString},
8 vec::Vec,
9};
10use core::{fmt, str::FromStr};
11use s2json::{
12 MValueCompatible, PrimitiveValue, ValuePrimitive, ValueType, VectorFeature, VectorFeatureType,
13 VectorGeometry, VectorLineString, VectorMultiLineString, VectorPoint,
14};
15use serde::{Deserialize, Serialize};
16
17#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
19pub struct GPX {
20 pub version: String, pub creator: String,
24 pub metadata: Option<GPXMetadata>,
26 pub wpt: Option<Vec<GPXWaypoint>>,
28 pub rte: Option<Vec<GPXRoute>>,
30 pub trk: Option<Vec<GPXTrack>>,
32 }
35impl GPX {
36 pub fn new(gpx_xml: &str) -> GPX {
38 let root_tag = xml_find_tag_by_name(gpx_xml, "gpx", None);
39
40 if let Some(root) = root_tag {
41 let version =
42 xml_get_attribute(&XMLTagItem::XMLTag(root.clone()), "version").unwrap_or_default();
43 let creator =
44 xml_get_attribute(&XMLTagItem::XMLTag(root.clone()), "creator").unwrap_or_default();
45
46 let metadata_tag = xml_find_tag_by_name(&root.outer, "metadata", None);
47 let metadata = metadata_tag.map(|tag| GPXMetadata::new(XMLTagItem::XMLTag(tag)));
48
49 let wpt = {
50 let wpt_tags = xml_find_tags_by_name(&root.outer, "wpt", None);
51 if !wpt_tags.is_empty() {
52 Some(
53 wpt_tags
54 .into_iter()
55 .map(|wpt| GPXWaypoint::new(XMLTagItem::XMLTag(wpt)))
56 .collect(),
57 )
58 } else {
59 None
60 }
61 };
62
63 let rte = {
64 let rte_tags = xml_find_tags_by_name(&root.outer, "rte", None);
65 if !rte_tags.is_empty() {
66 Some(
67 rte_tags
68 .into_iter()
69 .map(|rte| GPXRoute::new(XMLTagItem::XMLTag(rte)))
70 .collect(),
71 )
72 } else {
73 None
74 }
75 };
76
77 let trk = {
78 let trk_tags = xml_find_tags_by_name(&root.outer, "trk", None);
79 if !trk_tags.is_empty() {
80 Some(
81 trk_tags
82 .into_iter()
83 .map(|trk| GPXTrack::new(XMLTagItem::XMLTag(trk)))
84 .collect(),
85 )
86 } else {
87 None
88 }
89 };
90
91 GPX { version, creator, metadata, wpt, rte, trk }
92 } else {
93 GPX::default()
94 }
95 }
96}
97
98#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
100pub struct GPXMetadata {
101 pub name: Option<String>,
103 pub desc: Option<String>,
105 pub author: Option<GPXPerson>,
107 pub copyright: Option<GPXCopyright>,
109 pub link: Option<GPXLink>,
111 pub time: Option<String>,
113 pub keywords: Option<String>,
115 pub bounds: Option<GPXBounds>,
117 }
120impl GPXMetadata {
121 pub fn new(metadata_xml: XMLTagItem) -> Self {
123 let inner = match &metadata_xml {
124 XMLTagItem::XMLTag(tag) => tag.inner.clone().unwrap_or_default(),
125 XMLTagItem::String(s) => s.clone(),
126 };
127
128 let name = xml_find_tag_by_name(&inner, "name", None).and_then(|tag| tag.inner);
129 let desc = xml_find_tag_by_name(&inner, "desc", None).and_then(|tag| tag.inner);
130 let author = xml_find_tag_by_name(&inner, "author", None)
131 .map(|tag| GPXPerson::new(XMLTagItem::XMLTag(tag)));
132 let copyright = xml_find_tag_by_name(&inner, "copyright", None)
133 .map(|tag| GPXCopyright::new(XMLTagItem::XMLTag(tag)));
134 let link = xml_find_tag_by_name(&inner, "link", None)
135 .map(|tag| GPXLink::new(XMLTagItem::XMLTag(tag)));
136 let time = xml_find_tag_by_name(&inner, "time", None).and_then(|tag| tag.inner);
137 let keywords = xml_find_tag_by_name(&inner, "keywords", None).and_then(|tag| tag.inner);
138 let bounds = xml_find_tag_by_name(&inner, "bounds", None)
139 .map(|tag| GPXBounds::new(XMLTagItem::XMLTag(tag)));
140
141 GPXMetadata { name, desc, author, copyright, link, time, keywords, bounds }
142 }
143}
144impl HasLayer for GPXMetadata {
145 fn get_layer(&self) -> Option<String> {
146 None
147 }
148}
149
150#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
152pub struct GPXRoute {
153 pub name: Option<String>,
155 pub cmt: Option<String>,
157 pub desc: Option<String>,
159 pub src: Option<String>,
161 pub link: Option<Vec<GPXLink>>,
163 pub number: Option<usize>,
165 pub r#type: Option<String>,
167 pub rtept: Option<Vec<GPXWaypoint>>,
169 }
172impl GPXRoute {
173 pub fn new(route_xml: XMLTagItem) -> Self {
175 let inner = match &route_xml {
176 XMLTagItem::XMLTag(tag) => tag.inner.clone().unwrap_or_default(),
177 XMLTagItem::String(s) => s.clone(),
178 };
179
180 let name = xml_find_tag_by_name(&inner, "name", None).and_then(|tag| tag.inner);
181 let cmt = xml_find_tag_by_name(&inner, "cmt", None).and_then(|tag| tag.inner);
182 let desc = xml_find_tag_by_name(&inner, "desc", None).and_then(|tag| tag.inner);
183 let src = xml_find_tag_by_name(&inner, "src", None).and_then(|tag| tag.inner);
184 let link = {
185 let link_tags = xml_find_tags_by_name(&inner, "link", None);
186 if !link_tags.is_empty() {
187 Some(
188 link_tags
189 .into_iter()
190 .map(|tag| GPXLink::new(XMLTagItem::XMLTag(tag)))
191 .collect(),
192 )
193 } else {
194 None
195 }
196 };
197 let number = xml_find_tag_by_name(&inner, "number", None)
198 .and_then(|tag| tag.inner.and_then(|s| s.parse::<usize>().ok()));
199 let r#type = xml_find_tag_by_name(&inner, "type", None).and_then(|tag| tag.inner);
200 let rtept = {
201 let rtept_tags = xml_find_tags_by_name(&inner, "rtept", None);
202 if !rtept_tags.is_empty() {
203 Some(
204 rtept_tags
205 .into_iter()
206 .map(|tag| GPXWaypoint::new(XMLTagItem::XMLTag(tag)))
207 .collect(),
208 )
209 } else {
210 None
211 }
212 };
213
214 GPXRoute { name, cmt, desc, src, link, number, r#type, rtept }
215 }
216
217 pub fn line(&self) -> VectorLineString<GPXWaypoint> {
219 self.rtept.as_ref().map(|r| r.iter().map(|w| w.point()).collect()).unwrap_or_default()
220 }
221
222 pub fn feature(&self) -> VectorFeature<(), GPXProperties, GPXWaypoint> {
224 VectorFeature {
225 _type: VectorFeatureType::VectorFeature,
226 properties: self.into(),
227 geometry: VectorGeometry::new_linestring(self.line(), None),
228 ..Default::default()
229 }
230 }
231}
232impl From<&GPXRoute> for GPXProperties {
233 fn from(route: &GPXRoute) -> Self {
234 GPXProperties {
235 name: route.name.clone(),
236 cmt: route.cmt.clone(),
237 desc: route.desc.clone(),
238 src: route.src.clone(),
239 link: route.link.clone(),
240 number: route.number,
241 track_type: None,
242 route_type: route.r#type.clone(),
243 }
244 }
245}
246
247#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
249pub struct GPXTrack {
250 pub name: Option<String>,
252 pub cmt: Option<String>,
254 pub desc: Option<String>,
256 pub src: Option<String>,
258 pub link: Option<Vec<GPXLink>>,
260 pub number: Option<usize>,
262 pub r#type: Option<String>,
264 pub trkseg: Option<Vec<GPXTrackSegment>>,
266 }
269impl GPXTrack {
270 pub fn new(track_xml: XMLTagItem) -> Self {
272 let inner = match &track_xml {
273 XMLTagItem::XMLTag(tag) => tag.inner.clone().unwrap_or_default(),
274 XMLTagItem::String(s) => s.clone(),
275 };
276
277 let name = xml_find_tag_by_name(&inner, "name", None).and_then(|tag| tag.inner);
278 let cmt = xml_find_tag_by_name(&inner, "cmt", None).and_then(|tag| tag.inner);
279 let desc = xml_find_tag_by_name(&inner, "desc", None).and_then(|tag| tag.inner);
280 let src = xml_find_tag_by_name(&inner, "src", None).and_then(|tag| tag.inner);
281 let link = {
282 let link_tags = xml_find_tags_by_name(&inner, "link", None);
283 if !link_tags.is_empty() {
284 Some(
285 link_tags
286 .into_iter()
287 .map(|tag| GPXLink::new(XMLTagItem::XMLTag(tag)))
288 .collect(),
289 )
290 } else {
291 None
292 }
293 };
294 let number = xml_find_tag_by_name(&inner, "number", None)
295 .and_then(|tag| tag.inner.and_then(|s| s.parse::<usize>().ok()));
296 let r#type = xml_find_tag_by_name(&inner, "type", None).and_then(|tag| tag.inner);
297 let trkseg = {
298 let trkseg_tags = xml_find_tags_by_name(&inner, "trkseg", None);
299 if !trkseg_tags.is_empty() {
300 Some(
301 trkseg_tags
302 .into_iter()
303 .map(|tag| GPXTrackSegment::new(XMLTagItem::XMLTag(tag)))
304 .collect(),
305 )
306 } else {
307 None
308 }
309 };
310
311 GPXTrack { name, cmt, desc, src, link, number, r#type, trkseg }
312 }
313
314 pub fn multiline(&self) -> VectorMultiLineString<GPXWaypoint> {
316 self.trkseg
317 .as_ref()
318 .map(|r| {
319 r.iter()
320 .map(|s| {
321 s.trkpt
322 .as_ref()
323 .map(|t| t.iter().map(|w| w.point()).collect())
324 .unwrap_or_default()
325 })
326 .collect()
327 })
328 .unwrap_or_default()
329 }
330
331 pub fn feature(&self) -> VectorFeature<(), GPXProperties, GPXWaypoint> {
333 VectorFeature {
334 _type: VectorFeatureType::VectorFeature,
335 properties: self.into(),
336 geometry: VectorGeometry::new_multilinestring(self.multiline(), None),
337 ..Default::default()
338 }
339 }
340}
341impl From<&GPXTrack> for GPXProperties {
342 fn from(track: &GPXTrack) -> Self {
343 GPXProperties {
344 name: track.name.clone(),
345 cmt: track.cmt.clone(),
346 desc: track.desc.clone(),
347 src: track.src.clone(),
348 link: track.link.clone(),
349 number: track.number,
350 track_type: track.r#type.clone(),
351 route_type: None,
352 }
353 }
354}
355
356#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
358pub struct GPXTrackSegment {
359 pub trkpt: Option<Vec<GPXWaypoint>>,
361 }
364impl GPXTrackSegment {
365 pub fn new(trkseg_xml: XMLTagItem) -> Self {
367 let inner = match &trkseg_xml {
368 XMLTagItem::XMLTag(tag) => tag.inner.clone().unwrap_or_default(),
369 XMLTagItem::String(s) => s.clone(),
370 };
371
372 let trkpt = {
373 let trkpt_tags = xml_find_tags_by_name(&inner, "trkpt", None);
374 if !trkpt_tags.is_empty() {
375 Some(
376 trkpt_tags
377 .into_iter()
378 .map(|tag| GPXWaypoint::new(XMLTagItem::XMLTag(tag)))
379 .collect(),
380 )
381 } else {
382 None
383 }
384 };
385
386 GPXTrackSegment { trkpt }
387 }
388}
389
390#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, MValueCompatible)]
392pub struct GPXWaypoint {
393 pub lat: f64,
395 pub lon: f64,
397 pub ele: Option<f64>,
399 pub time: Option<String>,
401 pub magvar: Option<f64>,
403 pub geoidheight: Option<f64>,
405 pub name: Option<String>,
407 pub cmt: Option<String>,
409 pub desc: Option<String>,
411 pub src: Option<String>,
413 pub link: Option<Vec<GPXLink>>,
415 pub sym: Option<String>,
417 pub r#type: Option<String>,
419 pub fix: Option<GPXFixType>,
421 pub sat: Option<usize>,
423 pub hdop: Option<f64>,
425 pub vdop: Option<f64>,
427 pub pdop: Option<f64>,
429 pub ageofdgpsdata: Option<f64>,
431 pub dgpsid: Option<f64>,
433 }
436impl GPXWaypoint {
437 pub fn new(waypoint_xml: XMLTagItem) -> Self {
439 let lat = xml_get_attribute(&waypoint_xml, "lat")
440 .and_then(|s| s.parse::<f64>().ok())
441 .unwrap_or(0.0);
442 let lon = xml_get_attribute(&waypoint_xml, "lon")
443 .and_then(|s| s.parse::<f64>().ok())
444 .unwrap_or(0.0);
445 let fix =
446 xml_get_attribute(&waypoint_xml, "fix").and_then(|s| s.parse::<GPXFixType>().ok());
447
448 let inner = match waypoint_xml {
449 XMLTagItem::XMLTag(tag) => tag.inner.unwrap_or_default(),
450 XMLTagItem::String(s) => s,
451 };
452
453 let ele = xml_find_tag_by_name(&inner, "ele", None)
454 .and_then(|tag| tag.inner.and_then(|s| s.parse::<f64>().ok()));
455 let time = xml_find_tag_by_name(&inner, "time", None).and_then(|tag| tag.inner);
456 let magvar = xml_find_tag_by_name(&inner, "magvar", None)
457 .and_then(|tag| tag.inner.and_then(|s| s.parse::<f64>().ok()));
458 let geoidheight = xml_find_tag_by_name(&inner, "geoidheight", None)
459 .and_then(|tag| tag.inner.and_then(|s| s.parse::<f64>().ok()));
460 let name = xml_find_tag_by_name(&inner, "name", None).and_then(|tag| tag.inner);
461 let cmt = xml_find_tag_by_name(&inner, "cmt", None).and_then(|tag| tag.inner);
462 let desc = xml_find_tag_by_name(&inner, "desc", None).and_then(|tag| tag.inner);
463 let src = xml_find_tag_by_name(&inner, "src", None).and_then(|tag| tag.inner);
464 let link = {
465 let link_tags = xml_find_tags_by_name(&inner, "link", None);
466 if !link_tags.is_empty() {
467 Some(
468 link_tags
469 .into_iter()
470 .map(|tag| GPXLink::new(XMLTagItem::XMLTag(tag)))
471 .collect(),
472 )
473 } else {
474 None
475 }
476 };
477 let sym = xml_find_tag_by_name(&inner, "sym", None).and_then(|tag| tag.inner);
478 let r#type = xml_find_tag_by_name(&inner, "type", None).and_then(|tag| tag.inner);
479 let sat = xml_find_tag_by_name(&inner, "sat", None)
480 .and_then(|tag| tag.inner.and_then(|s| s.parse::<usize>().ok()));
481 let hdop = xml_find_tag_by_name(&inner, "hdop", None)
482 .and_then(|tag| tag.inner.and_then(|s| s.parse::<f64>().ok()));
483 let vdop = xml_find_tag_by_name(&inner, "vdop", None)
484 .and_then(|tag| tag.inner.and_then(|s| s.parse::<f64>().ok()));
485 let pdop = xml_find_tag_by_name(&inner, "pdop", None)
486 .and_then(|tag| tag.inner.and_then(|s| s.parse::<f64>().ok()));
487 let ageofdgpsdata = xml_find_tag_by_name(&inner, "ageofdgpsdata", None)
488 .and_then(|tag| tag.inner.and_then(|s| s.parse::<f64>().ok()));
489 let dgpsid = xml_find_tag_by_name(&inner, "dgpsid", None)
490 .and_then(|tag| tag.inner.and_then(|s| s.parse::<f64>().ok()));
491
492 GPXWaypoint {
493 lat,
494 lon,
495 ele,
496 time,
497 magvar,
498 geoidheight,
499 name,
500 cmt,
501 desc,
502 src,
503 link,
504 sym,
505 r#type,
506 fix,
507 sat,
508 hdop,
509 vdop,
510 pdop,
511 ageofdgpsdata,
512 dgpsid,
513 }
514 }
515
516 pub fn point(&self) -> VectorPoint<GPXWaypoint> {
518 VectorPoint { x: self.lon, y: self.lat, z: self.ele, m: Some(self.clone()), t: None }
519 }
520
521 pub fn feature(&self) -> VectorFeature<(), GPXProperties, GPXWaypoint> {
523 VectorFeature {
524 _type: VectorFeatureType::VectorFeature,
525 geometry: VectorGeometry::new_point(self.point(), None),
526 ..Default::default()
527 }
528 }
529}
530
531#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
533pub struct GPXCopyright {
534 pub author: String,
536 pub year: Option<String>,
538 pub license: Option<String>,
540}
541impl GPXCopyright {
542 pub fn new(copyright_xml: XMLTagItem) -> Self {
544 let inner = match copyright_xml {
545 XMLTagItem::XMLTag(tag) => tag.inner.unwrap_or_default(),
546 XMLTagItem::String(s) => s,
547 };
548
549 let author = xml_find_tag_by_name(&inner, "author", None)
550 .and_then(|tag| tag.inner)
551 .unwrap_or_default();
552 let year = xml_find_tag_by_name(&inner, "year", None).and_then(|tag| tag.inner);
553 let license = xml_find_tag_by_name(&inner, "license", None).and_then(|tag| tag.inner);
554
555 GPXCopyright { author, year, license }
556 }
557}
558
559#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, ValuePrimitive)]
561pub struct GPXLink {
562 pub href: String,
564 pub text: Option<String>,
566 pub r#type: Option<String>,
568}
569impl GPXLink {
570 pub fn new(link_xml: XMLTagItem) -> Self {
572 let href = xml_get_attribute(&link_xml, "href").unwrap_or_default();
573
574 let inner = match link_xml {
575 XMLTagItem::XMLTag(tag) => tag.inner.unwrap_or_default(),
576 XMLTagItem::String(s) => s,
577 };
578
579 let text = xml_find_tag_by_name(&inner, "text", None).and_then(|tag| tag.inner);
580 let r#type = xml_find_tag_by_name(&inner, "type", None).and_then(|tag| tag.inner);
581
582 GPXLink { href, text, r#type }
583 }
584}
585
586#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
588pub struct GPXPerson {
589 pub name: Option<String>,
591 pub email: Option<GPXEmail>,
593 pub link: Option<GPXLink>,
595}
596impl GPXPerson {
597 pub fn new(person_xml: XMLTagItem) -> Self {
599 let inner = match person_xml {
600 XMLTagItem::XMLTag(tag) => tag.inner.unwrap_or_default(),
601 XMLTagItem::String(s) => s,
602 };
603
604 let name = xml_find_tag_by_name(&inner, "name", None).and_then(|tag| tag.inner);
605 let email = xml_find_tag_by_name(&inner, "email", None)
606 .map(|tag| GPXEmail::new(XMLTagItem::XMLTag(tag)));
607 let link = xml_find_tag_by_name(&inner, "link", None)
608 .map(|tag| GPXLink::new(XMLTagItem::XMLTag(tag)));
609
610 GPXPerson { name, email, link }
611 }
612}
613
614#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
616pub struct GPXEmail {
617 pub id: String,
619 pub domain: String,
621}
622impl GPXEmail {
623 pub fn new(email_xml: XMLTagItem) -> Self {
625 let id = xml_get_attribute(&email_xml, "id").unwrap_or_default();
626 let domain = xml_get_attribute(&email_xml, "domain").unwrap_or_default();
627
628 GPXEmail { id, domain }
629 }
630}
631
632#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
634pub struct GPXBounds {
635 pub minlat: f64,
637 pub minlon: f64,
639 pub maxlat: f64,
641 pub maxlon: f64,
643}
644impl GPXBounds {
645 pub fn new(bounds_xml: XMLTagItem) -> Self {
647 let minlat = xml_get_attribute(&bounds_xml, "minlat")
648 .and_then(|s| s.parse::<f64>().ok())
649 .unwrap_or(0.0);
650 let minlon = xml_get_attribute(&bounds_xml, "minlon")
651 .and_then(|s| s.parse::<f64>().ok())
652 .unwrap_or(0.0);
653 let maxlat = xml_get_attribute(&bounds_xml, "maxlat")
654 .and_then(|s| s.parse::<f64>().ok())
655 .unwrap_or(0.0);
656 let maxlon = xml_get_attribute(&bounds_xml, "maxlon")
657 .and_then(|s| s.parse::<f64>().ok())
658 .unwrap_or(0.0);
659
660 GPXBounds { minlat, minlon, maxlat, maxlon }
661 }
662}
663
664#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
666#[serde(rename_all = "lowercase")]
667pub enum GPXFixType {
668 #[default]
670 None,
671 #[serde(rename = "2d")]
673 D2,
674 #[serde(rename = "3d")]
676 D3,
677 Dgps,
679 Pps,
681}
682impl fmt::Display for GPXFixType {
683 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
684 let s = match self {
685 GPXFixType::None => "none",
686 GPXFixType::D2 => "2d",
687 GPXFixType::D3 => "3d",
688 GPXFixType::Dgps => "dgps",
689 GPXFixType::Pps => "pps",
690 };
691 write!(f, "{s}")
692 }
693}
694impl From<&str> for GPXFixType {
695 fn from(s: &str) -> Self {
696 match s {
697 "none" => GPXFixType::None,
698 "2d" => GPXFixType::D2,
699 "3d" => GPXFixType::D3,
700 "dgps" => GPXFixType::Dgps,
701 "pps" => GPXFixType::Pps,
702 _ => GPXFixType::None, }
704 }
705}
706impl FromStr for GPXFixType {
707 type Err = ();
708
709 fn from_str(s: &str) -> Result<Self, Self::Err> {
710 Ok(GPXFixType::from(s))
711 }
712}
713impl TryFrom<String> for GPXFixType {
714 type Error = ();
715
716 fn try_from(s: String) -> Result<Self, Self::Error> {
717 GPXFixType::from_str(&s)
718 }
719}
720impl From<GPXFixType> for ValueType {
721 fn from(v: GPXFixType) -> Self {
722 ValueType::Primitive(PrimitiveValue::String(v.to_string()))
723 }
724}
725impl From<&ValueType> for GPXFixType {
726 fn from(v: &ValueType) -> Self {
727 match v {
728 ValueType::Primitive(PrimitiveValue::String(s)) => match s.to_lowercase().as_str() {
729 "none" => GPXFixType::None,
730 "2d" => GPXFixType::D2,
731 "3d" => GPXFixType::D3,
732 "dgps" => GPXFixType::Dgps,
733 "pps" => GPXFixType::Pps,
734 _ => GPXFixType::None,
735 },
736 _ => GPXFixType::None,
737 }
738 }
739}