-
Notifications
You must be signed in to change notification settings - Fork 12
Programming Guide
Usually, browser-based applications require an asynchronous programming model using callback functions if external data should be loaded. If multiple data files must be loaded, this can lead to complex code to ensure all resources have been loaded before proceeding with their processing. mapmap.js handles the loading of geometry and data internally and exposes a pseudo-synchronous API for primary functionality that ensures all resources have been loaded before any operations are performed on them.
// The map will be drawn and the interaction handler installed
// *after* the geometry file has loaded
map.geometry('admin.topojson', 'iso')
.choropleth('population')
.on('click', mapmap.zoom());If you need to execute code after all resources specified so far, you can use the .then() method, providing a callback function.
map.geometry('admin.topojson', 'iso')
.choropleth('population')
.then(function(){
console.log("Map loaded!");
});The .then() method also allows the map object to be used as a Thenable.
Data from CSV or JSON files can be joined with features specified in GeoJSON even if the structure or field names do not match. Simple joins can be performed by specifying the field names that should be used as primary keys to identify matching entities.
// Join geometry identified by the key 'iso' with
// data from a CSV where the key field is called 'code'
map.geometry('districts.geojson', 'iso')
.data('population.csv', 'code')
.choropleth('pop_count');To support more complex transformations in a modular fashion, mapmap utilizes the MapReduce programming model. MapReduce is a two-step process: First, each entity that should be part of the result is mapped to a key. Subsequently, all entities sharing identical keys are processed and reduced to a single entity. Many data transformations needed in thematic mapping (e.g. filtering, aggregation, splitting, transforming keys) can be efficiently implemented using the MapReduce paradigm.
Both steps in MapReduce are implemented as functions, that are called for each item separately - map is called with each data item, reduce with each key emitted by map and an Array of items mapped to that key. Both functions receive an emit() function as additional parameter, which is used to emit the result(s).
For example, here is some code that aggregates population data for administrative districts from their municipalities.
// This assumes the data is available only for municipalities
// so we have to sum it up
map.geometry('districts.geojson', 'iso')
.data('population.csv', {
map: function(d, emit) {
// only emit units on municipality level
if (d.code.length == 5) {
emit(d.code.substring(0,3), d);
}
},
reduce: function(key, values, emit) {
// sum up population count from municipalities
var result = {
pop_count: 0
};
for (var i=0; i<values.length; i++) {
result.pop_count += values[i].pop_count;
}
emit(key, result);
}
});Mapmap uses the datadata library internally, which provides the MapReduce implementation and some helper functions for utilizing it. For example, using datadata helpers, the above code can be written like this:
// Mapmap exposes a reference to the datadata library
var dd = mapmap.datadata;
map.geometry('districts.geojson', 'iso')
.data('population.csv', {
map: dd.map.key('code', function(c) {
return c.length == 5 && c.substring(0,3);
}),
reduce: dd.emit.sum('pop_count')
});mapmap.js Wiki