Published
Edited
Apr 3, 2020
Insert cell
Insert cell
md`# Data
CalTrain provides a GTFS timetable file on [their developer site](http://www.caltrain.com/developer.html). Awesome! I have no idea what "GTFS" stands for, but [Wikipedia](https://en.wikipedia.org/wiki/General_Transit_Feed_Specification) tells me it's the "General Transit Feed Specification". I suppose there's [a standard for everything](https://www.xkcd.com/927/).

Let's load that data.`
Insert cell
stops = {
let data = d3.csvParse(await FileAttachment("stops.txt").text(), d => {
return {
name: d.stop_name,
id: +d.stop_id,
position: [+d.stop_lat, +d.stop_lon],
}
});
delete data.columns
return data
}
Insert cell
stopTimes = {
let data = d3.csvParse(await FileAttachment("stop_times.txt").text(), d => {
return {
arrivalTime: d.arrival_time,
departureTime: d.departure_time,
stopId: +d.stop_id,
}
});
return data
}
Insert cell
md`Great! We have all these stops.`
Insert cell
map = {
let container = DOM.element("div", {
style: "width: ${width}px; height: 500px;"
});
yield container;
let map = L.map(container).setView(stops[0].position, 9);
let osmLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors"
}).addTo(map);
for (let stop of stops) {
L.marker(stop.position).addTo(map);
}
}
Insert cell
md`Let's ignore northbound or southbound trains by each CalTrain station.`
Insert cell
stopIdsByName = {
let map = {};
for (let stop of stops) {
if (!(stop.name in map)) {
map[stop.name] = [];
}
map[stop.name].push(stop.id);
}
return map;
}
Insert cell
md`Now we have to pick a stop to observe.`
Insert cell
viewof chosenStopName = {
const element = html`<select></select>`;
for (let stopName of Object.keys(stopIdsByName)) {
const option = html`<option>`;
option.text = stopName;
option.value = stopName
element.appendChild(option);
}
return element
}
Insert cell
times = stopTimes.filter(stopTime => stopIdsByName[chosenStopName].includes(stopTime.stopId))
Insert cell
arrivalTimes = times.map(time => time.arrivalTime)
Insert cell
md`How long in minutes are you at ${chosenStopName}?`
Insert cell
viewof duration = html`<input type=range min=1 max=60>`
Insert cell
md`You'll be at ${chosenStopName} for ${duration} minutes.`
Insert cell
md`Now let's find out when the best time is to see the most trains.`
Insert cell
sortedArrivalTimes = arrivalTimes.map(time => {
let components = time.split(":");
let hour = parseInt(components[0], 10);
let additionComponents = {};
if (hour > 23) {
let days = hour % 23
components[0] = hour - (days * 23);
time = components.join(":")
additionComponents = {"days": days}
}
return date.add(date.parse(time, "HH:mm:ss", new Date()), additionComponents);
}).sort(date.compareAsc);
Insert cell
startTime = {
let best = {
"time": null,
"trains": []
};
for (let startTime of sortedArrivalTimes) {
let endTime = date.add(startTime, { minutes: duration });
let trains = [];
for (let arrivalTime of sortedArrivalTimes) {
// compareAsc returns 1 if the first date is after the second date
if (date.compareAsc(startTime, arrivalTime) == 1) {
// Train hasn't arrived yet
continue;
}
if (date.compareAsc(arrivalTime, endTime) == 1) {
// Train arrives after our time period
break;
}
trains.push(arrivalTime);
}
if (trains.length > best["trains"].length) {
best["time"] = startTime;
best["trains"] = trains;
}
}
return best;
}
Insert cell
md`If you arrive at ${chosenStopName} at ${date.format(startTime["time"], "h:mm b")} you'll see ${startTime["trains"].length} train(s) over the next ${duration} minute(s).`
Insert cell
md`---
## Appendix`
Insert cell
d3 = require("d3")
Insert cell
L = require('leaflet@1.2.0')
Insert cell
html`<link href='${resolve('leaflet@1.2.0/dist/leaflet.css')}' rel='stylesheet' />`
Insert cell
date = await import("https://unpkg.com/date-fns@2.11.1/esm/index.js?module");
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more