Skip to content

Instantly share code, notes, and snippets.

@danbjoseph
Last active January 25, 2019 21:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save danbjoseph/13d9365450c27ed3bf5a568721296dcc to your computer and use it in GitHub Desktop.
Save danbjoseph/13d9365450c27ed3bf5a568721296dcc to your computer and use it in GitHub Desktop.
Calendar heat map (vertical)
license: gpl-3.0
day count
2016-05-12 171
2016-06-17 139
2016-05-02 556
2016-04-10 1
2016-05-04 485
2016-03-27 1
2016-05-26 42
2016-05-25 337
2016-05-23 267
2016-05-05 569
2016-03-31 32
2016-03-25 128
2016-05-13 221
2016-03-30 26
2016-03-15 3
2016-04-24 10
2016-04-27 312
2016-03-20 99
2016-05-10 358
2016-04-01 15
2016-05-11 199
2016-07-06 744
2016-05-08 23
2016-03-28 98
2016-03-29 64
2016-04-30 152
2016-03-21 148
2016-03-19 20
2016-05-07 69
2016-04-29 431
2016-04-25 330
2016-04-28 353
2016-04-18 9
2016-01-10 1
2016-01-09 2
2016-03-26 21
2016-05-27 18
2016-04-19 10
2016-04-06 1
2016-04-12 214
2016-05-03 536
2016-07-03 3
2016-06-16 1
2016-03-24 138
2016-04-26 351
2016-04-23 14
2016-05-01 19
2016-07-05 523
2016-05-22 3
2016-05-09 430
2016-05-24 472
2016-04-11 172
2016-03-17 7
2016-05-14 10
2016-05-06 449
2016-07-04 295
2016-05-15 12
2016-03-23 216
2016-03-18 47
2016-03-22 179
<!DOCTYPE html>
<meta charset="utf-8">
<style>
#calendar {
margin: 20px;
}
.month {
margin-right: 8px;
}
.month-name {
font-size: 85%;
fill: #777;
font-family: Arial, Helvetica;
}
.day.hover {
stroke: #6d6E70;
stroke-width: 2;
}
.day.focus {
stroke: #ffff33;
stroke-width: 2;
}
</style>
<body>
<div id="calendar"></div>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>
function drawCalendar(dateData){
var weeksInMonth = function(month){
var m = d3.timeMonth.floor(month)
return d3.timeWeeks(d3.timeWeek.floor(m), d3.timeMonth.offset(m,1)).length;
}
var minDate = d3.min(dateData, function(d) { return new Date(d.day) })
var maxDate = d3.max(dateData, function(d) { return new Date(d.day) })
var cellMargin = 2,
cellSize = 20;
var day = d3.timeFormat("%w"),
week = d3.timeFormat("%U"),
format = d3.timeFormat("%Y-%m-%d"),
titleFormat = d3.utcFormat("%a, %d-%b");
monthName = d3.timeFormat("%B"),
months= d3.timeMonth.range(d3.timeMonth.floor(minDate), maxDate);
var svg = d3.select("#calendar").selectAll("svg")
.data(months)
.enter().append("svg")
.attr("class", "month")
.attr("height", ((cellSize * 7) + (cellMargin * 8) + 20) ) // the 20 is for the month labels
.attr("width", function(d) {
var columns = weeksInMonth(d);
return ((cellSize * columns) + (cellMargin * (columns + 1)));
})
.append("g")
svg.append("text")
.attr("class", "month-name")
.attr("y", (cellSize * 7) + (cellMargin * 8) + 15 )
.attr("x", function(d) {
var columns = weeksInMonth(d);
return (((cellSize * columns) + (cellMargin * (columns + 1))) / 2);
})
.attr("text-anchor", "middle")
.text(function(d) { return monthName(d); })
var rect = svg.selectAll("rect.day")
.data(function(d, i) { return d3.timeDays(d, new Date(d.getFullYear(), d.getMonth()+1, 1)); })
.enter().append("rect")
.attr("class", "day")
.attr("width", cellSize)
.attr("height", cellSize)
.attr("rx", 3).attr("ry", 3) // rounded corners
.attr("fill", '#eaeaea') // default light grey fill
.attr("y", function(d) { return (day(d) * cellSize) + (day(d) * cellMargin) + cellMargin; })
.attr("x", function(d) { return ((week(d) - week(new Date(d.getFullYear(),d.getMonth(),1))) * cellSize) + ((week(d) - week(new Date(d.getFullYear(),d.getMonth(),1))) * cellMargin) + cellMargin ; })
.on("mouseover", function(d) {
d3.select(this).classed('hover', true);
})
.on("mouseout", function(d) {
d3.select(this).classed('hover', false);
})
.datum(format);
rect.append("title")
.text(function(d) { return titleFormat(new Date(d)); });
var lookup = d3.nest()
.key(function(d) { return d.day; })
.rollup(function(leaves) {
return d3.sum(leaves, function(d){ return parseInt(d.count); });
})
.object(dateData);
var scale = d3.scaleLinear()
.domain(d3.extent(dateData, function(d) { return parseInt(d.count); }))
.range([0.4,1]); // the interpolate used for color expects a number in the range [0,1] but i don't want the lightest part of the color scheme
rect.filter(function(d) { return d in lookup; })
.style("fill", function(d) { return d3.interpolatePuBu(scale(lookup[d])); })
.select("title")
.text(function(d) { return titleFormat(new Date(d)) + ": " + lookup[d]; });
}
d3.csv("dates.csv", function(response){
drawCalendar(response);
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment