tracktracker/src/front/map/vehicles.js

234 lines
7.5 KiB
JavaScript
Raw Normal View History

// import { getVectorContext } from "ol/render";
// import Point from "ol/geom/Point";
// import { fromExtent } from 'ol/geom/Polygon';
// import { Style, Icon } from "ol/style";
import * as turfHelpers from "@turf/helpers";
import { scaleZoom, makeBorderColor, makeCourseColor } from "./common";
import network from "../../data/network.json";
const VEHICLE_SIZE = 15;
const VEHICLE_BORDER = 10;
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.4 * size;
const borderSize = 0.2 * 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.
*/
export const addLayers = map => {
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`],
},
});
}
};
/**
* 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))
);
};
// const courseStyle = cacheStyle(
// lineColor => {
// const icon = window.document.createElement("canvas");
// const shapeSize = sizes.courseSize;
// const iconSize = sizes.courseSize + sizes.courseOuterBorder;
// icon.width = iconSize;
// icon.height = iconSize;
// const cx = icon.width / 2;
// const cy = icon.height / 2;
// const iconCtx = icon.getContext("2d");
// for (const [color, size] of [
// [makeBorderColor(lineColor), sizes.courseOuterBorder],
// [lineColor, sizes.courseBorder],
// [makeCourseColor(lineColor), sizes.courseInnerBorder]
// ]) {
// iconCtx.fillStyle = color;
// iconCtx.strokeStyle = color;
// iconCtx.lineWidth = size;
// iconCtx.lineJoin = "round";
// iconCtx.miterLimit = 200000;
// iconCtx.beginPath();
// iconCtx.moveTo(cx - 0.5 * shapeSize, cy - 0.3 * shapeSize);
// iconCtx.lineTo(cx + 0.5 * shapeSize, cy);
// iconCtx.lineTo(cx - 0.5 * shapeSize, cy + 0.3 * shapeSize);
// iconCtx.closePath();
// iconCtx.stroke();
// iconCtx.fill();
// }
// return new Style({
// image: new Icon({
// img: icon,
// imgSize: [icon.width, icon.height]
// })
// });
// },
// );
// export const setupCoursesAnimation = (
// map, coursesSimulation, stopsLayer, onClick
// ) => {
// const view = map.getView();
// // Draw courses directly on the map
// map.on("postcompose", ev => {
// coursesSimulation.update();
// // The normal way to access a layers vector context is through the
// // `postrender` event of that layer. However, `postrender` is not
// // triggered when no feature of the layer is inside the current
// // bounding box, but we want to draw vehicles in between stops even
// // if no stop is visible. This hack listens to the global `postcompose`
// // event, which is always triggered at every frame, and reconstructs
// // the stops layers vector context from internal variables
// /* eslint-disable no-underscore-dangle */
// if (stopsLayer.renderer_) {
// ev.context = stopsLayer.renderer_.context;
// ev.inversePixelTransform =
// stopsLayer.renderer_.inversePixelTransform;
// /* eslint-enable no-underscore-dangle */
// const ctx = getVectorContext(ev);
// const bbox = fromExtent(map.getView().calculateExtent());
// bbox.scale(1.05);
// for (const course of Object.values(coursesSimulation.courses)) {
// if (!bbox.intersectsCoordinate(course.position)) {
// continue;
// }
// const point = new Point(course.position);
// const color = network.lines[course.line].color;
// const style = courseStyle(color);
// style.getImage().setRotation(
// view.getRotation() +
// course.angle
// );
// ctx.setStyle(style);
// ctx.drawGeometry(point);
// }
// }
// map.render();
// });
// map.on("singleclick", ev => {
// const mousePixel = map.getPixelFromCoordinate(ev.coordinate);
// const maxDistance = sizes.courseSize + sizes.courseInnerBorder;
// const clicked = [];
// for (const course of Object.values(coursesSimulation.courses)) {
// const coursePixel = map.getPixelFromCoordinate(course.position);
// const dx = mousePixel[0] - coursePixel[0];
// const dy = mousePixel[1] - coursePixel[1];
// const distance = dx * dx + dy * dy;
// if (distance <= maxDistance * maxDistance) {
// clicked.push(course.id);
// }
// }
// // Sort selected courses in increasing departing/arrival time
// clicked.sort((id1, id2) => {
// const course1 = coursesSimulation.courses[id1];
// const course2 = coursesSimulation.courses[id2];
// const time1 = (
// course1.state === "moving"
// ? course1.arrivalTime
// : course1.departureTime
// );
// const time2 = (
// course2.state === "moving"
// ? course2.arrivalTime
// : course2.departureTime
// );
// return time1 - time2;
// });
// onClick(clicked);
// });
// };