Skip to content

Instantly share code, notes, and snippets.

@mthh
Last active December 7, 2017 23:54
Show Gist options
  • Save mthh/6acfed31d6004a544b9a90d3585f3a30 to your computer and use it in GitHub Desktop.
Save mthh/6acfed31d6004a544b9a90d3585f3a30 to your computer and use it in GitHub Desktop.
Choropleth map + graduated lines using Mapbox GL JS
license: gpl-3.0
border: no

Choropleth map + graduated lines using Mapbox GL JS

Use topojson.js to get the interior mesh of the study area and use it to compute absolute discontinuities between the mean income per household of these areas.
Finally use Mapbox GL JS API to render a choropleth map and the graduated lines.

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Choropleth map using Mapbox GL JS</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="https://unpkg.com/topojson@3"></script>
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.41.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.41.0/mapbox-gl.css' rel='stylesheet' />
<style>
body {
margin: 0;
padding: 0;
}
#cont {
width: 800px;
height: 600px;
margin: auto;
}
#map {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="cont"><div id='map'></div>
</div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibXRoaCIsImEiOiJjaXBkcmYyOHUwMDBndWVuZnU5YWdka2w2In0.fbZYSGMQgjc2B3_LdWvRbA';
let map;
let polygon_features;
let discont;
const req = new XMLHttpRequest();
req.open('GET', 'GrandParis.topojson', true);
req.onload = () => { prepare(JSON.parse(req.responseText)) };
req.send();
function prepare(data) {
polygon_features = topojson.feature(data, data.objects.GrandParis);
discont = makeDiscont(data, 'GrandParis', 'IncPerTH', 'abs');
map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/dark-v9', // stylesheet location
center:[2.39125, 48.835117], // [lng, lat]
zoom: 9 // starting zoom
});
map.on('load', function() {
map.addLayer({
id: 'communes',
type: 'fill',
'source': {
'type': 'geojson',
'data': polygon_features,
},
paint: {
'fill-color': [
'curve',
['step'],
['number', ['get', 'IncPerTH'], 1],
'#FFEDA0', 19733, '#FED976', 23229, '#FEB24C', 25782, '#FD8D3C', 29701, '#FC4E2A', 35055, '#E31A1C', 49221, '#BD0026', 97000, 'pink'
],
'fill-opacity': 0.95,
},
layout: {}
});
map.addLayer({
id: 'discont',
type: 'line',
'source': {
'type': 'geojson',
'data': discont,
},
paint: {
'line-color': '#7CFC00',
'line-width':[
'curve',
['step'],
['number', ['get', 'disc_value'], 1],
0.5, 5000, 3, 15000, 6, 30000, 9, 69000, 0
],
'line-opacity': 1,
},
layout: {
'line-join': 'round',
'line-cap': 'round',
}
});
});
}
function makeDiscont(topo_to_use, layer, field, discontinuity_type='abs') {
const result_value = new Map(),
topo_mesh = topojson.mesh,
math_max = Math.max,
getId = (a, b) => ([
[a.properties.LIBCOM, b.properties.LIBCOM].join('_'),
[b.properties.LIBCOM, a.properties.LIBCOM].join('_')]),
disc_func = discontinuity_type === 'rel'
? (val_a, val_b) => math_max(val_a / val_b, val_b / val_a)
: (val_a, val_b) => math_max(val_a - val_b, val_b - val_a);
topo_mesh(topo_to_use, topo_to_use.objects[layer], (a, b) => {
if (a !== b) {
let [new_id, new_id_rev] = getId(a, b),
val_a = a.properties[field],
val_b = b.properties[field];
if (val_a == '' || val_a == null || isNaN(+val_a) || val_b == '' || val_b == null || isNaN(+val_b)) {
return;
}
if (!(result_value.get(new_id) || result_value.get(new_id_rev))) {
result_value.set(new_id, disc_func(+val_a, +val_b));
}
}
return false;
});
let entries = Array.from(result_value.entries()),
arr_disc = [],
d_res = [],
nb_ft;
for (let i = 0, n = entries.length; i < n; i++) {
let kv = entries[i];
if (!isNaN(kv[1])) {
arr_disc.push(kv);
}
}
arr_disc.sort((a, b) => a[1] - b[1]);
nb_ft = arr_disc.length;
for (let i = 0; i < nb_ft; i++) {
let id_ft = arr_disc[i][0],
val = arr_disc[i][1],
datum = topo_mesh(topo_to_use, topo_to_use.objects[layer], (a, b) => {
let a_id = id_ft.split('_')[0],
b_id = id_ft.split('_')[1];
const [ref_a_id, ref_b_id] = getId(a, b)[0].split('_');
return a != b && (
ref_a_id === a_id && ref_b_id === b_id
|| ref_a_id === b_id && ref_b_id === a_id);
});
d_res.push([val, {
type: 'Feature',
geometry: datum,
properties: { id: id_ft, disc_value: val },
}]);
}
d_res.sort((a, b) => b[0] - a[0]);
return {
type: 'FeatureCollection',
features: d_res.map(a => a[1]),
};
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment