Public
Edited
Jun 22, 2023
Insert cell
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
{
let container = html`<div id="map_static_mapbox" style='height:400px; width: 100%; background-color: #f2f2f2' /></div>`;

// Da dimensiones al contenedor `container`
yield container;

// el condicional solo es para Observable
if (show_1) {
// Crea un nuevo mapa (el `map` object) con el constructor `mapboxgl.Map` y
// referencia tu contenedor `container`
const map = new mapboxgl.Map({
container,
center: [-5, 54],
zoom: 6.5,
style: "mapbox://styles/mapbox/light-v9"
// scrollZoom: false
});

// convierte los datos a un formato geojson
const xy = vessel.map((d) => [d.lon, d.lat]);
const vessel_data = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
coordinates: xy,
type: "LineString"
}
}
]
};

map.on("load", () => {
map.addSource("boat", {
type: "geojson",
data: vessel_data
});

// añade la capa con la linea
map.addLayer({
type: "line",
source: "boat",
id: "boat",
paint: {
"line-color": color,
"line-width": 2,
"line-opacity": 0.4
}
});
});

// Be careful to clean up the map's resources using \`map.remove()\` whenever
// this cell is re-evaluated.
invalidation.then(() => map.remove());
} else {
("Map hidden");
}
}
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>`;

// Da dimensiones al contenedor `container`
yield container;

if (show_3) {
play;
// Crea un nuevo mapa (el `map` object) con el constructor `mapboxgl.Map` y
// referencia tu contenedor `container`
let map = new mapboxgl.Map({
container,
center: [-5, 54],
zoom: 6.5,
style: "mapbox://styles/mapbox/light-v9",
scrollZoom: false
});

//// Contenedor CANVAS ////
// 1 - obtén el ancho y el alto del contenedor
const width = d3.select(`#${id_mapbox}`).node().clientWidth;
const height = d3.select(`#${id_mapbox}`).node().clientHeight;

// 2 - añade un nuevo div encima de nuestro mapa mapbox
// lo utilizaremos para modificar el CSS y poner nuestras trayectorias encima
d3.select(`#${id_mapbox}`).append("div").attr("id", "animation");

// 3 - añade nuestro nuevo canvas (que utilizaremos para incluir las trayectorias)
// en este nuevo div
const canvas = d3
.select("#animation")
.append("canvas")
.attr("id", id_vessel)
.node();

//// Atributos de canvas ////
// escálalo al doble de su tamaño para que tenga una buena resolución
// esta escala sólo se hace al principio!
canvas.width = 2 * width;
canvas.height = 2 * height;

//// Atributos de contexto --- como queremos pintar? ////
// èste es el sistema habitual para pintar en canvas
const ctx = canvas.getContext("2d");
ctx.globalCompositeOperation = "normal";
ctx.imageSmoothingEnabled = false;
ctx.globalAlpha = 1;
ctx.scale(2, 2); // nuestras escalas tienen que ser las mismas
ctx.clearRect(0, 0, width, height); // igual que mapbox
ctx.linecap = "round";
ctx.linejoin = "round";

// objetos para controlar la animación
let t = t_extent[0];
// const speed = 1000 * 60 * 5; // 5 min
const speed = speed_2;

//// Funciones para dibujar las líneas ////
// crea una proyección para transformar los `lon` y `lat` a los pixeles de mapbox
function projection(lonlat) {
const p = map.project(new mapboxgl.LngLat(lonlat[0], lonlat[1]));
return [p.x, p.y];
}

// tenemos que saber como dibujar la trayectoria
// para lo cual utilizaremos
// d3.line()
// con la proyección de mapbox que hemos creado `proyection`
// por último, indicaremos que vamos a dibujar en nuestro canvas no en SVG
const pathLine = d3
.line()
.x((d) => projection([d.lon, d.lat])[0])
.y((d) => projection([d.lon, d.lat])[1])
.context(ctx);

draw();
// Pintemos
// Guarda tu forma de pintar en una función para que sea fácil
// reutilizarla después
function draw() {
// limpia el fondo de tu canvas -- borra cualquier cosa que tengas
ctx.globalCompositeOperation = "normal";
ctx.fillStyle = "white";
ctx.globalAlpha = 1;
ctx.fillRect(0, 0, width, height);

//// datos del barco ////
// toda la trayectoria pasada
const past = vessel.filter((d) => parseDate(d.timestamp) <= t);

// la trayectoria pasada más cercana al momento actual
// coge los últimos 100 minutos
const current = vessel.filter(
(d) =>
parseDate(d.timestamp) <= t + hour &&
parseDate(d.timestamp) > t - hour
);

//// dibujemos ////
// trayectoria anterior
ctx.strokeStyle = color;
ctx.fillStyle = "none";
ctx.globalAlpha = 0.3;
ctx.lineWidth = 1;
ctx.beginPath();
pathLine(past);
ctx.stroke();

// trayectoria actual
ctx.globalAlpha = 0.6;
ctx.lineWidth = 2;
ctx.beginPath();
pathLine(current);
ctx.stroke();

// barco - última posición
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();
}

// actualiza valores de animación
// se utilizarán para filtrar los datos
// y parar la animación
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
Insert cell
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