2022-07-09 19:50:53 +00:00
|
|
|
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;
|
|
|
|
|
2022-07-09 22:14:00 +00:00
|
|
|
const iconSize = 0.7 * size;
|
|
|
|
const borderSize = 0.3 * size;
|
2022-07-09 19:50:53 +00:00
|
|
|
|
|
|
|
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.
|
2022-07-10 16:34:57 +00:00
|
|
|
* @param {Function} onClick Function to call when a vehicle is clicked.
|
2022-07-09 19:50:53 +00:00
|
|
|
*/
|
2022-07-10 16:34:57 +00:00
|
|
|
export const addLayers = (map, onClick) => {
|
2022-07-09 19:50:53 +00:00
|
|
|
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,
|
2022-07-10 16:34:57 +00:00
|
|
|
"icon-ignore-placement": true,
|
|
|
|
"icon-overlap": "always",
|
2022-07-09 19:50:53 +00:00
|
|
|
"icon-rotation-alignment": "map",
|
|
|
|
"icon-rotate": ["get", "bearing"],
|
|
|
|
},
|
|
|
|
paint: {
|
|
|
|
"icon-color": ["get", `${kind}Color`],
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2022-07-09 22:14:00 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
});
|
|
|
|
|
2022-07-10 16:34:57 +00:00
|
|
|
onClick(event.features.map(
|
2022-07-09 22:14:00 +00:00
|
|
|
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 = '';
|
|
|
|
});
|
2022-07-09 19:50:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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))
|
|
|
|
);
|
|
|
|
};
|