Skip to content

Instantly share code, notes, and snippets.

@mastersigat
Last active January 23, 2021 13:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mastersigat/9835cd8398adaceb3b44863681e71d2e to your computer and use it in GitHub Desktop.
Save mastersigat/9835cd8398adaceb3b44863681e71d2e to your computer and use it in GitHub Desktop.
#MapboxGL / Clusters thématiques
license: mit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Create and style clusters</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v2.0.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.0.1/mapbox-gl.css" rel="stylesheet" />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibmluYW5vdW4iLCJhIjoiY2pjdHBoZGlzMnV4dDJxcGc5azJkbWRiYSJ9.o4dZRrdHcgVEKCveOXG1YQ';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/dark-v10',
center: [-1.68, 48.11],
zoom: 10.3,
pitch : 20,
minZoom:10.3,
maxZoom:18
});
// CLUSTER THEMATIQUE
var mag1 = ['match',['get', 'type'],
['Appartement'],
false,
true
];
var mag2 = [
'match',
['get', 'type'],
['Maison'],
false,
true
];
var colors = ['#79d320', '#fe4fc6'];
map.on('load', function () {
map.addSource('earthquakes', {
'type': 'geojson',
'data': 'https://raw.githubusercontent.com/mastersigat/data/main/DVFRM.geojson',
'cluster': true,
'clusterMaxZoom': 15,
'clusterRadius': 50,
'clusterProperties': {
// keep separate counts for each magnitude category in a cluster
'mag1': ['+', ['case', mag1, 1, 0]],
'mag2': ['+', ['case', mag2, 1, 0]]
}
});
map.addLayer({
'id': 'earthquake_label',
'type': 'symbol',
'source': 'earthquakes',
'filter': ['!=', 'cluster', true],
'layout': {
'text-field': [
'number-format',
['get', 'mag'],
{ 'min-fraction-digits': 0, 'max-fraction-digits': 0 }
],
'text-font': ['Open Sans Semibold', 'Arial Unicode MS Bold'],
'text-size': 1
},
'paint': {
'text-color': [
'case',
['<', ['get', 'mag'], 1],
'black',
'white'
]
}
});
var markers = {};
var markersOnScreen = {};
function updateMarkers() {
var newMarkers = {};
var features = map.querySourceFeatures('earthquakes');
for (var i = 0; i < features.length; i++) {
var coords = features[i].geometry.coordinates;
var props = features[i].properties;
if (!props.cluster) continue;
var id = props.cluster_id;
var marker = markers[id];
if (!marker) {
var el = createDonutChart(props);
marker = markers[id] = new mapboxgl.Marker({
element: el
}).setLngLat(coords);
}
newMarkers[id] = marker;
if (!markersOnScreen[id]) marker.addTo(map);
}
for (id in markersOnScreen) {
if (!newMarkers[id]) markersOnScreen[id].remove();
}
markersOnScreen = newMarkers;
}
map.on('data', function (e) {
if (e.sourceId !== 'earthquakes' || !e.isSourceLoaded) return;
map.on('move', updateMarkers);
map.on('moveend', updateMarkers);
updateMarkers();
});
});
function createDonutChart(props) {
var offsets = [];
var counts = [
props.mag1,
props.mag2
];
var total = 0;
for (var i = 0; i < counts.length; i++) {
offsets.push(total);
total += counts[i];
}
var fontSize =
total >= 1000 ? 15 : total >= 100 ? 12 : total >= 6 ? 10 : 10;
var r = total >= 1000 ? 30 : total >= 100 ? 22 : total >= 10 ? 14 : 18;
var r0 = Math.round(r * 0.6);
var w = r * 2;
var html =
'<div><svg width="' +
w +
'" height="' +
w +
'" viewbox="0 0 ' +
w +
' ' +
w +
'" text-anchor="middle" style="font: ' +
fontSize +
'px sans-serif; display: block">';
for (i = 0; i < counts.length; i++) {
html += donutSegment(
offsets[i] / total,
(offsets[i] + counts[i]) / total,
r,
r0,
colors[i]
);
}
html +=
'<circle cx="' +
r +
'" cy="' +
r +
'" r="' +
r0 +
'" fill="white" /><text dominant-baseline="central" transform="translate(' +
r +
', ' +
r +
')">' +
total.toLocaleString() +
'</text></svg></div>';
var el = document.createElement('div');
el.innerHTML = html;
return el.firstChild;
}
function donutSegment(start, end, r, r0, color) {
if (end - start === 1) end -= 0.00001;
var a0 = 2 * Math.PI * (start - 0.25);
var a1 = 2 * Math.PI * (end - 0.25);
var x0 = Math.cos(a0),
y0 = Math.sin(a0);
var x1 = Math.cos(a1),
y1 = Math.sin(a1);
var largeArc = end - start > 0.5 ? 1 : 0;
return [
'<path d="M',
r + r0 * x0,
r + r0 * y0,
'L',
r + r * x0,
r + r * y0,
'A',
r,
r,
0,
largeArc,
1,
r + r * x1,
r + r * y1,
'L',
r + r0 * x1,
r + r0 * y1,
'A',
r0,
r0,
0,
largeArc,
0,
r + r0 * x0,
r + r0 * y0,
'" fill="' + color + '" />'
].join(' ');
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment