tracktracker/src/front/map/vehicles.js

126 lines
3.6 KiB
JavaScript

import * as turfHelpers from "@turf/helpers";
import { scaleZoom, makeBorderColor, makeCourseColor } from "./common";
import network from "../../data/network.json";
const makeVehicleIcon = size => {
const canvas = window.document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.width = size;
canvas.height = size;
const cx = size / 2;
const cy = size / 2;
const iconSize = 0.7 * size;
const borderSize = 0.3 * size;
context.fillStyle = "black";
context.strokeStyle = "black";
context.lineWidth = borderSize;
context.lineJoin = "round";
context.miterLimit = 200000;
context.beginPath();
context.moveTo(cx - 0.3 * iconSize, cy + 0.5 * iconSize);
context.lineTo(cx, cy - 0.5 * iconSize);
context.lineTo(cx + 0.3 * iconSize, cy + 0.5 * iconSize);
context.closePath();
context.stroke();
context.fill();
return {
width: size,
height: size,
data: context.getImageData(0, 0, size, size).data,
};
};
/**
* Add layers that display the live vehicle positions on the map.
* @param {Map} map The map to add the layers to.
* @param {Object.<callable>} handlers Handlers for the map events.
*/
export const addLayers = (map, handlers) => {
map.addImage("vehicle", makeVehicleIcon(128), {sdf: true});
map.addSource("vehicles", {
type: "geojson",
data: turfHelpers.featureCollection([]),
});
for (let [kind, factor] of [
["outer", 0.3],
["border", 0.25],
["inner", 0.2],
]) {
map.addLayer({
id: `vehicles-${kind}`,
type: "symbol",
source: "vehicles",
layout: {
"icon-image": "vehicle",
"icon-size": scaleZoom(factor),
"icon-allow-overlap": true,
"icon-rotation-alignment": "map",
"icon-rotate": ["get", "bearing"],
},
paint: {
"icon-color": ["get", `${kind}Color`],
},
});
}
map.on("click", "vehicles-outer", event => {
// Sort clicked courses in increasing order of departing/arrival time
event.features.sort((feature1, feature2) => {
const course1 = feature1.properties;
const course2 = feature2.properties;
const time1 = (
course1.speed > 0 ? course1.arrivalTime : course1.departureTime
);
const time2 = (
course2.speed > 0 ? course2.arrivalTime : course2.departureTime
);
return time1 - time2;
});
handlers.onVehicleClick(event.features.map(
feature => feature.properties.id
));
});
// Show a pointer cursor when hovering over a vehicle
map.on("mouseenter", "vehicles-outer", () => {
map.getCanvas().style.cursor = "pointer";
});
map.on("mouseleave", "vehicles-outer", () => {
map.getCanvas().style.cursor = '';
});
};
/**
* Update the vehicle positions on the map.
* @param {Map} map The map to update.
* @param {Array<Object>} courses The active courses.
*/
export const update = (map, courses) => {
const features = Object.values(courses);
for (let course of features) {
const props = course.properties;
props.borderColor = network.lines[props.line].color;
props.innerColor = makeCourseColor(props.borderColor);
props.outerColor = makeBorderColor(props.borderColor);
}
map.getSource("vehicles").setData(
turfHelpers.featureCollection(Object.values(courses))
);
};