Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c06eed1
Initial markers
joined May 23, 2018
8a2435c
Progress on markers
joined Jun 1, 2018
ee3a04f
Update d3-timer to 1.0.9
skinkie Oct 7, 2018
686bc74
Try out to update Marey in realtime
skinkie Oct 7, 2018
e941e63
Fix a bug, it should be rt, not realtime
skinkie Oct 7, 2018
88f7525
Updating the Mix is not required.
skinkie Oct 7, 2018
111058c
Add descriptive comments
skinkie Oct 7, 2018
e795939
Make sure only to start an update timer for a dataset that is expecte…
skinkie Oct 7, 2018
11c5a73
Fix issue #61
skinkie Oct 7, 2018
e866d93
Merge branch 'stopnames' into realtime-update
skinkie Oct 7, 2018
e26b1da
Merge branch 'reverse' into realtime-update
skinkie Oct 7, 2018
50fa69f
Merge branch 'reverse' into realtime-update
skinkie Oct 7, 2018
8e05344
Start of implementation for #62
skinkie Oct 8, 2018
171cd8b
Fix tripLabel
skinkie Oct 8, 2018
b1734f0
Bugfixes
skinkie Oct 8, 2018
16bd67a
Fix #72
skinkie Oct 20, 2018
eeb0fa9
Merge branch 'fix_shared_sequence_last_stop' into realtime-update
skinkie Oct 20, 2018
9ca325f
Apply suggestion of @joined
skinkie Oct 20, 2018
248e589
Merge branch 'master' into markers
skinkie Mar 8, 2019
7984afa
eslint 5 fixes
skinkie Mar 8, 2019
d056603
Implement markers visualisation on scheduled trips.
skinkie Mar 9, 2019
4dafc22
If a marker is placed at exactly the first stop.
skinkie Mar 9, 2019
1cc1a39
Implement the option to load a generic markers.json per operational d…
skinkie Mar 9, 2019
2be7e77
Merge remote-tracking branch 'origin/markers' into realtime-update
skinkie Mar 9, 2019
6be36b2
Onscreen remove
skinkie Mar 9, 2019
2e6059b
Marker updates.
skinkie Apr 23, 2019
7b92433
Implement a line/direction label.
skinkie Apr 23, 2019
f46cdae
Support the feature request to only show a single line direction.
skinkie Apr 24, 2019
8606621
Very raw cut to allow trip selection by url arguments.
skinkie Apr 24, 2019
8c5dd90
Highlight the selected trip
skinkie Apr 25, 2019
3677df0
Limit the view of the selection, and add the the trip code as hashval…
skinkie Apr 25, 2019
969c1a2
Bug fixes.
skinkie Apr 25, 2019
b755f0a
Update view
skinkie Apr 25, 2019
bd94795
Realtime disable options
skinkie May 22, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"d3-shape": "^1.2.0",
"d3-time": "^1.0.8",
"d3-time-format": "^2.1.1",
"d3-timer": "^1.0.7",
"d3-timer": "^1.0.9",
"d3-zoom": "^1.7.1",
"dat.gui": "^0.7.0",
"jquery": "^3.3.1",
Expand Down
9 changes: 9 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,16 @@ <h3>Public Transport Decision Support System</h3>
<div class="linedirectionSel" style="display: none;">
<label for="line-direction">Line - direction</label>
<select id="line-direction"></select>

<label for="line-direction-overlap">Overlapping other lines</label>
<input type="checkbox" id="line-direction-overlap" checked="0" />
</div>

<label for="realtime">Enable realtime</label>
<input type="checkbox" id="realtime" checked="1" />

<br />

<input class="button-primary close-sidebar" type="submit" value="Load">
</fieldset>
</form>
Expand Down
85 changes: 79 additions & 6 deletions src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ const getSelectedDatasetURL = () => {
return `${publicationInUse.url}${datasetInUse.filename}`;
};

const getSelectedDatasetMarkersURL = () => {
// Get the publication currently selected (date)
const publicationInUse = indexData.publications
.find(pub => pub.date === document.getElementById('day').value);
// Get the dataset currently selected (group of lines) within the publication selected
// Compute URL of dataset selected
return `${publicationInUse.url}markers.json`;
};

// Load the available line-directions within this group of lines
const loadAvailableLineDirections = () => {
fetch(getSelectedDatasetURL())
Expand Down Expand Up @@ -107,10 +116,35 @@ const processIndex = () => {

/* eslint no-new: "off" */
// Fetch default dataset and create its corresponding visualization
const defaultDatasetURL = `${publications[0].url}${publications[0].datasets[0].filename}`;
// const defaultDatasetURL = `${publications[0].url}${publications[0].datasets[0].filename}`;
Object.assign(options, { selectedDate: publications[0].date });
fetch(defaultDatasetURL).then(r => r.json())
.then((defaultData) => { new PTDS(defaultData, options); });
// fetch(defaultDatasetURL).then(r => r.json())
// .then((defaultData) => { new PTDS(defaultData, options, null); });
};

// URL Hash trip selection
const urlHashTripSelection = (url, tripCode, date) => {
// Load the chosen dataset
fetch(url).then(r => r.json())
.then((data) => {
// Empty the main div element
document.getElementById('main').innerHTML = '';
// Remove the dat.GUI widget(s) if present
for (const dg of document.getElementsByClassName('dg main')) dg.remove();

const journeyPattern = data.journeyPatterns[data.vehicleJourneys[tripCode].journeyPatternRef];

// Create new visualization, using the specified mode.
const selectedMode = 'marey';
options.mode = selectedMode;
options.line = journeyPattern.lineRef;
options.direction = journeyPattern.direction;
options.overlap = true;
options.realtime = true;
options.trip = tripCode;
Object.assign(options, { selectedDate: date });
new PTDS(data, options, null);
});
};

// Form submission handler
Expand All @@ -136,11 +170,22 @@ const formSubmit = (event) => {
const [line, direction] = document.getElementById('line-direction').value.split(' - ');
options.line = line;
options.direction = parseInt(direction, 10);
options.overlap = document.getElementById('line-direction-overlap').checked;
options.realtime = document.getElementById('realtime').checked;
} else {
options.mode = 'spiralSimulation';
}
Object.assign(options, { selectedDate: document.getElementById('day').value });
new PTDS(data, options);

const urlMarkersSelected = getSelectedDatasetMarkersURL();
fetch(urlMarkersSelected).then(r => r.json())
.then((markerdata) => {
new PTDS(data, options, markerdata);
})
.catch(() => {
/* When developing remove this catch */
new PTDS(data, options, null);
});
});
};

Expand All @@ -149,11 +194,39 @@ $(document).ready(() => {
const indexFileURL = 'https://services.opengeo.nl/ptds/index.json';
fetch(indexFileURL).then(r => r.json())
// Process the index file when finished loading it
.then((data) => { indexData = data; processIndex(); });
.then((data) => {
indexData = data;
processIndex();

// Handle loading a trip from a URL
const { hash } = window.location;
if (hash !== '' && hash !== '#') {
const tripCode = hash.substring(1, hash.length);
const parts = tripCode.split(':');
const lineCode = parts[1];
const date = parts[3];
document.getElementById('day').value = date;
document.getElementById('mode').value = 'marey';

const publication = indexData.publications.filter(e => (e.date === date));
if (publication.length > 0) {
const publicationInUse = publication[0];
const dataset = publicationInUse.datasets.filter(e => (e.lines.includes(lineCode)));
if (dataset.length > 0) {
const datasetInUse = dataset[0];
document.getElementById('lines-groups').value = datasetInUse.filename;
const url = `${publicationInUse.url}${datasetInUse.filename}`;
urlHashTripSelection(url, tripCode, date);
}
}
}
});

const { hash } = window.location;

// Activate sidebar plugin
$('#sidebar').simplerSidebar({
init: 'opened',
init: (hash === '' || hash === '#') ? 'opened' : 'closed',
selectors: {
trigger: '#toggle-sidebar',
quitter: '.close-sidebar',
Expand Down
1 change: 1 addition & 0 deletions src/js/models/stoparea.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default class StopArea {
this.code = code;
this.stops = stops;
this.center = this.computeCenter();
this.name = stops[0].name; /* TODO: This might need to be improved */
}

/**
Expand Down
15 changes: 14 additions & 1 deletion src/js/models/vehiclejourney.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ export default class VehicleJourney {
};
}

get tripLabel() {
if (this.isRealTime) {
/* TODO: Temporary workaround, we should have all blocks */
const firstRt = this.rt[Object.keys(this.rt)[0]];
return `${this.journeyPattern.line.code} - ${(firstRt.blockNumber !== undefined ? firstRt.blockNumber : '?')} (${firstRt.vehicleNumber})`;
}
return `${this.journeyPattern.line.code}`;
}

/**
* Check if given a specific time, the journey is active
* @param {Date} time - Time
Expand Down Expand Up @@ -132,6 +141,7 @@ export default class VehicleJourney {
* Computes the realtime positions information of the vehicles belonging to this journey
* @return {Array.<{
* vehicleNumber: number,
* blockNumber: number,
* positions: {time: Date, distanceSinceLastStop: number, distanceFromStart: number,
* status: string, prognosed: boolean}
* }>} - List of enriched realtime position info
Expand All @@ -142,8 +152,11 @@ export default class VehicleJourney {
// Extract array of static schedule distances at each stop
const staticDistances = this.journeyPattern.distances;

return Object.values(this.rt).map(({ vehicleNumber, times, distances }) => ({
return Object.values(this.rt).map(({ vehicleNumber, blockNumber, times,
distances, markers }) => ({
vehicleNumber,
blockNumber,
markers,
// Enrich the vehicles position data with the distance since the last stop
// and the index of that stop, as well as the status compared to the schedule
positions: times.map((time, index) => {
Expand Down
71 changes: 70 additions & 1 deletion src/js/ptdataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ import TimeUtils from './timeutils';
* Class representing a public transport dataset
*/
export default class PTDataset {
constructor(inputData, referenceDate) {
constructor(inputData, referenceDate, markerData) {
this.updateUrl = inputData.updateUrl;
this.referenceDate = referenceDate;
Object.assign(this, PTDataset.computeStopsAndStopAreas(inputData.scheduledStopPoints));
Object.assign(this, this.computeLinesJourneyPatterns(inputData.journeyPatterns));
this.vehicleJourneys = this.computeVehicleJourneys(inputData.vehicleJourneys);
this.stopsLinks = this.computeLinks();
this.markers = null;
if (markerData != null && markerData.markers != null) {
this.markers = this.computeMarkers(markerData.markers);
this.addMarkersToDataset(this.markers);
}

// Compute times of the first and last stop of any journey in the dataset
this.earliestTime = Math.min(...Object.values(this.journeyPatterns)
Expand All @@ -28,6 +34,33 @@ export default class PTDataset {
.map(jp => jp.firstAndLastTimes.last));
}

addMarkersToDataset(markers) {
for (const marker of markers) {
const { vehicleJourneyCode, vehicleNumber } = marker.reference;
if (Object.prototype.hasOwnProperty.call(this.vehicleJourneys, vehicleJourneyCode)) {
const vehicleJourneyData = this.vehicleJourneys[vehicleJourneyCode];
const { rt } = vehicleJourneyData;
if (rt != null && vehicleNumber != null
&& Object.prototype.hasOwnProperty.call(rt, vehicleNumber)) {
const vehicleData = rt[vehicleNumber];
if (Object.prototype.hasOwnProperty.call(vehicleData, 'markers')) {
const markersData = vehicleData.markers;
markersData.push(marker);
} else {
vehicleData.markers = [marker];
}
} else if (vehicleNumber == null) {
if (Object.prototype.hasOwnProperty.call(vehicleJourneyData, 'markers')) {
const markersData = vehicleJourneyData.markers;
markersData.push(marker);
} else {
vehicleJourneyData.markers = [marker];
}
}
}
}
}

/**
* Convert raw stops data into rich Stop and StopArea objects,
* storing them in an object indexed by their code for fast lookup
Expand Down Expand Up @@ -190,4 +223,40 @@ export default class PTDataset {
vehicleJourney => vehicleJourney.code,
);
}

/**
* Update raw vehicle journey realtime data into an existing VehicleJourney object,
* fetch each object indexed by their code for fast lookup and update that object
* @param {Object} _vehicleJourneys Raw vehicle realtime data
*/
updateVehicleJourneys(_vehicleJourneys) {
for (const [code, { realtime, cancelled }] of Object.entries(_vehicleJourneys)) {
// Convert time in seconds since noon minus 12h to Date object
for (const rtVehicle of Object.values(realtime)) {
rtVehicle.times = rtVehicle.times.map(time => TimeUtils
.secondsToDateObject(time, this.referenceDate));
}

const vehicleJourney = this.vehicleJourneys[code];
if (typeof vehicleJourney !== 'undefined') {
vehicleJourney.rt = realtime;
vehicleJourney.cancelled = cancelled;
}
}
}

/**
* Transform the time used in the markers into Java Date.
* @param {Object} markers Raw marker data
* @return {Object} - Enriched markers data
*/
computeMarkers(markers) {
return markers.map(({ id, reference, time, message, url }) => ({
id,
reference,
time: TimeUtils.secondsToDateObject(time, this.referenceDate),
message,
url,
}));
}
}
Loading