Compare commits

...

2 Commits

Author SHA1 Message Date
Mattéo Delabre d688bff813
Load simulation in front 2020-07-23 17:29:35 +02:00
Mattéo Delabre f054b11743
Exclude unknown data from realtime 2020-07-23 01:02:22 +02:00
4 changed files with 156 additions and 28 deletions

View File

@ -1,7 +1,4 @@
require('regenerator-runtime/runtime');
const {createMap} = require('./map');
require('../tam/simulation');
createMap(/* map = */ 'map');

View File

@ -21,6 +21,7 @@ const color = require('color');
const mapboxToken = `pk.eyJ1IjoibWF0dGVvZGVsYWJyZSIsImEiOiJja2NxaTUyMmUwcmFhMn\
h0NmFsdzQ3emxqIn0.cyxF0h36emIMTk3cc4VqUw`;
const simulation = require('../tam/simulation');
const network = require('../tam/network.json');
const getRouteColors = routes =>
@ -39,7 +40,7 @@ const getRouteColors = routes =>
return ['#FFFFFF'];
};
const makeDataSources = async () =>
const makeDataSources = () =>
{
const segmentsSource = new VectorSource();
const stopsSource = new VectorSource();
@ -101,7 +102,7 @@ const stopsStyle = feature => new Style({
}),
});
const createMap = async (target) =>
const createMap = target =>
{
// Map background
const backgroundSource = new XYZSource({
@ -116,8 +117,8 @@ const createMap = async (target) =>
source: backgroundSource,
});
// Data overlay
const {segmentsSource, stopsSource} = await makeDataSources();
// Static data overlay
const {segmentsSource, stopsSource} = makeDataSources();
const segmentsBorderLayer = new VectorLayer({
source: segmentsSource,
@ -144,6 +145,63 @@ const createMap = async (target) =>
updateWhileAnimating: true,
});
// Dynamic data overlay
const coursesSource = new VectorSource();
const onFrame = courses =>
{
// Remove stale courses
for (let feature of coursesSource.getFeatures())
{
if (!(feature.getId() in courses))
{
coursesSource.removeFeature(feature);
}
}
// Add new courses or update existing courses
const newFeatures = [];
for (let [courseId, course] of Object.entries(courses))
{
if ('position' in course)
{
const feature = coursesSource.getFeatureById(courseId);
const coords = proj.fromLonLat([
course.position.lon,
course.position.lat
]);
if (feature === null)
{
const feature = new Feature({
colors: ['#FF0000'],
geometry: new Point(coords)
});
feature.setId(courseId);
newFeatures.push(feature);
}
else
{
feature.getGeometry().setCoordinates(coords);
}
}
}
coursesSource.addFeatures(newFeatures);
};
simulation.run(onFrame);
const coursesLayer = new VectorLayer({
source: coursesSource,
style: stopsStyle,
updateWhileInteracting: true,
updateWhileAnimating: true,
});
// Setup map
const map = new Map({
target,
@ -152,6 +210,7 @@ const createMap = async (target) =>
segmentsBorderLayer,
segmentsInnerLayer,
stopsLayer,
coursesLayer,
],
view: new View({
center: proj.fromLonLat([3.88, 43.605]),

View File

@ -1,5 +1,6 @@
const tam = require('./sources/tam');
const util = require('../util');
const network = require('./network.json');
/**
* Comparison function between two stop passings.
@ -57,6 +58,28 @@ const getCourses = () => new Promise((res, rej) =>
if (!util.isObject(entry))
{
// Filter courses to only keep those referring to known data
for (let courseId of Object.keys(courses))
{
const course = courses[courseId];
if (!(course.line in network.lines))
{
delete courses[courseId];
}
else
{
for (let passing of course.nextPassings)
{
if (!(passing.stopId in network.stops))
{
delete courses[courseId];
break;
}
}
}
}
// End of courses information stream. Sort next stops by increasing
// arrival time in each course then save result in memory cache
for (let course of Object.values(courses))

View File

@ -71,12 +71,6 @@ const updateFromTam = async (courses) =>
for (let [id, course] of Object.entries(currentCourses))
{
// Only track selected lines
if (!['1', '2', '3', '4'].includes(course.line))
{
continue;
}
// Find out the next stop, ignoring the ones that are in the past
let nextStop = null;
let arrivalTime = null;
@ -155,6 +149,7 @@ const updatePositions = (courses, time) =>
{
if (course.state === 'moving')
{
// Increase the travelled distance respective to the current speed
const delta = course.speed * time;
const segment = getCurrentSegment(course);
@ -168,20 +163,66 @@ const updatePositions = (courses, time) =>
{
course.traveledDistance += delta;
}
// Recompute updated position
const departureStop = network.stops[course.departureStop];
const arrivalStop = network.stops[course.arrivalStop];
const nextNodeIndex = segment.points.findIndex(
({distance}) => distance >= course.traveledDistance);
if (nextNodeIndex === 0)
{
course.position = {
lat: departureStop.lat,
lon: departureStop.lon
};
}
else if (nextNodeIndex === -1)
{
course.position = {
lat: arrivalStop.lat,
lon: arrivalStop.lon
};
}
else
{
const previousNode = segment.points[nextNodeIndex - 1];
const nextNode = segment.points[nextNodeIndex];
const curLength = course.traveledDistance
- previousNode.distance;
const totalLength = nextNode.distance
- previousNode.distance;
const progression = curLength / totalLength;
course.position = {
lat: progression * nextNode.lat
+ (1 - progression) * previousNode.lat,
lon: progression * nextNode.lon
+ (1 - progression) * previousNode.lon,
};
}
}
else
{
const currentStop = network.stops[course.currentStop];
course.position = {
lat: currentStop.lat,
lon: currentStop.lon,
};
}
}
};
const run = callback =>
{
const courses = {};
let lastFrame = null;
let lastUpdate = null;
const loop = now =>
const loop = () =>
{
const time = lastFrame === null ? 0 : now - lastFrame;
lastFrame = now;
updatePositions(courses, time);
const now = Date.now();
if (lastUpdate === null || lastUpdate + 5000 <= now)
{
@ -189,7 +230,15 @@ const loop = now =>
updateFromTam(courses);
}
requestAnimationFrame(loop);
const time = lastFrame === null ? 0 : now - lastFrame;
lastFrame = now;
updatePositions(courses, time);
callback(courses);
};
requestAnimationFrame(loop);
const interval = setInterval(loop, 24);
return () => clearInterval(interval);
};
exports.run = run;