Lunr, Marker Cluster and Async

Summary

  1. lunr.js
  2. Marker Cluster
    Leaflet Improvement to display a lot of points
  3. Refactor code for async.

Session

I showed @AirCrewMentor the work done with the Map project using Leaflet and he liked how I managed to extract all the data and how it was working, despite the many problems there are, like global variables, no tests, no browserify and so on…

At least I got the maps working 😛 .

2387097999-Screenshot 2015-05-06 14.24.22

To convert the CSV (.DAT) file into JSON I used the Papa Parse Software, that worked really good.

After that I enabled a search field that was able to center the map on the result.

3837629339-Screenshot 2015-05-06 14.25.05

1. lunr.js

Viewing the strict of the search field I enabled, he suggested to use Lunr as a search engine.

2. Market Cluster

AirCrewMentor suggested to use a Leaflet Improvement to display a lot of points that group them, really cool feature, looking forward to use it.

3. Refactor code for async.

Here is the code we are going to refactor:

var airports = Papa.parse("./files/airports.dat", {
  download: true,
  header: true,
  complete: function(results) {
    airports = results;
    locateAirports(airports);
  }
});

var markers = new L.FeatureGroup();

var locateAirports = function(airports) {
  console.log(map.getZoom());
  if (map.hasLayer(searchMarker)) {
    map.removeLayer(searchMarker);
  };
  if (map.getZoom() < 5) {
    markers.clearLayers();
    return;
  };
  markers.clearLayers();
  for (var i = 0, j = airports.data.length; i < j; ++i) {
    var airportLatitude = airports.data[i].Latitude,
    airportLongitude = airports.data[i].Longitude;
    if (map.getBounds().contains([+airportLatitude, +airportLongitude])) {
      var marker = L.marker([airportLatitude, airportLongitude])
      .bindPopup('
      <p class="airport_marker">' + airports.data[i]["Name"] + '</p>
      ');
      markers.addLayer(marker);
    }
  }
// if we want the airports to be always visible uncomment following line.
// map.addLayer(markers);
};
var airportsLayerGroup = L.layerGroup([markers]);
var overAirports = {
  "Airports": airportsLayerGroup
};

The big fail is that the code was not designed in an asynchronous manner and this can cause a lot of troubles and is not a best practice, I knew that when I was doing it, but my low experience made me focus on making it work rather than making it work the right way. I hope that getting more experience and better understanding of what asynchronous mean, I am going to be able to use it when properly required.

on asynchronous code we first treat the error case and later the good one.
Please @AirCrewMentor  check this. Thanks.

So AirCrewMentor  showed me a little trick that he uses to make the global variables to go away and at the same time use the asynchronous code.

(function (name, global, definition) {
  // On Node.js/CommonJS
if (typeof module !== 'undefined') {
  module.exports = definition(name, global);

  // On the browser
  } else {
  global[name] = self;
}
}("localeAirports", this, function () {
'use strict';

///////

function localeAirports(map, callback) {
}

///////

return localeAirports;
}));

So using it we started the refactor of the code.

About this refactor I have a couple of remarks I would like to point out.

AirCrewMentor was talking all the time about how I had mixed concepts and that was because the same function that was intended to give me the airports, was updating the map as well, so it seems a novice mistake, I should keep each function with an specific task, so we refactored it taking out the part where it was updating the map.

We got a little into the callback function that is needed on the asynchronous code.

  1. We wrote the function to convert the CSV file using Papa Parse in a way that if the airports variable already had the value asigned, it would use the callback to go back to the function that called it. Otherwise it will convert it, so this way we can assure the process only one time.
var airports;
///////
function getAirports(callback) {
  if (airports) {
    callback(null, airports);
    } else {
    Papa.parse("./files/airports.dat", {
      download: true,
      header: true,
      complete: function(results) {
        airports = results;
        callback(null, airports)
      }
    });
  }
}
  1. Converted the function so it was the locateAirports who was calling always the getAirports function, waiting for it to return the airports, otherwise exiting the function preventing the code to break.

  2. Then if the airports variable was returned it would proceed and it would finally give back the results, that we would use in other function to update the map. So we extracted the part of the function that was updating the map and let this one with a single purpose.

function locateAirports(map, callback) {
  getAirports(function(error, airports) {
    if (error) {
      callback(error);
      return;
    };
    var results = [];

    for (var i = 0, j = airports.data.length; i < j; ++i) {
      var airportLatitude = airports.data[i].Latitude,
      airportLongitude = airports.data[i].Longitude;
      if (map.getBounds().contains([+airportLatitude, +airportLongitude])) {
        var marker = L.marker([airportLatitude, airportLongitude])
        .bindPopup('
        <p class="airport_marker">' + airports.data[i]["Name"] + '</p>
        ');
        results.push(marker);
      }
    }
  callback(null, results);
  });
}

Remaks.

  • use of the command ls -hal where adding the h give use the unit of measure.

We get this result:

$ ls -hal
total 6408
drwxr-xr-x 5 username staff 170B May 3 00:55 .
drwxr-xr-x 17 username staff 578B May 4 15:34 ..
-rw-r--r-- 1 username staff 830K May 3 00:55 airports.dat
-rw-r--r-- 1 username staff 2.3M May 3 00:55 routes.dat

Instead of this:

$ ls -all
total 6408
drwxr-xr-x 5 username staff 170 May 3 00:55 .
drwxr-xr-x 17 username staff 578 May 4 15:34 ..
-rw-r--r-- 1 username staff 850419 May 3 00:55 airports.dat
-rw-r--r-- 1 username staff 2375638 May 3 00:55 routes.dat

Notice the 2.3M instead of 2375638. Much more cleaner and readable.

Homework.

  • Implement Cluster and Lunr.js
  • Read Javascript: The good parts
Thanks @AirCrewMentor . I am happy you liked the work. 🙂
Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s