-
viewof riderchart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
let x = d3.scaleLinear()
.range([0 + margin.left, width - margin.right]);
+
riderchart = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);
-
let y = d3.scaleBand()
.range([0, height - margin.top - margin.bottom])
.padding(0.1);
+
svg.append(() => style);
-
let xAxisSpecs = d3.axisTop(x)
.ticks(6)
.tickFormat(x => Math.abs(x))
+
let x = d3.scaleLinear().range([0 + margin.left, width - margin.right]);
-
let yAxisSpecs = d3.axisLeft(y)
.tickSize(0)
+
let y = d3
.scaleBand()
.range([0, height - margin.top - margin.bottom])
.padding(0.1);
-
let xAxisGrid = d3.axisTop(x)
.ticks(6)
.tickSize(-(height - margin.top - margin.bottom))
.tickFormat("")
+
let xAxisSpecs = d3
.axisTop(x)
.ticks(6)
.tickFormat(x => Math.abs(x));
-
let xAxis = g => g
.attr("transform", `translate(0,${margin.top})`)
.call(xAxisSpecs)
.call(g => g.selectAll(".domain").remove())
+
let yAxisSpecs = d3.axisLeft(y).tickSize(0);
-
const xoffset = -40
let yAxis = g => g
.attr("transform", `translate(${margin.left},${margin.top})`)
.call(yAxisSpecs)
.call(g => g.selectAll(".domain").remove())
.call(g => g.selectAll(".tick text")
.attr('x', xoffset-10)
.attr('class', 'station-name'))
+
let xAxisGrid = d3
.axisTop(x)
.ticks(6)
.tickSize(-(height - margin.top - margin.bottom))
.tickFormat("");
-
let maxRider = d3.max([d3.max(data, d => -d.pickup), d3.max(data, d => d.dropoff)])
x.domain([-maxRider, maxRider])
y.domain(data.map(d => d.station))
+
let xAxis = g =>
g
.attr("transform", `translate(0,${margin.top})`)
.call(xAxisSpecs)
.call(g => g.selectAll(".domain").remove());
-
let colorScale = d3.scaleOrdinal()
.domain(['pickup','dropoff','waited'])
.range(d3.schemePaired.slice(0,3))
+
const xoffset = -40;
let yAxis = g =>
g
.attr("transform", `translate(${margin.left},${margin.top})`)
.call(yAxisSpecs)
.call(g => g.selectAll(".domain").remove())
.call(g =>
g
.selectAll(".tick text")
.attr('x', xoffset - 10)
.attr('class', 'station-name')
);
-
// add the x-axis gridlines
svg.append("g")
.attr("class", "grid")
.attr("transform", `translate(0,${margin.top})`)
.call(xAxisGrid)
+
let maxRider = d3.max([
d3.max(data, d => -d.pickup),
d3.max(data, d => d.dropoff)
]);
x.domain([-maxRider, maxRider]);
y.domain(data.map(d => d.station));
-
// add subway line
const linewidth = 8
svg.append('g')
.attr("transform", `translate(${margin.left},${margin.top})`)
.append('line')
.attr('class', 'shuttle')
.attr('x1', xoffset)
.attr('x2', xoffset)
.attr('y1', y('Fire Station') + y.bandwidth()/2)
.attr('y2', y('Seward 9N') + y.bandwidth()/2)
.attr('stroke', '#319244')
.attr('stroke-width', linewidth)
// create group for each shuttle stop
let rects = svg.selectAll('.shuttle-stop')
.data(data)
.join('g')
.attr('class', 'shuttle-stop')
.attr("transform", `translate(0,${margin.top})`)
+
let colorScale = d3
.scaleOrdinal()
.domain(['pickup', 'dropoff', 'waited'])
.range(d3.schemePaired.slice(0, 3));
-
// add elements for each shuttle stop
rects.append('circle')
.attr('class', 'station')
.attr("transform", `translate(${margin.left},0)`)
.attr('cx', xoffset)
.attr('cy', d => y(d.station) + y.bandwidth()/2)
.attr('r', linewidth/2 + 1)
.attr('stroke-width', 2)
+
// add the x-axis gridlines
svg
.append("g")
.attr("class", "grid")
.attr("transform", `translate(0,${margin.top})`)
.call(xAxisGrid);
-
rects.append('text')
.attr('class', 'headway')
.attr("transform", `translate(${margin.left},0)`)
.attr('x', xoffset + 45)
.attr('y', d => y(d.station) + y.bandwidth()/2)
.attr('dy', '0.35em')
.text(d => `${Math.floor(d.headway/60)}:${d3.format('>02')(d.headway%60)}`)
+
// add subway line
const linewidth = 8;
svg
.append('g')
.attr("transform", `translate(${margin.left},${margin.top})`)
.append('line')
.attr('class', 'shuttle')
.attr('x1', xoffset)
.attr('x2', xoffset)
.attr('y1', y('Fire Station') + y.bandwidth() / 2)
.attr('y2', y('Seward 9N') + y.bandwidth() / 2)
.attr('stroke', '#319244')
.attr('stroke-width', linewidth);
-
rects.append('rect')
.attr('class', 'pickup pdw')
.attr('x', d => x(-d.pickup))
.attr('y', d => y(d.station))
.attr('width', d => x(d.pickup)-x(0))
.attr('height', y.bandwidth())
.attr("data-tippy-content", d => `${d.station}: ${d.pickup} pickups`)
.attr('fill', colorScale('pickup'))
+
// create group for each shuttle stop
let rects = svg
.selectAll('.shuttle-stop')
.data(data)
.join('g')
.attr('class', 'shuttle-stop')
.attr("transform", `translate(0,${margin.top})`);
-
rects.append('rect')
.attr('class', 'dropoff pdw')
.attr('x', d => x(0))
.attr('y', d => y(d.station))
.attr('width', d => x(d.dropoff)-x(0))
.attr('height', y.bandwidth())
.attr("data-tippy-content", d => `${d.station}: ${d.dropoff} dropoffs`)
.attr('fill', colorScale('dropoff'))
+
// add elements for each shuttle stop
rects
.append('circle')
.attr('class', 'station')
.attr("transform", `translate(${margin.left},0)`)
.attr('cx', xoffset)
.attr('cy', d => y(d.station) + y.bandwidth() / 2)
.attr('r', linewidth / 2 + 1)
.attr('stroke-width', 2);
-
rects.append('rect')
.attr('class', 'waited pdw')
.attr('x', d => x(-d.waited))
.attr('y', d => y(d.station))
.attr('width', d => x(d.waited)-x(0))
.attr('height', y.bandwidth())
.attr("data-tippy-content", d => `${d.station}: ${d.waited} waited`)
.attr('fill', colorScale('waited'))
// add axes
svg.append("g")
.attr("class", "x-axis")
.attr("transform", `translate(0,${margin.top})`)
.call(xAxis);
+
rects
.append('text')
.attr('class', 'headway')
.attr("transform", `translate(${margin.left},0)`)
.attr('x', xoffset + 45)
.attr('y', d => y(d.station) + y.bandwidth() / 2)
.attr('dy', '0.35em')
.text(
d => `${Math.floor(d.headway / 60)}:${d3.format('>02')(d.headway % 60)}`
);
-
svg.append("g")
.attr("class", "y-axis")
.call(yAxis);
+
rects
.append('rect')
.attr('class', 'pickup pdw')
.attr('x', d => x(-d.pickup))
.attr('y', d => y(d.station))
.attr('width', d => x(d.pickup) - x(0))
.attr('height', y.bandwidth())
.attr("data-tippy-content", d => `${d.station}: ${d.pickup} pickups`)
.attr('fill', colorScale('pickup'));
-
// headers
let headers = svg.append('g')
.attr('class', 'header')
.attr("transform", `translate(0,${margin.top})`)
// title
headers.append('text')
.attr('transform', `translate(${x(0)},${-50})`)
.text(`${d3.sum(data, d => d.dropoff)} riders`)
.style('font-size', '2.0em')
// x-axis headers
headers.append('text')
.attr('transform', `translate(${x(-maxRider/2)},${-30})`)
.text('pickup')
headers.append('text')
.attr('transform', `translate(${x(maxRider/2)},${-30})`)
.text('dropoff')
function updateChart(day) {
const T = 750
svg.selectAll('rect.pickup').transition().duration(T)
.attr('x', d => x(-d.pickup))
.attr('width', d => x(d.pickup)-x(0))
.attr("data-tippy-content", d => `${d.station}: ${d.pickup} pickups`)
svg.selectAll('rect.dropoff').transition().duration(T)
.attr('width', d => x(d.dropoff)-x(0))
.attr("data-tippy-content", d => `${d.station}: ${d.dropoff} dropoff`)
svg.selectAll('rect.waited').transition().duration(T)
.attr('x', d => x(-d.waited))
.attr('width', d => x(d.waited)-x(0))
.attr("data-tippy-content", d => `${d.station}: ${d.waited} waited`)
}
+
rects
.append('rect')
.attr('class', 'dropoff pdw')
.attr('x', d => x(0))
.attr('y', d => y(d.station))
.attr('width', d => x(d.dropoff) - x(0))
.attr('height', y.bandwidth())
.attr("data-tippy-content", d => `${d.station}: ${d.dropoff} dropoffs`)
.attr('fill', colorScale('dropoff'));
-
return Object.assign(svg.node(), {updateChart});
+
rects
.append('rect')
.attr('class', 'waited pdw')
.attr('x', d => x(-d.waited))
.attr('y', d => y(d.station))
.attr('width', d => x(d.waited) - x(0))
.attr('height', y.bandwidth())
.attr("data-tippy-content", d => `${d.station}: ${d.waited} waited`)
.attr('fill', colorScale('waited'));
// add axes
svg
.append("g")
.attr("class", "x-axis")
.attr("transform", `translate(0,${margin.top})`)
.call(xAxis);
svg
.append("g")
.attr("class", "y-axis")
.call(yAxis);
// headers
let headers = svg
.append('g')
.attr('class', 'header')
.attr("transform", `translate(0,${margin.top})`);
// title
headers
.append('text')
.attr('transform', `translate(${x(0)},${-50})`)
.text(`${d3.sum(data, d => d.dropoff)} riders`)
.style('font-size', '2.0em');
// x-axis headers
headers
.append('text')
.attr('transform', `translate(${x(-maxRider / 2)},${-30})`)
.text('pickup');
headers
.append('text')
.attr('transform', `translate(${x(maxRider / 2)},${-30})`)
.text('dropoff');
function updateChart(day) {
rects
.data(rawdata.filter(d => +d.date == +new Date(day)))
.transition()
.duration(750)
.call(g =>
g
.select("rect.pickup")
.attr('x', d => x(-d.pickup))
.attr('width', d => x(d.pickup) - x(0))
.attr("data-tippy-content", d => `${d.station}: ${d.pickup} pickups`)
)
.call(g =>
g
.select("rect.dropoff")
.attr('width', d => x(d.dropoff) - x(0))
.attr("data-tippy-content", d => `${d.station}: ${d.dropoff} dropoff`)
)
.call(g =>
g
.select("rect.waited")
.attr('x', d => x(-d.waited))
.attr('width', d => x(d.waited) - x(0))
.attr("data-tippy-content", d => `${d.station}: ${d.waited} waited`)
);
}
return Object.assign(svg.node(), { updateChart });