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:

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 .
    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:
    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 :


    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 .