Run 169 (a.k.a. Debticonn) is a society of people who want to run a road race in every one of Connecticut's 169 towns. There is no time limit and this can take many years. I recently joined, so I'm starting the journey. Some who are very advanced in this journey warned me that it's good to be strategic about choosing which towns to run first, because some only have one or two races a year. With this in mind, I created this visualization to help in planning which races to run. I wanted to include information at different levels: from an overall sense of how many races there are every month, to how many races of "elusive" towns there are, to the detailed schedule.
Source: Debticonn.org (original data)
Built with blockbuilder.org
The code was inspired by an example from curran's block: Extremist Murders in the US
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto');
body {
margin:0px;
}
.label {
font-size: 12pt;
font-family: 'Roboto', 'sans-serif';
alignment-baseline: middle;
fill: #828282;
}
.subtitle {
font-size: 24pt;
font-family: 'Roboto';
alignment-baseline: middle;
fill: #505050;
}
.footer {
font-family: 'Roboto', 'sans-serif';
font-size: 12pt;
alignment-baseline: middle;
fill: #505050;
}
.number {
font-size: 12pt;
font-family: 'Roboto', 'sans-serif';
alignment-baseline: middle;
fill: #505050;
}
.bar_other {
fill: #a0a0a0;
opacity: 0.7;
}
.bar {
opacity: 0.7;
}
.old {
fill: #5e5e5e;
}
.current {
fill: #ad0000;
}
.future {
fill: #e06c00;
}
.activebar {
opacity: 1.0;
}
</style>
</head>
<body>
<script>
const width = 960;
const height = 500;
const margin = {
left: 50,
right: 60,
top: 80,
bottom: 52
}
const bar_offset = 40;
const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append('text')
.attr('class', 'subtitle')
.attr('x', margin.left)
.attr('y', 30)
.text('Number of Run169-qualifying races in 2017');
svg.append('text')
.attr('class', 'footer')
.attr('x', margin.left)
.attr('y', 60)
.text('Highlighted: elusive races (from towns with 2 or fewer races in 2017)');
svg.append('text')
.attr('class', 'footer')
.attr('x', margin.left + bar_offset)
.attr('y', 500-39)
.text('(hover to see elusive races in that month)');
svg.append('text')
.attr('class', 'footer')
.attr('x', margin.left + bar_offset)
.attr('y', 500-20)
.text('Source: Run169 (debticonn.org)');
const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`)
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
d3.csv('races_by_month.csv', function(d) {
d.month_num = +d.month_num;
d.num_elusive_races = +d.num_elusive_races;
d.num_all_races = +d.num_all_races;
return d;
}, function(data) {
const xValue = d => d.num_elusive_races;
const xValue2 = d => d.num_all_races;
const yValue = d => d.month;
const current_date = new Date();
const current_month = current_date.getMonth() + 1; // getMonth is zero-index
// assign a bar type depending on the bar month vs. current month
const dClass = function(d) {
bar_type = d.month_num < current_month ? 'old' : d.month_num > current_month ? 'future' : 'current';
return 'bar ' + bar_type;
};
const race_dates = d => d.race_dates;
const xScale = d3.scaleLinear()
.domain([0, d3.max(data, xValue2)])
.range([0, innerWidth]);
const yScale = d3.scaleBand()
.domain(data.map(yValue).reverse())
.range([innerHeight, 0])
.padding(0.1);
const groups = g.selectAll('g').data(data);
const groupsEnter = groups
.enter().append('g')
.attr('transform', d => `translate(0, ${yScale(yValue(d))})`);
// bars for "elusive" towns
groupsEnter
.append('rect')
.attr('class', dClass )
.attr('x', bar_offset)
.attr('width', d => xScale(xValue(d)))
.attr('height', yScale.bandwidth())
.on('mouseover', function(d) {
d3.select(this)
.attr('class', d => dClass(d) + ' activebar');
})
.on('mouseout', function(d) {
d3.select(this)
.attr('class', data => dClass(data) + ' bar');
})
.append('title')
.text(d => 'Elusive races in ' + yValue(d) + ':\n' + race_dates(d)).style('display', 'block');
// bars for "the rest" of the towns
groupsEnter
.append('rect')
.attr('class', 'bar_other' )
.attr('x', d => bar_offset + xScale(xValue(d)))
.attr('width', d => xScale(xValue2(d)) - xScale(xValue(d)))
.attr('height', yScale.bandwidth());
groupsEnter
.append('text')
.attr('class', 'label')
.attr('x', 0)
.attr('y', yScale.bandwidth() / 2)
.text(yValue);
groupsEnter
.append('text')
.attr('class', 'number')
.attr('x', d => xScale(xValue(d)) + 8 + bar_offset)
.attr('y', yScale.bandwidth() / 2)
.text(d => `${xValue(d)} / ${xValue2(d)}`);
});
</script>
</body>
https://d3js.org/d3.v4.min.js