|
7 | 7 |
|
8 | 8 | import UIKit
|
9 | 9 |
|
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 | + } |
13 | 23 |
|
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) |
15 | 28 | do {
|
16 | 29 | 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() |
18 | 33 | }
|
19 | 34 | catch {
|
20 | 35 | print(error)
|
21 | 36 | }
|
22 |
| - return nil |
23 | 37 | }
|
24 | 38 |
|
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 | + } |
29 | 51 | }
|
30 | 52 |
|
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 | + } |
37 | 93 | }
|
38 |
| - |
39 | 94 | return nil
|
40 | 95 | }
|
| 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 | + } |
41 | 125 |
|
42 |
| - public func parseGPXWith(data: Data) -> GPXRoot? { |
| 126 | + public func parser(_ parser: XMLParser, foundCharacters string: String) { |
43 | 127 |
|
44 |
| - let xml = try? TBXML(xmlData: data, error: ()) |
| 128 | + let foundString = string.trimmingCharacters(in: .whitespacesAndNewlines) |
45 | 129 |
|
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: () |
48 | 303 | }
|
49 |
| - |
50 |
| - return nil |
51 | 304 | }
|
52 | 305 |
|
| 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 | + |
53 | 318 | }
|
0 commit comments