Compare commits
2 Commits
76a187bbb6
...
d688bff813
Author | SHA1 | Date |
---|---|---|
Mattéo Delabre | d688bff813 | |
Mattéo Delabre | f054b11743 |
|
@ -1,7 +1,4 @@
|
|||
require('regenerator-runtime/runtime');
|
||||
|
||||
const {createMap} = require('./map');
|
||||
|
||||
require('../tam/simulation');
|
||||
|
||||
createMap(/* map = */ 'map');
|
||||
|
|
|
@ -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]),
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 courses = {};
|
||||
let lastFrame = null;
|
||||
let lastUpdate = null;
|
||||
|
||||
const loop = now =>
|
||||
const run = callback =>
|
||||
{
|
||||
const time = lastFrame === null ? 0 : now - lastFrame;
|
||||
const courses = {};
|
||||
let lastFrame = null;
|
||||
let lastUpdate = null;
|
||||
|
||||
lastFrame = now;
|
||||
updatePositions(courses, time);
|
||||
const loop = () =>
|
||||
{
|
||||
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);
|
||||
};
|
||||
|
||||
const interval = setInterval(loop, 24);
|
||||
return () => clearInterval(interval);
|
||||
};
|
||||
|
||||
requestAnimationFrame(loop);
|
||||
exports.run = run;
|
||||
|
|
Loading…
Reference in New Issue