Deck.gl Example For Filtering and Basic Transitions
This is a basic example for displaying what can be achieved using the deck.gl transitions. We will simply make use of the DataFilterExtension deck.gl offers in order to periodically highlight multiple points on our map.
This is basic example for displaying what can be achieved using the deck.gl transitions. We will simply make use of the DataFilterExtension deck.gl offers in order to periodically highlight multiple points on our map.
TripsLayer might be seen as a better option for transitions with the support of timestamps. But we would like to show that displaying transitions and animating your layer is possible on all the deck.gl layers.
Initialising the Map
By default deck.gl renders in the body tag if we do not define a container element. We will use the default body for this example. Initial view state is roughly the center of France. Don't forget to use your Clockwork Micro API key.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
<script src="https://unpkg.com/maplibre-gl@3.5.2/dist/maplibre-gl.js"></script>
<title>Deck.gl Transitions | CWM</title>
<style>
body {
margin: 0;
padding: 0;
position: relative;
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<script>
const url = "https://maps.clockworkmicro.com/streets/v1/style?x-api-key=";
const apiKey = "cwm_api_key";
const deckgl = new deck.DeckGL({
mapStyle: url + apiKey,
initialViewState: {
longitude: 2.45646,
latitude: 47.083511,
zoom: 6,
},
controller: true,
});
</script>
</body>
</html>
Creating the Dataset
We are going to need a mock dataset for the points. As our dataset we will create colours and positions at random. We will also create a time key for filtering the points at a given frame.
const itemNum = 5000; // number points to be rendered in total
const data = createRandomLatLng(itemNum);
let min = 0;
const span = 500;
const maxTime = data[data.length - 1].time;
function createRandomLatLng(n) {
let result = [];
for (let i = 0; i < n; i++) {
let point = {};
point.position = [Math.random() * 7 - 1, Math.random() * 8 + 43];
point.color = [
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
];
point.time = i;
result.push(point);
}
return result;
}
Layers and Transitions
We create the layers using a function in order to set the range for filter and the layers dynamically. This also allows us to make use of the requestAnimationFrame method. Using this method we will be creating the smoothest animations.
We could replace the current logic for animating with the setInterval but we would end up with an animation that would most likely look jerky and not right.
function redrawLayer() {
if (min < maxTime) min += 10;
else min = -span;
deckgl.setProps({
layers: [
new deck.ScatterplotLayer({
id: "highlighted",
data,
getPosition: (d) => d.position,
getFillColor: (d) => d.color,
radiusMinPixels: 10,
getFilterValue: (d) => d.time,
filterRange: [min, min + span],
filterSoftRange: [min + span * 0.8, min + span],
extensions: [new deck.DataFilterExtension({ filterSize: 1 })],
}),
new deck.ScatterplotLayer({
id: "all",
data,
getPosition: (d) => d.position,
getRadius: 0.5,
getFillColor: [0, 0, 0],
radiusMinPixels: 1,
radiusMaxPixels: 1,
}),
],
});
requestAnimationFrame(redrawLayer);
}
requestAnimationFrame(redrawLayer);
For more information about the filtering logic you can visit the docs.
Full code for the project is the following.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
<script src="https://unpkg.com/maplibre-gl@3.5.2/dist/maplibre-gl.js"></script>
<title>Deck.gl Transitions | CWM</title>
<style>
body {
margin: 0;
padding: 0;
position: relative;
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<script>
const url = "https://maps.clockworkmicro.com/streets/v1/style?x-api-key=";
const apiKey = "cwm_api_key";
const itemNum = 5000;
const data = createRandomLatLng(itemNum);
let min = 0;
const span = 500;
const maxTime = data[data.length - 1].time;
function createRandomLatLng(n) {
let result = [];
for (let i = 0; i < n; i++) {
let point = {};
point.position = [Math.random() * 7 - 1, Math.random() * 8 + 43];
point.color = [
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
];
point.time = i;
result.push(point);
}
return result;
}
const deckgl = new deck.DeckGL({
mapStyle: url + apiKey,
initialViewState: {
longitude: 2.45646,
latitude: 47.083511,
zoom: 6,
},
controller: true,
});
redrawLayer();
function redrawLayer() {
if (min < maxTime) min += 10;
else min = -span;
deckgl.setProps({
layers: [
new deck.ScatterplotLayer({
id: "highlighted",
data,
getPosition: (d) => d.position,
getFillColor: (d) => d.color,
radiusMinPixels: 10,
getFilterValue: (d) => d.time,
filterRange: [min, min + span],
filterSoftRange: [min + span * 0.8, min + span],
extensions: [new deck.DataFilterExtension({ filterSize: 1 })],
}),
new deck.ScatterplotLayer({
id: "all",
data,
getPosition: (d) => d.position,
getRadius: 0.5,
getFillColor: [0, 0, 0],
radiusMinPixels: 1,
radiusMaxPixels: 1,
}),
],
});
requestAnimationFrame(redrawLayer);
}
requestAnimationFrame(redrawLayer);
</script>
</body>
</html>