Skip to content

Instantly share code, notes, and snippets.

@emorydunn
Last active January 8, 2020 02:01
Show Gist options
  • Save emorydunn/245c0f2c7753c2d30cffcc40d3239c8b to your computer and use it in GitHub Desktop.
Save emorydunn/245c0f2c7753c2d30cffcc40d3239c8b to your computer and use it in GitHub Desktop.
Swift script to Convert GPX files to GeoJSON files for import into Compass
// A Swift script to Convert GPX files to GeoJSON files for import into Compass
// The script takes a single argument, the GPX file, and saves out a matching GeoJSON file
//
// The GeoJSON is meant to be imported into Compass (https://github.com/aaronpk/Compass)
// and thus is not "correct" GeoJSON
import Foundation
/// An object to handle converting a GPX file to GeoJSON
struct GPXtoGeoJSON {
/// URL of the GPX file
let gpxURL: URL
/// URL of the GeoJSON file
///
/// This URL is the same as the `gpxURL` with the extension replaced with `geojson`
var jsonURL: URL {
return gpxURL.deletingPathExtension().appendingPathExtension("geojson")
}
/// Return a GeoJSON dictionary by reading the GPX XML
///
/// GPX trackpoints, `trkpt`, are extracted from the document using an XPath:
/// `/gpx/trk/trkseg/trkpt`
func geoJSON() throws -> [String: Any] {
let doc = try XMLDocument(contentsOf: gpxURL, options: [])
let points = try doc.nodes(forXPath: "/gpx/trk/trkseg/trkpt")
let locations = points.compactMap {
makeFeature(fromNode: $0 as! XMLElement)
}
return [
"locations": locations
]
}
/// Write the specified GeoJSON disctionary to the `jsonURL`
/// - Parameter geoJSON: The GeoJSON object to write
func write(geoJSON: [String: Any]) throws {
let data = try JSONSerialization.data(withJSONObject: geoJSON, options: [])
try data.write(to: jsonURL)
}
/// Convert a GPX `trkpt` to a GeoJSON feature.
///
/// The `XMLElement` needs to have the following attributes, along with a `time` element:
/// - `lat`
/// - `lon`
///
/// Similar to the following:
///
/// ```
/// <trkpt lat="43.876595" lon="5.389074">
/// <ele>273.561401</ele>
/// <time>2012-10-06T07:17:20Z</time>
/// </trkpt>
/// ```
///
/// - Parameter node: The XML object to convert
/// - Returns: A GeoJSON feature dictionary
func makeFeature(fromNode node: XMLElement) -> [String : Any]? {
// Get coordinates from node
guard
let latString = node.attribute(forName: "lat")?.stringValue,
let lonString = node.attribute(forName: "lon")?.stringValue
else {
return nil
}
// Get the timestamp from the node
let timeNode = node.elements(forName: "time")[0]
guard let time = timeNode.stringValue else {
return nil
}
return [
"type": "Feature",
"geometry": [
"type": "Point",
"coordinates": [
Decimal(string: lonString),
Decimal(string: latString)
]
],
"properties": [
"timestamp": time
]
]
}
}
do {
let args = CommandLine.arguments
guard args.count == 2 else {
print("Please provide a GPX file")
exit(1)
}
let gpxURL = URL(fileURLWithPath: args[1])
let gpx = GPXtoGeoJSON(gpxURL: gpxURL)
print("Converting \(gpxURL.lastPathComponent)")
let geoJSON = try gpx.geoJSON()
try gpx.write(geoJSON: geoJSON)
print("GeoJSON written to \(gpx.jsonURL.path)")
} catch {
print(error.localizedDescription)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment