|
|
@ -1,15 +1,8 @@ |
|
|
|
// 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"); |
|
|
@ -20,8 +13,8 @@ const makeVehicleIcon = size => { |
|
|
|
const cx = size / 2; |
|
|
|
const cy = size / 2; |
|
|
|
|
|
|
|
const iconSize = 0.4 * size; |
|
|
|
const borderSize = 0.2 * size; |
|
|
|
const iconSize = 0.7 * size; |
|
|
|
const borderSize = 0.3 * size; |
|
|
|
|
|
|
|
context.fillStyle = "black"; |
|
|
|
context.strokeStyle = "black"; |
|
|
@ -47,8 +40,9 @@ const makeVehicleIcon = size => { |
|
|
|
/** |
|
|
|
* 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 => { |
|
|
|
export const addLayers = (map, handlers) => { |
|
|
|
map.addImage("vehicle", makeVehicleIcon(128), {sdf: true}); |
|
|
|
|
|
|
|
map.addSource("vehicles", { |
|
|
@ -77,6 +71,37 @@ export const addLayers = map => { |
|
|
|
}, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
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 = ''; |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -98,136 +123,3 @@ export const update = (map, courses) => { |
|
|
|
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 layer’s 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 layer’s 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);
|
|
|
|
// });
|
|
|
|
// };
|
|
|
|