I have to say that I love maps and for a long time I have wanted to check out the complementary tools:
Leaflet
maps and NoSQL geo-function.
Hence, I decided to build a location app which will help people to find their closest airports.
I evaluated
ArangoDB
and
MongoDB
as NoSQL data bases.
ArangoDB worked very good but the python driver was not as good as I expected therefore MongoDB was the selected data base
.
After finishing the app, if I try to execute the app it will return the 10-closest airports to me:
After finishing the app, if I try to execute the app it will return the 10-closest airports to me:
Backend
The first task was finding all the data related to the airports. It was very easy thanks to
Openflights
which have a lot of information about flights, airports and so on.
After having the data, I needed to re-format it in order to be imported by MongoDB. The most important part was to transform the given latitude and longitude to a GeoJson Point format .
After having the data, I needed to re-format it in order to be imported by MongoDB. The most important part was to transform the given latitude and longitude to a GeoJson Point format .
1: { "type": "Point", "coordinates": [100.0, 0.0] }
The
"coordinates"
field has to contain the longitude first and the latitude after.
MongoDB geo-function $near returns the a sorted list of the closest locations (stored documents) to a given location. But, to take advantage of this functionality a geo-index is needed:
MongoDB geo-function $near returns the a sorted list of the closest locations (stored documents) to a given location. But, to take advantage of this functionality a geo-index is needed:
1: db.collection_name.ensureIndex({"loc":"2dsphere"})
Once that we have all our data ready, we use
Flask
as backend technology and
MongoEngine
as
Object-Document Mapper
. I tried to use the near function which is implemented inside MongoEngine, but I could not, hence I took the raw-query functionality.
models.py :
models.py :
1: from settings import db
2:
3:
4: class Airport(db.Document):
5: city = db.StringField()
6: name = db.StringField()
7: country = db.StringField()
8: iata_faa = db.StringField(default=None)
9: tz = db.StringField(default=None)
10: icao = db.StringField(default=None)
11: dst = db.StringField(default=None)
12: loc = db.PointField(default=None)
13:
14: @staticmethod
15: def get_closest_airports(lonlat, limit=10):
16: """
17: Using a raw query it returns the "limit" closest airports.
18:
19: :param latlon list:
20: :return list of airports:
21: """
22: return Airport.objects(
23: __raw__={"loc": {"$near": {"$geometry": {"type": "Point", "coordinates": lonlat}}}}).limit(limit)
24:
25: def __dict__(self):
26: return {
27: "city": self.city,
28: "name": self.name,
29: "country": self.country,
30: "lat": self.loc["coordinates"].pop(),
31: "lon": self.loc["coordinates"].pop()
32: }
Frontend
Then, we began to work with LeafLet library in order to locate the user and represent the locations.
1: function customMark(lat, lon, message, icon)
2: {
3: var marker = L.marker([lat, lon], {icon: icon}).addTo(map)
4: if (message != null)
5: {
6: marker.bindPopup(message);
7: }
8: return marker
9: }
10:
11: function onLocationFound(e)
12: {
13: var radius = e.accuracy / 2;
14:
15: L.marker(e.latlng).addTo(map)
16: .bindPopup("You are within " + radius + " meters from this point").openPopup();
17:
18: L.circle(e.latlng, radius).addTo(map);
19: }
20:
21: function markAirports(airports)
22: {
23: var airportIcon = L.icon({
24: iconUrl: 'http://excellencyairportlimousine.com/pics/airport.png',
25: iconSize: [50, 50],
26: iconAnchor: [25, 25],
27: popupAnchor: [-3, -76],
28:
29: });
30:
31: for(var i = 0; i < airports.length; i++)
32: {
33: var airport = airports[i];
34: var city = airport["city"];
35: var name = airport["name"];
36: var lat = airport["lat"];
37: var lon = airport["lon"];
38: customMark(lat, lon, name, airportIcon);
39: }
40: }
41:
42: function onLocationClosest(e)
43: {
44: var handleResponse = function (status, response) {
45: alert(response)
46: }
47:
48: var handleStateChange = function(xmlhttp)
49: {
50: return (xmlhttp.readyState == 4) ? handleResponse(xmlhttp.status, xmlhttp.response) : null;
51: }
52:
53: var xmlhttp = new XMLHttpRequest();
54: xmlhttp.open("POST", "/", false);
55: xmlhttp.setRequestHeader('Content-Type', 'application/json');
56: xmlhttp.onreadystatechange = handleStateChange(xmlhttp);
57:
58: seen = [];
59: xmlhttp.send(JSON.stringify(e,function(key, val)
60: {
61: if (val != null && typeof val == "object") {
62: if (seen.indexOf(val) >= 0)
63: return
64: seen.push(val)
65: }
66: return val
67: }));
68:
69: var response = JSON.parse(xmlhttp.response);
70: markAirportsAndYourPosition(response, e);
71: }
72:
73: function closestAirports()
74: {
75: map.on('locationfound', onLocationClosest);
76: map.locate();
77: }
Conclusion
I have really enjoyed this project. The Flask+MongoDB+LeafLet mixture has been a very good option. I expect to use ArangoDB in the future.
I would like to thank Openflights again. Without their data, this PoC would not have been possible.
The app should be running in http://airports-dollbox.rhcloud.com/ .
This project is in github .
I would like to thank Openflights again. Without their data, this PoC would not have been possible.
The app should be running in http://airports-dollbox.rhcloud.com/ .
This project is in github .