Skip to content

Instantly share code, notes, and snippets.

@zanarmstrong
Last active September 8, 2015 03:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zanarmstrong/73c995405555f6b4d893 to your computer and use it in GitHub Desktop.
Save zanarmstrong/73c995405555f6b4d893 to your computer and use it in GitHub Desktop.
Caltrain Bike Bumps
body {
margin: 30px;
font-family: 'Raleway', sans-serif;
}
p {
line-height: 1.2;
margin-top: 0px;
margin-bottom: 5px;
color: #626262;
}
img {
margin-top: 30px;
}
.module {
width: 700px;
margin: 30px 0 0 0;
overflow: hidden;
height: 102px;
}
.module p {
margin: 0;
font-style: italic;
font-size: 28px;
}
.fade {
position: relative;
height: 102px;
}
.fade:after {
content: "";
text-align: right;
position: absolute;
bottom: 0;
right: 0;
width: 30%;
height: 36px;
background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 50%);
}
.description {
font-size: 16px;
}
.bumps {
opacity: .3;
}
.bumps.selected {
opacity: 1;
stroke: black;
stroke-width: 2;
}
.station {
fill: #8d8d8d;
stroke: none;
}
.station.selected {
fill: black;
}
.stationLine {
stroke: #8d8d8d;
}
.play {
stroke-width: 2px;
stroke: #363636;
fill: #363636;
stroke-linejoin: round;
}
.pause {
stroke: #363636;
stroke-width: 4px;
stroke-linecap: round;
}
.hidden {
display: none;
}
#playPause {
position: absolute;
left: 620px;
top: 220px;
}
"use strict";
// variables
var margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
},
width = 700 - margin.left - margin.right,
height = 380;
var docMargin = {
top: 30,
right: 30,
bottom: 30,
left: 30
}
var lineDrop = 20,
quoteNum = 0;
var playOn = true;
// created manually based on image --> will need to improve this
var stations = {
"4th & King": 132,
"22nd Street": 141,
"Bayshore": 160,
"South SF": 185,
"San Bruno": 195,
"Millbrae": 224,
// confirm value for Broadway
"Broadway": 230,
"Burlingame": 245,
"San Mateo": 277,
"Hayward Park": 295,
"Hillsdale": 308,
"Belmont": 330,
"San Carlos": 347,
"Redwood City": 376,
"Menlo Park": 422,
"Palo Alto": 438,
"California Ave": 459,
"San Antonio": 493,
"Mountain View": 516,
"Sunnyvale": 551,
"Lawrence": 576,
"Santa Clara": 622,
"College Park": 639,
"San Jose": 655
}
var stationList = [
"4th & King",
"22nd Street",
"Bayshore",
"South SF",
"San Bruno",
"Millbrae",
"Broadway",
"Burlingame",
"San Mateo",
"Hayward Park",
"Hillsdale",
"Belmont",
"San Carlos",
"Redwood City",
"Menlo Park",
"Palo Alto",
"California Ave",
"San Antonio",
"Mountain View",
"Sunnyvale",
"Lawrence",
"Santa Clara",
"College Park",
"San Jose"
]
var scales = {
// todo - remove hardcode here
dateScale: d3.time.scale()
.domain([d3.time.format("%Y-%m-%d").parse("2015-01-01"), d3.time.format("%Y-%m-%d").parse("2015-02-19")])
.range([lineDrop, height - 30])
}
var color = {
north: "#FF6F00",
south: "#E0009D",
unknown: "#8d8d8d"
}
// standard svg starter
var svg = d3.select('#svgDiv')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// station line & dots
var stationGroup = svg.append("g")
stationGroup.append("line")
.classed("stationLine", true)
.attr("y1", lineDrop)
.attr("y2", lineDrop)
.attr("x1", 132 - docMargin.left)
.attr("x2", 655 - docMargin.left)
.attr("stroke", color.station);
stationGroup.selectAll(".station")
.data(stationList)
.enter()
.append("circle")
.attr("class", "station")
.attr("cy", lineDrop)
.attr("r", 3)
.attr("cx", function(d, i) {
return stations[d] - docMargin.left
})
.on("mouseover", function(d) {
console.log(d)
});
var intervalId = -1;
function startInterval(data) {
if (data != undefined) {
intervalId = setInterval(function() {
quoteNum = (quoteNum + 1) % data.length;
setUp(quoteNum, data)
}, 2000);
}
}
function setUp(j, data) {
updateText(data[j]);
d3.selectAll(".bumps")
.data(data)
.classed("selected",
function(d, i) {
if (i == j) {
return true
} else {
return false
}
});
selectStation(data[j]["Departure station "]);
}
// get bump data
d3.csv("bikeCaltrain.csv", function(error, data){
setUpBumpCircles(data);
setUp(quoteNum, data)
startInterval(data);
playPauseSetup(data);
})
function setUpBumpCircles(data) {
svg.selectAll(".bumps")
.data(data)
.enter()
.append("circle")
.attr("class", "bumps")
.attr("cy", function(d) {
return scales.dateScale(d3.time.format("%m/%d/%Y").parse(d["Date of bicycle bump(s) "]))
})
.attr("cx", function(d) {
return stations[d["Departure station "]] - docMargin.left
})
.attr("r", function(d) {
return Math.pow(d['Total number of bumped bikes '], .5) * 4
})
.attr("fill", function(d) {
if (d['Travel direction'] == "North") {
return color.north;
} else if (d['Travel direction'] == "South"){
return color.south
} else {
return color.unknown
}
})
.on("mouseover", function(d) {
var currentStation = d["Departure station "]
updateText(d);
d3.selectAll(".bumps").data(data).classed("selected", false)
d3.select(this).classed("selected", true)
selectStation(currentStation);
clearInterval(intervalId);
})
.on("mouseout", function(d) {
updateText(d);
d3.select(this).classed("selected", false)
d3.selectAll(".station").data(stationList).attr("fill", color.station);
if(playOn){
startInterval(data);
}
})
}
function updateText(dataObj) {
if (dataObj["Comments "] != undefined && dataObj["Comments "] != "") {
console.log(dataObj["Comments "])
document.getElementById("quote").innerHTML = dataObj["Comments "]
} else {
document.getElementById("quote").innerHTML = "<span style='color: #EBEBEB'>[no comment]</span>"
}
if (dataObj["Scheduled departure time "] != "") {
document.getElementById("date").innerHTML =
d3.time.format("%a %B %d, %Y")(
d3.time.format("%m/%d/%Y").parse(dataObj["Date of bicycle bump(s) "])
) +
" at " +
dataObj["Scheduled departure time "]
} else {
document.getElementById("date").innerHTML = d3.time.format("%a %B %d, %Y")(
d3.time.format("%m/%d/%Y").parse(dataObj["Date of bicycle bump(s) "]))
}
document.getElementById("numberBumped").innerHTML = "<strong>" + dataObj['Total number of bumped bikes '] + " people</strong> with bikes bumped"
if (dataObj['Travel direction'] == "North") {
var highlightColor = color.north;
} else {
highlightColor = color.south;
}
document.getElementById("location").innerHTML = "Traveling " + "<span style='color:" + highlightColor + "'>" +
dataObj['Travel direction'] + "</span>" + " from <strong>" +
dataObj['Departure station '] + "</strong>";
}
function selectStation(name) {
d3.selectAll(".station")
.data(stationList)
.classed("selected",
function(d) {
if (d == name) {
return true
} else {
return false
}
});
}
function playPauseUpdate(){
d3.selectAll(".play").classed("hidden", playOn);
d3.selectAll(".pause").classed("hidden", !playOn);
}
function playPauseSetup(data) {
// handle play/pause
var circleCenter = {
x: 16,
y: 50
};
function switchOnOff() {
playOn = !playOn;
d3.selectAll(".play").classed("hidden", playOn);
d3.selectAll(".pause").classed("hidden", !playOn);
if(playOn == false){
console.log('clearing interval')
clearInterval(intervalId)
} else {
console.log('starting interval')
startInterval(data)
}
}
var playPauseGroup = d3.select("#playPause")
.append('svg')
.attr('width', 32)
.attr('height', 68)
.on("click", switchOnOff);
playPauseGroup.append('circle')
.classed("playPauseCircle", true)
.attr({
cx: circleCenter.x,
cy: circleCenter.y,
r: 15,
fill: "#BBBABA"
})
playPauseGroup.append('polygon')
.classed("play", true)
.classed("hidden", playOn)
.attr("points", function() {
return (circleCenter.x - 4) +
"," + (circleCenter.y - 6) +
" " + (circleCenter.x + 7) +
"," + (circleCenter.y) +
" " + (circleCenter.x - 4) +
"," + (circleCenter.y + 6)
});
playPauseGroup.append('line')
.classed("pause", true)
.classed("hidden", !playOn)
.attr({
x1: circleCenter.x - 3,
x2: circleCenter.x - 3,
y1: circleCenter.y - 5,
y2: circleCenter.y + 3
});
playPauseGroup.append('line')
.classed("pause", true)
.classed("hidden", !playOn)
.attr({
x1: circleCenter.x + 4,
x2: circleCenter.x + 4,
y1: circleCenter.y - 5,
y2: circleCenter.y + 3
});
}
Timestamp Date of bicycle bump(s) Total number of bumped bikes Departure station Travel direction Scheduled departure time Train number Train type Comments
2/2/2015 10:17:21 01/05/2015 5 4th & King South 5:14pm 370 Unfortunately your staff were turning away people when the south end bike cart was more than suffice to handle all cyclist. Your staff needs to do a better job at counting the number of available space before turn cyclist away. Simply counting number of bike through the door is not adequate since most folding bikes are left "folded" and does not take up room. It should be the responsibility of your staff to ensure every space is filled when available. Your conductors and station agents really need to take a more hands on approach and walk to the carts and confirm.
2/2/2015 10:17:21 01/05/2015 1 4th & King South 282 Bombardier I have experienced this multiple times, and this customer experience is unacceptable. Please take action to increase bicycle capacity as soon as possible.
2/2/2015 10:17:21 01/06/2015 5 4th & King South 376 We were told that the bike car was full.
2/2/2015 10:17:21 01/06/2015 7 Palo Alto North 371 Bombardier I would like to encourage Caltrain to increase capacity for bikes on the new train cars or add another bike car. It is a rather unpleasant experience to have a time schedule planned and not be able to meet it because I am not allowed on the train.
2/2/2015 10:17:21 01/07/2015 4 Redwood City North 269 Not good...
2/2/2015 10:17:21 01/07/2015 8 4th & King South 5:14pm Bombardier
2/2/2015 10:17:21 01/12/2015 8 22nd Street South 7:19am Could see at least 4 spots inside (racks with 3 bikes) but he wouldn't let any of us on
2/2/2015 10:17:21 01/12/2015 8 22nd Street South 314
2/2/2015 10:17:21 01/13/2015 1 Palo Alto North 323 I would love to see an additional bike car on this train, or a conversion to a gallery train which has more space for bikes.
2/2/2015 10:17:21 01/14/2015 2 Hillsdale North 323 Gallery
2/2/2015 10:17:21 01/14/2015 4 4th & King South 5:14pm 370 Gallery
2/2/2015 10:17:21 01/20/2015 4 Menlo Park North 5:19pm
2/2/2015 10:17:21 01/21/2015 10 Palo Alto North 375 Worth noting: the car didn't look full and there was a surveyor on the train. Not sure what that means...
2/2/2015 10:17:21 01/21/2015 1 Menlo Park North 6:19pm 279
2/2/2015 10:17:21 01/23/2015 2 Menlo Park North 5:57pm 277
2/2/2015 10:17:21 01/26/2015 10 Hillsdale North 217 People now calling work to say they will be late.
2/2/2015 10:17:21 01/27/2015 6 Hillsdale North 217 We were told that the bike car was full.
2/2/2015 10:17:21 01/27/2015 6 Hillsdale North 217 Although I got on I was the only one.
2/2/2015 10:17:21 01/27/2015 1 Redwood City North 8:45am 227
2/3/2015 18:03:04 02/03/2015 14 Palo Alto North 5:54pm 277 Gallery Might miss rental inspection
2/4/2015 0:11:22 02/03/2015 4 San Mateo 269 This has never happened at this station to me and is an ominous sign of lack of bike capacity on Caltrain.
2/4/2015 0:23:05 02/03/2015 16 Palo Alto North 5:54pm 277 Please increase room for bikes on CalTrain. Maybe hit up Facebook or one of the other dot com entities for some funds. Seems like they have an abundance of employees in SF that bike commute. Just saying.
2/4/2015 0:27:08 02/03/2015 1 Palo Alto 277 While I understand there is limited space, this is quite an inconvenience for those who depend on Caltrain for transportation. It is only the first week on February.
2/4/2015 0:29:33 02/03/2015 6 Redwood City 279 Tonight I needed to get home to relieve my nanny at 7, but that won't happen because you stubbornly refuse to address the actual issue of bikes being bumped. Tonight I will pay for it with an extra 45 minutes of pay to my nanny, a wasted 45 minutes of time I could have spent with my daughter, and a pair of gloves that I apparently drop while running between cars. Awesome! Implementing a queuing system would help fairness. Another improvement would be to publish detailed bump statistics by train and station so I can at least try to determine which trains to avoid. Another option, particularly at RWC for the timed transfer to local service would be allow more bikes on because there are always a ton more that get off at the very next stop. It makes no sense to bump a bunch of people only to have half the bikes get off right away. I only wish you would DO something to improve the random kick in the face that is get bumped from Caltrain. There are many options, just try something.
2/4/2015 9:07:25 02/04/2015 3 22nd Street South 9:02am 332 Gallery I was lucky enough to get on, others weren't so lucky. The train was at bike capacity, but still plenty of spare seats.
2/4/2015 17:32:19 02/04/2015 3 Redwood City North 5:25pm 269 Gallery I was last on. Count for rear bike car only.
2/5/2015 11:08:28 02/03/2015 3 Menlo Park North 5:57pm 277 Gallery
2/5/2015 19:32:49 02/05/2015 5 Millbrae 8:03am 220
2/5/2015 19:36:04 02/05/2015 3 Palo Alto North 269 Bikes bumped from north car.
2/10/2015 9:12:38 02/10/2015 3 Hillsdale North 323 What's most upsetting is that I was bumped from the first car and sprinted down to the second car, only to have the doors closed a few seconds before I got there. Through the windows I could see two racks with only two bikes each on them. So the whole bumping business could have been avoided altogether with better communication. Most bikers try their best to work within the system, but in 18 months of bike commuting via Caltrain, I've mostly seen conductors act rudely or impatiently with bikers (with some refreshing exceptions). I get that Caltrain is the only commuter rail option for the peninsula, so commuters have no leverage. But conductors are making a crappy situation worse with their attitude.
2/11/2015 21:55:58 02/10/2015 8 4th & King South 6:56pm
2/11/2015 22:00:28 02/10/2015 13 Palo Alto North 267 This was my first time ever riding Caltrain. There was plenty of room on my 6:45 ride to Palo Alto, but I was disappointed by the overwhelming lack of capacity for bicycle commuters on my way back. It was really cramped and uncomfortable. Please support us bicyclists by adding enough bike capacity on your trains.
2/12/2015 5:04:57 02/11/2015 3 Redwood City North 5:25pm 269 Gallery
2/12/2015 8:12:48 02/12/2015 15 Redwood City North 7:30am 319 Bombardier There was space for bikes but the conductor was freaking out and did not let us on.
2/12/2015 8:25:33 02/03/2015 3 San Mateo North 5:36pm 269 Gallery
2/12/2015 9:34:33 02/11/2015 3 Mountain View North 7:57am 323 Bombardier Only one bike was allowed to board. I was there 15 mins early to be sure to get a spot but came out empty. Missed my meeting in the city
2/12/2015 9:45:07 02/12/2015 12 Hillsdale North 7:51am 217 Bombardier Combo train due to fatality (should have been a gallery but was a bombardier and it was packed)
2/12/2015 13:43:44 02/10/2015 5 Menlo Park North 6:46pm 385 Gallery It was so disheartening. It made me really sad. Had such a long day and just wanted to go home. As ridership increases, can we not add more trains and/or more bike cars?
2/12/2015 14:32:59 02/12/2015 9 San Antonio North 7:27am 217 Bombardier whenever trains get combined, they bumped bikers. it really is annoying.
2/12/2015 18:58:49 02/12/2015 5 Palo Alto North 6:43 pm 385 Gallery A ton of bikes were getting on and filled it up. It made me late for date night :(
2/16/2015 17:35:17 02/16/2015 3 Millbrae South 8:17am Bombardier 1st SB train of the day when operating on a holiday schedule is a bombardier and far over capacity for bikes by the time it hits Millbrae...really? Is this mendacity of sheer incompetence? Made an extra effort to travel to a station several miles from my home station to attempt to get to work at best an hour late and ended up being several hours late, no announcement of the train on the announcement board, conductor tells me there's another train behind, which is correct, but 20 minutes behind isn't directly behind right, and isn't that always true in some capacity, there's always another train behind?
2/19/2015 8:20:17 2/19/2015 12 Millbrae South 8:15am 322 Gallery There seemed room onboard
2/19/2015 8:43:08 2/19/2015 12 Millbrae South 8:15am 322 Gallery There seemed room onboard
2/19/2015 8:44:53 2/19/2015 8 Millbrae South 8:32am 324 Gallery
station lat long
4th & King 37.77645 -122.39471
22nd Street 37.75767 -122.39264
Bayshore 37.70954 -122.40132
South SF 37.65590 -122.40526
San Bruno 37.6297005 -122.411359
Millbrae 37.600006 -122.386534
Broadway 37.587466 -122.363233
Burlingame 37.587466 -122.363233
San Mateo 37.568209 -122.323933
Hayward Park 37.552346 -122.308916
Hillsdale 37.537503 -122.298001
Belmont 37.520504 -122.276075
San Carlos 37.507361 -122.260365
Redwood City 37.485412 -122.231957
Menlo Park 37.45418 -122.18202
Palo Alto 37.44307 -122.1649
California Ave 37.428835 -122.142703
San Antonio 37.407157 -122.107231
Mountain View 37.393879 -122.076327
Sunnyvale 37.378427 -122.030742
Lawrence 37.370815 -121.997258
Santa Clara 37.352864 -121.937178
College Park 37.342599 -121.915577
San Jose 37.330419 -121.902129
station,lat,long
4th and King,37.78310,-122.39576
22cd St San Francisco,37.75767,-122.39264
Bayshore,37.70954,-122.40132
South SF,37.65590,-122.40526
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Caltrain Bike Bump Charts</title>
<link href='http://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="bike.css">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<p id="screenCoords"></p>
<h1>Caltrain Bike Bumps in 2015</h1>
<p>Just bumped from Caltrain? Report it at <a href="sfbike.org/bumpform">sfbike.org/bumpform</a>.</p>
<div id="playPause"></div>
<div class="module fade"><p id="quote" class="quote"></p></div>
<p id="date" class="description"></p>
<p id="numberBumped" class="description"></p>
<p id="location" class="description"></p>
<div id="svgDiv"></div>
<img src="caltrain.png"></img>
<!-- call JS files -->
<script src="bike.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment