Skip to content

Commit

Permalink
Merge pull request #6 from ricobeck/master
Browse files Browse the repository at this point in the history
[WIP] Adds support for waypoints
  • Loading branch information
mmllr authored Feb 14, 2022
2 parents 330b148 + ae65222 commit 18658c5
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 2 deletions.
26 changes: 26 additions & 0 deletions Sources/GPXKit/GPXFileParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public enum GPXParserError: Error, Equatable {
internal enum GPXTags: String {
case gpx
case metadata
case waypoint = "wpt"
case time
case track = "trk"
case name
Expand All @@ -27,6 +28,7 @@ internal enum GPXTags: String {
case power
case description = "desc"
case keywords
case comment = "cmt"
}

internal enum GPXAttributes: String {
Expand Down Expand Up @@ -72,6 +74,7 @@ final public class GPXFileParser {
}
return GPXTrack(
date: node.childFor(.metadata)?.childFor(.time)?.date,
waypoints: parseWaypoints(node.childrenOfType(.waypoint)),
title: title,
description: trackNode.childFor(.description)?.content,
trackPoints: parseSegment(trackNode.childFor(.trackSegment)),
Expand All @@ -80,6 +83,11 @@ final public class GPXFileParser {
)
}

private func parseWaypoints(_ nodes: [XMLNode]) -> [Waypoint]? {
guard !nodes.isEmpty else { return nil }
return nodes.compactMap { Waypoint.init($0) ?? nil }
}

private func parseKeywords(node: XMLNode) -> [String] {
node.childFor(.metadata)?
.childFor(.keywords)?
Expand Down Expand Up @@ -150,6 +158,24 @@ final public class GPXFileParser {
}
}

internal extension Waypoint {
init?(_ waypointNode: XMLNode) {
guard let lat = waypointNode.latitude,
let lon = waypointNode.longitude
else {
return nil
}
self.coordinate = Coordinate(
latitude: lat,
longitude: lon
)
self.date = waypointNode.childFor(.time)?.date
self.name = waypointNode.childFor(.name)?.content
self.comment = waypointNode.childFor(.comment)?.content
self.description = waypointNode.childFor(.description)?.content
}
}

internal extension TrackPoint {
init?(trackNode: XMLNode) {
guard let lat = trackNode.latitude,
Expand Down
8 changes: 6 additions & 2 deletions Sources/GPXKit/GPXTrack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import Foundation
public struct GPXTrack: Hashable {
/// Optional date stamp of the gpx track
public var date: Date?
/// Waypoint defined for the gpx
public var waypoints: [Waypoint]?
/// Title of the gpx track
public var title: String
/// Description of the gpx track
Expand All @@ -20,12 +22,14 @@ public struct GPXTrack: Hashable {
/// Initializes a GPXTrack. You don't need to construct this value by yourself, as it is done by GXPKits track parsing logic.
/// - Parameters:
/// - date: The date stamp of the track. Defaults to nil.
/// - waypoints: Array of `Waypoints`. Defaults to nil.
/// - title: String describing the track.
/// - trackPoints: Array of `TrackPoint`s describing the route.
/// - keywords: Array of `String`s with keyords. Default is an empty array (no keywords).
/// - keywords: Array of `String`s with keywords. Default is an empty array (no keywords).
/// - gradeSegmentLength: The length in meters for the grade segments. Defaults to 50 meters.
public init(date: Date? = nil, title: String, description: String? = nil, trackPoints: [TrackPoint], keywords: [String] = [], gradeSegmentLength: Double = 50.0) {
public init(date: Date? = nil, waypoints: [Waypoint]? = nil, title: String, description: String? = nil, trackPoints: [TrackPoint], keywords: [String] = [], gradeSegmentLength: Double = 50.0) {
self.date = date
self.waypoints = waypoints
self.title = title
self.description = description
self.trackPoints = trackPoints
Expand Down
32 changes: 32 additions & 0 deletions Sources/GPXKit/Waypoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Foundation

/// Value type describing a single Waypoint defined within a `GPXTrack`. A `Waypoint` has a location consisting of latitude, longitude and some metadata,
/// e.g. name and description.
public struct Waypoint: Hashable {
/// The coordinate (latitude, longitude and elevation in meters)
public var coordinate: Coordinate
/// Optional date for a given point.
public var date: Date?
/// Optional name of the waypoint
public var name: String?
/// Optional comment for the waypoint
public var comment: String?
/// Optional description of the waypoint
public var description: String?

/// Initializer
/// You don't need to construct this value by yourself, as it is done by GXPKits track parsing logic.
/// - Parameters:
/// - coordinate: Location of the waypoint, required
/// - date: Optional date
/// - name: Name of the waypoint
/// - comment: A short comment
/// - description: A longer description
public init(coordinate: Coordinate, date: Date? = nil, name: String? = nil, comment: String? = nil, description: String? = nil) {
self.coordinate = coordinate
self.date = date
self.name = name
self.comment = comment
self.description = description
}
}
31 changes: 31 additions & 0 deletions Tests/GPXKitTests/GPXParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,35 @@ class GPXParserTests: XCTestCase {
let sut = try XCTUnwrap(self.result)
XCTAssertEqual(["one", "two", "three", "four"], sut.keywords)
}

func testParsingAFileWithoutWaypointDefinitionsHasEmptyWaypoints() throws {
parseXML(testXMLData)
let sut = try XCTUnwrap(self.result)

XCTAssertNil(sut.waypoints)
}

func testParsingWaypointAttributes() throws {
parseXML(testXMLDataContainingWaypoint)
let sut = try XCTUnwrap(self.result)

let waypointStart = Waypoint(
coordinate: Coordinate(latitude: 51.2760600, longitude: 12.3769500),
date: expectedDate(for: "2020-03-18T12:39:47Z"),
name: "Start",
comment: "start comment",
description: "This is the start"
)

let waypointFinish = Waypoint(
coordinate: Coordinate(latitude: 51.2760420, longitude: 12.3769760),
date: expectedDate(for: "2020-03-18T12:39:48Z"),
name: "Finish",
comment: "finish comment",
description: "This is the finish"
)

XCTAssertEqual([waypointStart, waypointFinish], sut.waypoints)

}
}
53 changes: 53 additions & 0 deletions Tests/GPXKitTests/TestFixtures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,59 @@ let testTrackWithoutTime = GPXTrack(date: nil,
date: nil)
])

let testXMLDataContainingWaypoint = """
<?xml version="1.0" encoding="UTF-8"?>
<gpx creator="StravaGPX" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd" version="1.1" xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
<metadata>
<time>2020-03-18T12:39:47Z</time>
</metadata>
<wpt lat="51.2760600" lon="12.3769500">
<time>2020-03-18T12:39:47Z</time>
<name>Start</name>
<cmt>start comment</cmt>
<desc>This is the start</desc>
</wpt>
<wpt lat="51.2760420" lon="12.3769760">
<time>2020-03-18T12:39:48Z</time>
<name>Finish</name>
<cmt>finish comment</cmt>
<desc>This is the finish</desc>
</wpt>
<trk>
<name>Haus- und Seenrunde Ausdauer</name>
<desc>Track description</desc>
<type>1</type>
<trkseg>
<trkpt lat="51.2760600" lon="12.3769500">
<ele>114.2</ele>
<time>2020-03-18T12:39:47Z</time>
<extensions>
<power>42</power>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
<gpxtpx:hr>97</gpxtpx:hr>
<gpxtpx:cad>40</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="51.2760420" lon="12.3769760">
<ele>114.0</ele>
<time>2020-03-18T12:39:48Z</time>
<extensions>
<power>272</power>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>20</gpxtpx:atemp>
<gpxtpx:hr>97</gpxtpx:hr>
<gpxtpx:cad>40</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
"""


let sampleGPX = """
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="Garmin Connect"
Expand Down

0 comments on commit 18658c5

Please sign in to comment.