Skip to content

Commit e800262

Browse files
authored
Merge pull request #5 from vincentneo/NativeXML
Use of Native XML parser. Seems to work fine!
2 parents 7374912 + 6f53695 commit e800262

14 files changed

+591
-323
lines changed

Classes/GPXParser.swift

+288-23
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,312 @@
77

88
import UIKit
99

10-
open class GPXParser: NSObject {
11-
12-
// MARK: Instance
10+
open class GPXParser: NSObject, XMLParserDelegate {
11+
12+
var parser: XMLParser
13+
14+
// MARK:- Init
15+
16+
public init(withData data: Data) {
17+
18+
self.parser = XMLParser(data: data)
19+
super.init()
20+
parser.delegate = self
21+
parser.parse()
22+
}
1323

14-
public func parseGPXAt(url: URL) -> GPXRoot? {
24+
public init(withPath path: String) {
25+
self.parser = XMLParser()
26+
super.init()
27+
let url = URL(fileURLWithPath: path)
1528
do {
1629
let data = try Data(contentsOf: url)
17-
return self.parseGPXWith(data: data)
30+
self.parser = XMLParser(data: data)
31+
parser.delegate = self
32+
parser.parse()
1833
}
1934
catch {
2035
print(error)
2136
}
22-
return nil
2337
}
2438

25-
public func parseGPXAt(path: String) -> GPXRoot? {
26-
27-
let url = URL(fileURLWithPath: path)
28-
return GPXParser().parseGPXAt(url: url)
39+
public init(withURL url: URL) {
40+
self.parser = XMLParser()
41+
super.init()
42+
do {
43+
let data = try Data(contentsOf: url)
44+
self.parser = XMLParser(data: data)
45+
parser.delegate = self
46+
parser.parse()
47+
}
48+
catch {
49+
print(error)
50+
}
2951
}
3052

31-
public func parseGPXWith(string: String) -> GPXRoot? {
32-
33-
let xml = try? TBXML(xmlString: string, error: ())
34-
35-
if xml?.rootXMLElement != nil {
36-
return GPXRoot(XMLElement: xml!.rootXMLElement, parent: nil)
53+
// MARK:- GPX Parsing
54+
55+
var element = String()
56+
var latitude: CGFloat? = CGFloat()
57+
var longitude: CGFloat? = CGFloat()
58+
59+
// Elements
60+
var waypoint = GPXWaypoint()
61+
var route = GPXRoute()
62+
var routepoint = GPXRoutePoint()
63+
var track = GPXTrack()
64+
var tracksegment = GPXTrackSegment()
65+
var trackpoint = GPXTrackPoint()
66+
67+
// Arrays of elements
68+
var waypoints = [GPXWaypoint]()
69+
var routes = [GPXRoute]()
70+
var routepoints = [GPXRoutePoint]()
71+
72+
var tracks = [GPXTrack]()
73+
var tracksegements = [GPXTrackSegment]()
74+
var trackpoints = [GPXTrackPoint]()
75+
76+
var metadata: GPXMetadata? = GPXMetadata()
77+
var extensions: GPXExtensions? = GPXExtensions()
78+
79+
var isWaypoint: Bool = false
80+
var isMetadata: Bool = false
81+
var isRoute: Bool = false
82+
var isRoutePoint: Bool = false
83+
var isTrack: Bool = false
84+
var isTrackSegment: Bool = false
85+
var isTrackPoint: Bool = false
86+
var isExtension: Bool = false
87+
88+
func value(from string: String?) -> CGFloat? {
89+
if string != nil {
90+
if let number = NumberFormatter().number(from: string!) {
91+
return CGFloat(number.doubleValue)
92+
}
3793
}
38-
3994
return nil
4095
}
96+
97+
public func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
98+
element = elementName
99+
100+
switch elementName {
101+
case "metadata":
102+
isMetadata = true
103+
case "wpt":
104+
isWaypoint = true
105+
latitude = value(from: attributeDict ["lat"])
106+
longitude = value(from: attributeDict ["lon"])
107+
case "rte":
108+
isRoute = true
109+
case "rtept":
110+
isRoutePoint = true
111+
case "trk":
112+
isTrack = true
113+
case "trkseg":
114+
isTrackSegment = true
115+
case "trkpt":
116+
isTrackPoint = true
117+
latitude = value(from: attributeDict ["lat"])
118+
longitude = value(from: attributeDict ["lon"])
119+
case "extensions":
120+
isExtension = true
121+
default: ()
122+
}
123+
124+
}
41125

42-
public func parseGPXWith(data: Data) -> GPXRoot? {
126+
public func parser(_ parser: XMLParser, foundCharacters string: String) {
43127

44-
let xml = try? TBXML(xmlData: data, error: ())
128+
let foundString = string.trimmingCharacters(in: .whitespacesAndNewlines)
45129

46-
if xml?.rootXMLElement != nil {
47-
return GPXRoot(XMLElement: xml?.rootXMLElement, parent: nil)
130+
if isWaypoint || isTrackPoint || isRoutePoint {
131+
waypoint.latitude = latitude
132+
waypoint.longitude = longitude
133+
if foundString.isEmpty == false {
134+
switch element {
135+
case "ele":
136+
self.waypoint.elevation = value(from: foundString)!
137+
case "time":
138+
let dateFormatter = DateFormatter()
139+
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
140+
self.waypoint.time = dateFormatter.date(from: foundString)!
141+
case "magvar":
142+
self.waypoint.magneticVariation = value(from: foundString)!
143+
case "geoidheight":
144+
self.waypoint.geoidHeight = value(from: foundString)!
145+
case "name":
146+
self.waypoint.name = foundString
147+
case "desc":
148+
self.waypoint.desc = foundString
149+
case "source":
150+
self.waypoint.source = foundString
151+
case "sat":
152+
self.waypoint.satellites = Int(value(from: foundString)!)
153+
case "hdop":
154+
self.waypoint.horizontalDilution = value(from: foundString)!
155+
case "vdop":
156+
self.waypoint.verticalDilution = value(from: foundString)!
157+
case "pdop":
158+
self.waypoint.positionDilution = value(from: foundString)!
159+
case "ageofdgpsdata":
160+
self.waypoint.ageofDGPSData = value(from: foundString)!
161+
case "dgpsid":
162+
self.waypoint.DGPSid = Int(value(from: foundString)!)
163+
default: ()
164+
}
165+
}
166+
}
167+
if isMetadata {
168+
if foundString.isEmpty != false {
169+
switch element {
170+
case "name":
171+
self.metadata!.name = foundString
172+
case "desc":
173+
self.metadata!.desc = foundString
174+
case "time":
175+
let dateFormatter = DateFormatter()
176+
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
177+
self.metadata!.time = dateFormatter.date(from: foundString)!
178+
case "keyword":
179+
self.metadata!.keyword = foundString
180+
// author, copyright, link, bounds, extensions not implemented.
181+
default: ()
182+
}
183+
}
184+
}
185+
}
186+
187+
public func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
188+
switch elementName {
189+
190+
case "metadata":
191+
isMetadata = false
192+
193+
case "wpt":
194+
let tempWaypoint = GPXWaypoint()
195+
196+
// copy values
197+
tempWaypoint.elevation = self.waypoint.elevation
198+
tempWaypoint.time = self.waypoint.time
199+
tempWaypoint.magneticVariation = self.waypoint.magneticVariation
200+
tempWaypoint.geoidHeight = self.waypoint.geoidHeight
201+
tempWaypoint.name = self.waypoint.name
202+
tempWaypoint.desc = self.waypoint.desc
203+
tempWaypoint.source = self.waypoint.source
204+
tempWaypoint.satellites = self.waypoint.satellites
205+
tempWaypoint.horizontalDilution = self.waypoint.horizontalDilution
206+
tempWaypoint.verticalDilution = self.waypoint.verticalDilution
207+
tempWaypoint.positionDilution = self.waypoint.positionDilution
208+
tempWaypoint.ageofDGPSData = self.waypoint.ageofDGPSData
209+
tempWaypoint.DGPSid = self.waypoint.DGPSid
210+
tempWaypoint.latitude = self.waypoint.latitude
211+
tempWaypoint.longitude = self.waypoint.longitude
212+
213+
self.waypoints.append(tempWaypoint)
214+
// clear values
215+
isWaypoint = false
216+
latitude = nil
217+
longitude = nil
218+
219+
case "rte":
220+
self.route.add(routepoints: routepoints)
221+
let tempTrack = GPXRoute()
222+
tempTrack.routepoints = self.route.routepoints
223+
self.routes.append(route)
224+
225+
// clear values
226+
isRoute = false
227+
228+
case "rtept":
229+
230+
let tempRoutePoint = GPXRoutePoint()
231+
232+
// copy values
233+
tempRoutePoint.elevation = self.waypoint.elevation
234+
tempRoutePoint.time = self.waypoint.time
235+
tempRoutePoint.magneticVariation = self.waypoint.magneticVariation
236+
tempRoutePoint.geoidHeight = self.waypoint.geoidHeight
237+
tempRoutePoint.name = self.waypoint.name
238+
tempRoutePoint.desc = self.waypoint.desc
239+
tempRoutePoint.source = self.waypoint.source
240+
tempRoutePoint.satellites = self.waypoint.satellites
241+
tempRoutePoint.horizontalDilution = self.waypoint.horizontalDilution
242+
tempRoutePoint.verticalDilution = self.waypoint.verticalDilution
243+
tempRoutePoint.positionDilution = self.waypoint.positionDilution
244+
tempRoutePoint.ageofDGPSData = self.waypoint.ageofDGPSData
245+
tempRoutePoint.DGPSid = self.waypoint.DGPSid
246+
tempRoutePoint.latitude = self.waypoint.latitude
247+
tempRoutePoint.longitude = self.waypoint.longitude
248+
249+
self.routepoints.append(tempRoutePoint)
250+
251+
isRoutePoint = false
252+
case "trk":
253+
254+
self.track.add(trackSegments: tracksegements)
255+
256+
let tempTrack = GPXTrack()
257+
tempTrack.tracksegments = self.track.tracksegments
258+
self.tracks.append(tempTrack)
259+
260+
//clear values
261+
isTrack = false
262+
263+
case "trkseg":
264+
self.tracksegment.add(trackpoints: trackpoints)
265+
266+
let tempTrackSegment = GPXTrackSegment()
267+
tempTrackSegment.trackpoints = self.tracksegment.trackpoints
268+
self.tracksegements.append(tempTrackSegment)
269+
270+
// clear values
271+
isTrackSegment = false
272+
273+
case "trkpt":
274+
275+
let tempTrackPoint = GPXTrackPoint()
276+
277+
// copy values
278+
tempTrackPoint.elevation = self.waypoint.elevation
279+
tempTrackPoint.time = self.waypoint.time
280+
tempTrackPoint.magneticVariation = self.waypoint.magneticVariation
281+
tempTrackPoint.geoidHeight = self.waypoint.geoidHeight
282+
tempTrackPoint.name = self.waypoint.name
283+
tempTrackPoint.desc = self.waypoint.desc
284+
tempTrackPoint.source = self.waypoint.source
285+
tempTrackPoint.satellites = self.waypoint.satellites
286+
tempTrackPoint.horizontalDilution = self.waypoint.horizontalDilution
287+
tempTrackPoint.verticalDilution = self.waypoint.verticalDilution
288+
tempTrackPoint.positionDilution = self.waypoint.positionDilution
289+
tempTrackPoint.ageofDGPSData = self.waypoint.ageofDGPSData
290+
tempTrackPoint.DGPSid = self.waypoint.DGPSid
291+
tempTrackPoint.latitude = self.waypoint.latitude
292+
tempTrackPoint.longitude = self.waypoint.longitude
293+
294+
self.trackpoints.append(tempTrackPoint)
295+
296+
// clear values
297+
isTrackPoint = false
298+
latitude = nil
299+
longitude = nil
300+
case "extensions":
301+
isExtension = false
302+
default: ()
48303
}
49-
50-
return nil
51304
}
52305

306+
// MARK:- Export parsed data
307+
308+
open func parsedData() -> GPXRoot {
309+
let root = GPXRoot()
310+
root.metadata = metadata // partially implemented
311+
root.extensions = extensions // not implemented
312+
root.add(waypoints: waypoints)
313+
root.add(routes: routes)
314+
root.add(tracks: tracks)
315+
return root
316+
}
317+
53318
}

Example/GPXKit/ViewController.swift

+31-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,41 @@ import UIKit
1010
import GPXKit
1111

1212
class ViewController: UIViewController {
13+
14+
var tracks = [GPXTrack]()
15+
var waypoints = [GPXWaypoint]()
1316

1417
override func viewDidLoad() {
1518
super.viewDidLoad()
1619

17-
// Do any additional setup after loading the view, typically from a nib.
20+
// Example of parsing a GPX file from a sample URL
21+
22+
let urlString : String = "https://raw.githubusercontent.com/gps-touring/sample-gpx/master/BrittanyJura/Courgenay_Ballon-DAlsace.gpx"
23+
let url: URL = URL(string: urlString)!
24+
25+
// GPXRoot object that contains all the data parsed from GPXParser.
26+
let gpx = GPXParser(withURL: url).parsedData()
27+
28+
self.tracks = gpx.tracks
29+
self.waypoints = gpx.waypoints
30+
31+
// This example prints all the waypoints's latitude, longitude and date from the GPX file.
32+
for waypoint in self.waypoints {
33+
print("waypoint-latitude: \(waypoint.latitude!)")
34+
print("waypoint-longitude: \(waypoint.longitude!)")
35+
print("waypoint-date: \(waypoint.time)")
36+
}
37+
38+
// This example prints all the trackpoint's latitude, longitude and date from the GPX file.
39+
for track in self.tracks {
40+
for tracksegment in track.tracksegments {
41+
for trackpoint in tracksegment.trackpoints {
42+
print("trackpoint-latitude: \(trackpoint.latitude!)")
43+
print("trackpoint-longitude: \(trackpoint.longitude!)")
44+
print("trackpoint-date: \(trackpoint.time)")
45+
}
46+
}
47+
}
1848
}
1949

2050
override func didReceiveMemoryWarning() {

Example/Podfile.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PODS:
2-
- GPXKit (0.1.0)
2+
- GPXKit (0.2.2)
33

44
DEPENDENCIES:
55
- GPXKit (from `../`)
@@ -9,7 +9,7 @@ EXTERNAL SOURCES:
99
:path: "../"
1010

1111
SPEC CHECKSUMS:
12-
GPXKit: 0600ffbd2405f19955bb442f36d108a099d1f3a0
12+
GPXKit: 6d9209c2ff116aab99068c65c6d0523697a7bcd4
1313

1414
PODFILE CHECKSUM: 8a32a3f9dd9bad529d650f7a04d51689d0db8eca
1515

0 commit comments

Comments
 (0)