Skip to content

Instantly share code, notes, and snippets.

@ericrobskyhuntley
Last active April 10, 2024 15:01
Show Gist options
  • Save ericrobskyhuntley/54b56b83d567adbfed0bcef41192c38d to your computer and use it in GitHub Desktop.
Save ericrobskyhuntley/54b56b83d567adbfed0bcef41192c38d to your computer and use it in GitHub Desktop.
Demoing functions to offset circles by a data-driven quantity (hack necessary because of lack of data-driven styling on `'circle-translate'`).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Add a line to a map using a GeoJSON source</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.2.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.2.0/mapbox-gl.js"></script>
<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 = 'YOUR TOKEN';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [-71.09965838868389, 42.36758222733826],
zoom: 14
});
map.getCanvas().style.cursor = 'default'
var json = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"id": 1,
"units": 1
},
"geometry": {
"coordinates": [
-71.09965838868389,
42.36758222733826
],
"type": "Point"
}
},
{
"type": "Feature",
"properties": {
"id": 2,
"units": 3
},
"geometry": {
"coordinates": [
-71.09965838868389,
42.36758222733826
],
"type": "Point"
}
},
{
"type": "Feature",
"properties": {
"id": 3,
"units": 5
},
"geometry": {
"coordinates": [
-71.09965838868389,
42.36758222733826
],
"type": "Point"
}
}
]
}
function storeCoords() {
json.features.forEach(function(item) {
item.properties.x = item.geometry.coordinates[0],
item.properties.y = item.geometry.coordinates[1]
})
return(json)
}
let zero;
let offset = false;
function firstFrame(timestamp) {
zero = document.timeline.currentTime;
animateOffset(zero);
}
function animateOffset(timestamp, dur = 150) {
const v = (timestamp - zero);
let value = (offset) ? 1 - (v / dur) : v / dur;
if (value >= 0 && value <= 1) {
map.getSource('points').setData(pointOffset(prop = value))
requestAnimationFrame((t) => animateOffset(t));
} else offset = !offset;
}
function pointOffset(prop, bbox_prop = 0.05) {
const bbox = map.getBounds()
const r = Math.min(
Math.abs(bbox._ne.lng - bbox._sw.lng),
Math.abs(bbox._ne.lat - bbox._sw.lat)
) * bbox_prop
const rad = (2 * Math.PI / json.features.length)
json.features.forEach(function (item, index) {
item.geometry.coordinates = [
item.properties.x + ((Math.sin(rad * index) * r) * prop),
item.properties.y + ((Math.cos(rad * index) * r) * prop)
]
})
return(json)
}
map.on('load', () => {
map.addSource('points', {
'type': 'geojson',
'data': storeCoords()
});
map.addLayer({
'id': 'points',
'type': 'circle',
'source': 'points',
'paint': {
'circle-color': '#f00',
'circle-stroke-color': '#fff',
'circle-radius': ['*', ['get', 'units'], 5],
'circle-stroke-opacity': 1,
'circle-stroke-width': 1,
'circle-opacity': 0.5
}
});
});
map.on('mouseenter', 'points', () => {
map.getCanvas().style.cursor = 'pointer'
})
map.on('mouseleave', 'points', () => {
map.getCanvas().style.cursor = 'default'
})
map.on('click', 'points', (e) => {
e.preventDefault();
(!offset) ? requestAnimationFrame(firstFrame) : undefined;
})
map.on('click', function(e) {
if (e.defaultPrevented === false && offset) {
requestAnimationFrame(firstFrame);
}
})
map.on('zoom', () => {
let prop = (offset) ? 1 : 0;
map.getSource('points').setData(pointOffset(prop = prop))
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment