LooLocator: Defining the Data Model
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 of1000
of latitude52.51633
, and longitude13.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.