Public
Edited
Feb 13
2 forks
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
one_vessel.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// // request animation frame --- not needed, because Observable
// var requestAnimationFrame =
// window.requestAnimationFrame ||
// window.mozRequestAnimationFrame ||
// window.webkitRequestAnimationFrame ||
// window.msRequestAnimationFrame;

// window.cancelRequestAnimFrame = (function () {
// return (
// window.cancelAnimationFrame ||
// window.webkitCancelRequestAnimationFrame ||
// window.mozCancelRequestAnimationFrame ||
// window.oCancelRequestAnimationFrame ||
// window.msCancelRequestAnimationFrame ||
// clearTimeout
// );
// })();

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>`;

// Give the container dimensions.
yield container;
if (show_3) {
play;

// Create a new map with `mapboxgl.Map`
// use `container`
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());
}
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more