-
Notifications
You must be signed in to change notification settings - Fork 12
Programming Guide
mapmap.js turns an SVG element into an interactive map. Currently, the SVG element must be present in the HTML code, and a reference to it or a selector expression must be passed to the mapmap()
function. Furthermore, as SVG elements don't have an inherent size, you need to set the width and height attributes of the SVG element to specify the aspect ratio and abstract resolution of your map. You can use CSS to override these and define the actual dimensions of your map.
In order to correctly position HTML-based overlays (like legends or mouseover information) on the map, the SVG element must be contained inside a positioned element, i.e. an element with position:
absolute
, relative
or fixed
.
A typical HTML setup would look like:
<style type="text/css">
.mapWrapper {position: relative;}
</style>
<!-- ... -->
<div class="mapWrapper">
<svg id="mapEl" width="800" height="400"></svg>
</div>
The most simple map you can create will just render some GeoJSON:
var map = mapmap('#mapEl')
.geometry('mygeodata.geojson');
mapmap.js supports method chaining, supporting a concise and declarative programming style. This means that most methods of the map object return this
, allowing you to subsequently call another method on the map.
Methods that return values are named getXyz
to distinguish them from chainable methods.
Most methods in the mapmap API take only a minimum amount of parameters -- usually the ones that must always be specified -- and an additional options parameter to pass optional parameters. The options parameter is a plain JavaScript object, and the API Documentation lists the available options and their default values.
// initialize a map with German localization
var map = mapmap('#mapEl', { language: 'de' });
In some cases, there is a default option, so if the options is not an object but a string or function it is assigned to the default option. This is indicated in the API documentation if the options parameter is named like keyOrOptions
or similar - meaning that if `keyOrOptions is not an plain object, it will be assigned to the key option.
map.geometry('mygeodata.geojson', 'iso');
is equivalent to
map.geometry('mygeodata.geojson', { key: 'iso' });
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.
The properties of individual fields or groups of fields can be described by passing metadata specifiers to the map. The meta()
method takes a plain JS object, listing a number of metadata selectors (filednames with wildcard support) and associated metadata specifiers.
A metadata selector is a string containing a fieldname, or a regular expression for more complex matching. String selectors may use the *
wildcard to replace an arbitrary number of characters or the ?
wildcard to replace a single character. By using wildcards, you can assign identical metadata to similar fields - e.g. pop_*
would assign the metadata specification to all fields starting with "pop_".
All metadata specifiers that match a given filed name will be applied to the field in order of their specificity. Specificity is is defined as the length of the string without wildcards, or the length of the regular expression. By exploiting specificity, you can apply metadata specifications in a cascading manner, using wildcards to express properties for a group of fields and then specifying per-field overrides (e.g. the human-readable label for the field) on an individual basis.
map.meta({
'unemployment_*': { // applies to all unemployment fields
label: "Overall Unemployment Rate",
valueLabel: "Unemployment Rate",
numberFormat: '%',
color: colorbrewer.YlOrRd[6]
},
'umemployment_m': { // this is more specific, so will override above
label: "Male Unemployment, 2014"
},
'umemployment_f': {
label: "Female Unemployment, 2014"
}
});
TO DO. See API documentation for a list of available metadata attributes.
TO DO. See API documentation for a list of available metadata attributes.
TO DO. See API documentation for a list of available metadata attributes.
TO DO. See API documentation for a list of available metadata attributes.
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')
});
TO DO
TO DO
TO DO
[Documentation in progress... stay tuned...]
mapmap.js Wiki