Overpass Turbo

Overpass turbo is a web-based data filtering tool for OpenStreetMap. With overpass turbo we can run Overpass API queries and analyse the resulting OSM data interactively on a map. The queries can be written in either in Overpass QL or XML. We can specify if output needs to be in json or XML. Overpass Turbo also comes with a nice query builder that assists in building queries. The query we want to run posts the following XML to Overpass Turbo, and gets toilets in the range of 1000 meters.

<osm-script output="json">
  <query into="_" type="node">
    <has-kv k="amenity" modv="" v="toilets"/>
    <around from="_" into="_" 
    		lat="52.51633" lon="13.37751" radius="1000"/>
  </query>
  <print e="" from="_" geometry="skeleton" 
  		 limit="" mode="body" n="" order="id" s="" w=""/>
</osm-script>

The query translates to:

Find nodes which match the type amenity of type toilets, around a radius of 1000 of latitude 52.51633, and longitude 13.37751

We’ll now query the Overpass turbo API using curl.

curl -X POST -H 'Content-type: text/xml' \
-d '<osm-script output="json">
	<query into="_" type="node">
		<has-kv k="amenity" modv="" v="toilets"/>
		<around from="_" into="_" lat="52.51633" lon="13.37751" radius="800"/>
	</query>
	<print e="" from="_" geometry="skeleton" limit="" mode="body" n="" order="id" s="" w=""/>
	</osm-script>' \
https://overpass-api.de/api/interpreter

Tip: You can also test the query in Overpass Turbo visual query builder.

Overpass Turbo will process the query respond wit the nearest amenities as JSON data.

{
  "version": 0.6,
  "generator": "Overpass API 0.7.54.13 ff15392f",
  "osm3s": {
    "timestamp_osm_base": "2018-03-05T12:39:03Z",
    "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
  },
  "elements": [
	{
		"type": "node",
		"id": 927092067,
		"lat": 52.5141833,
		"lon": 13.3914532,
		"tags": {
			"amenity": "toilets",
			"fee": "yes",
			"name": "City Toilette",
			"toilets:wheelchair": "yes",
			"wheelchair": "yes",
		}
	},
	// more elements...
}

We’ll use intermediate DTOs that match the data from OSM, and convert it to the final location model.

internal struct RawOSMData: Codable {
    // We're interested only in elements, and can ignore other data
    var elements: [OSMElement]?
    private enum CodingKeys: String, CodingKey {
        case elements
    }
}

internal struct OSMElement: Codable {
    var type: String?
    var id: Int?
    var lat: Double?
    var lon: Double?
    var tags: Tags?
    struct Tags: Codable {
        var amenity: String?
        var fee: String?
        var name: String?
        var englishName: String?
        var wheelchair: String?
        enum CodingKeys: String, CodingKey {
            case englishName = "name:en"	
            case amenity = "amenity"
            case fee = "fee"
            case name = "name"
            case wheelchair = "wheelchair"
        }
    }
}

Location Model

And now we can define the Location Model class, which will have the following properties:

class Location: NSObject {
    let id: String
    let title: String?
    let locationDescription: String
    let coordinates: (Double, Double)
    var amenity: String?
    var fee: Bool?
    var feeAmount: String?
    let isAccessible: Bool
    
    init(id: String, title:String, locationDescription: String, coordintes: (Double, Double), isAccessible: Bool ) {
        self.id = id
        self.title = title
        self.locationDescription = locationDescription
        self.coordinates = coordintes
        self.isAccessible = isAccessible
    }
    convenience init(jsonElement: OSMElement) {
        self.init(id: String(describing: jsonElement.id),
                  title: jsonElement.tags?.englishName ?? jsonElement.tags?.name ?? "Unknown Amenity",
                  locationDescription: jsonElement.tags?.name ?? "Toilet",
                  coordintes: (jsonElement.lat ?? 0.0, jsonElement.lon ?? 0.0),
                  isAccessible: jsonElement.tags?.wheelchair != nil)
    }
}

The class Location has all the information we need to show the amenity on the MapView.

In the next post we’ll see how to design a modular networking layer for the App.