{
const id_mapbox = "map_animacion";
const id_vessel = "vessel_animacion";
let container = html`<div id="${id_mapbox}" style='height:400px; width: 100%; background-color: #f3f3f3' /></div>`;
yield container;
if (show_3) {
play;
let map = new mapboxgl.Map({
container,
center: [-5, 54],
zoom: 6.5,
style: "mapbox://styles/mapbox/light-v9"
// scrollZoom: false
});
//// CANVAS ////
// 1 - obtain the width and height of the container
const width = d3.select(`#${id_mapbox}`).node().clientWidth;
const height = d3.select(`#${id_mapbox}`).node().clientHeight;
// 2 - add the new div on top of the mapbox map
// we will use it to modify the CSS and add the trajectories on top
d3.select(`#${id_mapbox}`).append("div").attr("id", "fixed");
// 3 - add the new canvas in this new div
// (we will use it to include the trajectories)
const canvas = d3
.select("#fixed")
.append("canvas")
.attr("id", id_vessel)
.node();
//// Canvas Attributes ////
// Scale it to twice its size to have a better resolution
// This scale is used only at the beginning
canvas.width = 2 * width;
canvas.height = 2 * height;
//// Context attributes - how do we want to paint? ////
// This is the usual way to paint in canvas
const ctx = canvas.getContext("2d");
ctx.scale(2, 2); // the scales must be the same
ctx.clearRect(0, 0, width, height); // same sizes as in Mapbox
ctx.linecap = "round";
ctx.linejoin = "round";
//// Functions to call the lines ////
// Use a projection to transform the long lats into pixels
function projection(lonlat) {
const p = map.project(new mapboxgl.LngLat(lonlat[0], lonlat[1]));
return [p.x, p.y];
}
// objects to control the animation
let t = t_extent[0];
// const speed = 1000 * 60 * 5; // 5 min
const speed = speed_2;
// Use d3.line()
// and add the Mapbox projection
// tell d3.line() that we will paint the line in canvas
const pathLine = d3
.line()
.x((d) => projection([d.lon, d.lat])[0])
.y((d) => projection([d.lon, d.lat])[1])
.context(ctx);
draw();
function draw() {
// restart canvas
ctx.globalCompositeOperation = "normal";
ctx.fillStyle = "white";
ctx.globalAlpha = 1;
ctx.fillRect(0, 0, width, height);
//// vessel data ////
// full trajectory
const past = vessel.filter((d) => parseDate(d.timestamp) <= t);
// draw latest trajectories
// picks the last 100 minutes
const current = vessel.filter(
(d) =>
parseDate(d.timestamp) <= t + hour * 1.5 &&
parseDate(d.timestamp) > t - hour * 1.5
);
// previous trajectory
ctx.strokeStyle = color;
ctx.fillStyle = "none";
ctx.globalAlpha = 0.3;
ctx.lineWidth = 1;
ctx.beginPath();
pathLine(past);
ctx.stroke();
// draw "current" trajectory
ctx.globalAlpha = 0.6;
ctx.lineWidth = 2;
ctx.beginPath();
pathLine(current);
ctx.stroke();
// boat - last position
if (current.length > 0) {
var xy = projection([
current[current.length - 1].lon,
current[current.length - 1].lat
]);
ctx.fillStyle = color;
ctx.strokeStyle = color_2;
ctx.globalAlpha = 1;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(xy[0], xy[1], 4, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.closePath();
}
// update the animation values
if (t < t_extent[1]) {
t = t + speed;
window.requestAnimationFrame(draw);
} else if (t >= t_extent[1]) {
t = t_extent[1];
}
}
// Be careful to clean up the map's resources using \`map.remove()\` whenever
// this cell is re-evaluated.
invalidation.then(() => map.remove());
}
}