This example builds a heatmap calendar with horizontal weeks like a traditional calendar instead of the vertical weeks in Calendar heatmap (vertical). See also Mike Bostock's Calendar View.
Last active
June 27, 2019 18:13
-
-
Save danbjoseph/3f42bb3f0ab6133cfc192e878c9030ed to your computer and use it in GitHub Desktop.
Calendar heat map (horizontal)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> | |
<script> | |
function drawCalendar(myData) { | |
var calendarRows = 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(myData, function(d) { return new Date(d.submissionTime); }); | |
var maxDate = d3.max(myData, function(d) { return new Date(d.submissionTime); }); | |
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); | |
for(var i=0; i<myData.length; i++){ | |
myData[i].today = myData[i].submissionTime.slice(0,10); | |
} | |
var svg = d3.select("#calendar").selectAll("svg") | |
.data(months) | |
.enter().append("svg") | |
.attr("class", "month") | |
.attr("width", (cellSize * 7) + (cellMargin * 8) ) | |
.attr("height", function(d) { | |
var rows = calendarRows(d); | |
return (cellSize * rows) + (cellMargin * (rows + 1)) + 20; // the 20 is for the month labels | |
}) | |
.append("g") | |
svg.append("text") | |
.attr("class", "month-name") | |
.attr("x", ((cellSize * 7) + (cellMargin * 8)) / 2 ) | |
.attr("y", 15) | |
.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("x", function(d) { | |
return (day(d) * cellSize) + (day(d) * cellMargin) + cellMargin; | |
}) | |
.attr("y", 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 + 20; | |
}) | |
.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.today; }) | |
.rollup(function(leaves) { return leaves.length; }) | |
.object(myData); | |
count = d3.nest() | |
.key(function(d) { return d.today; }) | |
.rollup(function(leaves) { return leaves.length; }) | |
.entries(myData); | |
scale = d3.scaleLinear() | |
.domain(d3.extent(count, function(d) { return d.value; })) | |
.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])); }) | |
.classed("clickable", true) | |
.on("click", function(d){ | |
if(d3.select(this).classed('focus')){ | |
d3.select(this).classed('focus', false); | |
} else { | |
d3.select(this).classed('focus', true) | |
} | |
// doSomething(); | |
}) | |
.select("title") | |
.text(function(d) { return titleFormat(new Date(d)) + ": " + lookup[d]; }); | |
} | |
d3.csv("submissions.csv", function(response) { | |
drawCalendar(response); | |
}) | |
</script> | |
</body> |
We can make this file beautiful and searchable if this error is corrected: No commas found in this CSV file in line 0.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
submissionTime | |
2017-09-27T16:21:13.988Z | |
2017-09-27T16:21:10.444Z | |
2017-09-27T16:21:04.537Z | |
2017-09-28T17:51:19.026Z | |
2017-09-28T17:51:13.704Z | |
2017-09-28T17:51:59.180Z | |
2017-09-28T17:51:36.288Z | |
2017-09-28T17:51:41.993Z | |
2017-09-28T17:51:24.239Z | |
2017-09-28T17:51:45.261Z | |
2017-09-28T17:51:05.595Z | |
2017-09-28T17:51:28.132Z | |
2017-09-28T17:51:04.410Z | |
2017-09-28T17:51:10.971Z | |
2017-09-28T17:51:55.169Z | |
2017-09-28T17:51:50.099Z | |
2017-09-28T17:51:07.970Z | |
2017-09-28T17:51:34.015Z | |
2017-09-28T17:51:52.889Z | |
2017-09-28T17:51:22.374Z | |
2017-09-28T17:51:16.294Z | |
2017-09-28T17:51:39.297Z | |
2017-09-28T17:51:31.607Z | |
2017-09-28T17:51:20.837Z | |
2017-09-28T17:52:01.266Z | |
2017-09-29T15:54:18.410Z | |
2017-09-29T15:54:23.104Z | |
2017-09-29T15:54:29.337Z | |
2017-09-29T15:54:33.397Z | |
2017-09-29T15:54:35.060Z | |
2017-09-29T15:54:36.959Z | |
2017-09-29T16:47:02.481Z | |
2017-09-29T16:47:03.662Z | |
2017-09-29T16:47:04.464Z | |
2017-10-03T17:00:56.188Z | |
2017-10-03T17:00:57.605Z | |
2017-10-03T17:00:58.574Z | |
2017-10-03T17:00:59.833Z | |
2017-10-03T17:01:00.882Z | |
2017-10-03T17:01:04.374Z | |
2017-10-03T17:01:05.606Z | |
2017-10-03T17:01:41.907Z | |
2017-10-04T16:32:38.787Z | |
2017-10-04T16:32:41.320Z | |
2017-10-04T16:32:43.936Z | |
2017-10-04T16:32:51.535Z | |
2017-10-04T16:32:57.178Z | |
2017-10-04T16:32:59.446Z | |
2017-10-04T16:33:02.290Z | |
2017-10-04T16:33:07.605Z | |
2017-10-04T16:33:21.245Z | |
2017-10-04T16:33:21.404Z | |
2017-10-04T16:33:28.446Z | |
2017-10-04T16:33:34.599Z | |
2017-10-04T16:33:40.175Z | |
2017-10-04T16:33:42.479Z | |
2017-10-04T16:33:46.558Z | |
2017-10-04T16:33:51.701Z | |
2017-10-04T16:33:56.583Z | |
2017-10-04T16:34:10.091Z | |
2017-10-04T16:34:10.216Z | |
2017-10-04T16:34:15.977Z | |
2017-10-04T16:34:20.967Z | |
2017-10-04T16:34:26.459Z | |
2017-10-04T16:34:32.806Z | |
2017-10-04T16:34:33.428Z | |
2017-10-04T16:34:34.825Z | |
2017-10-04T16:34:41.896Z | |
2017-10-04T16:34:50.728Z | |
2017-10-04T16:35:06.723Z | |
2017-09-27T16:21:13.988Z | |
2017-09-27T16:21:10.444Z | |
2017-09-27T16:21:04.537Z | |
2017-09-28T17:51:19.026Z | |
2017-09-28T17:51:13.704Z | |
2017-09-28T17:51:59.180Z | |
2017-09-28T17:51:36.288Z | |
2017-09-28T17:51:41.993Z | |
2017-09-28T17:51:24.239Z | |
2017-09-28T17:51:45.261Z | |
2017-09-28T17:51:05.595Z | |
2017-09-28T17:51:28.132Z | |
2017-09-28T17:51:04.410Z | |
2017-09-28T17:51:10.971Z | |
2017-09-28T17:51:55.169Z | |
2017-09-28T17:51:50.099Z | |
2017-09-28T17:51:07.970Z | |
2017-09-28T17:51:34.015Z | |
2017-09-28T17:51:52.889Z | |
2017-09-28T17:51:22.374Z | |
2017-09-28T17:51:16.294Z | |
2017-09-28T17:51:39.297Z | |
2017-09-28T17:51:31.607Z | |
2017-09-28T17:51:20.837Z | |
2017-09-28T17:52:01.266Z | |
2017-09-29T15:54:18.410Z | |
2017-09-29T15:54:23.104Z | |
2017-09-29T15:54:29.337Z | |
2017-09-29T15:54:33.397Z | |
2017-09-29T15:54:35.060Z | |
2017-09-29T15:54:36.959Z | |
2017-09-29T16:47:02.481Z | |
2017-09-29T16:47:03.662Z | |
2017-09-29T16:47:04.464Z | |
2017-11-05T17:00:56.188Z | |
2017-11-05T17:00:57.605Z | |
2017-11-05T17:00:58.574Z | |
2017-11-05T17:00:59.833Z | |
2017-11-05T17:01:00.882Z | |
2017-11-05T17:01:04.374Z | |
2017-11-05T17:01:05.606Z | |
2017-11-05T17:01:41.907Z | |
2017-10-18T16:32:38.787Z | |
2017-10-18T16:32:41.320Z | |
2017-10-18T16:32:43.936Z | |
2017-10-18T16:32:51.535Z | |
2017-10-18T16:32:57.178Z | |
2017-10-18T16:32:59.446Z | |
2017-10-18T16:33:02.290Z | |
2017-10-18T16:33:07.605Z | |
2017-10-18T16:33:21.245Z | |
2017-10-18T16:33:21.404Z | |
2017-10-18T16:33:28.446Z | |
2017-10-18T16:33:34.599Z | |
2017-10-18T16:33:40.175Z | |
2017-10-18T16:33:42.479Z | |
2017-10-18T16:33:46.558Z | |
2017-10-18T16:33:51.701Z | |
2017-10-18T16:33:56.583Z | |
2017-10-18T16:34:10.091Z | |
2017-10-18T16:34:10.216Z | |
2017-10-18T16:34:15.977Z | |
2017-10-18T16:34:20.967Z | |
2017-10-18T16:34:26.459Z | |
2017-10-18T16:34:32.806Z | |
2017-10-18T16:34:33.428Z | |
2017-10-18T16:34:34.825Z | |
2017-10-18T16:34:41.896Z | |
2017-10-18T16:34:50.728Z | |
2017-10-18T16:35:06.723Z | |
2017-10-19T16:21:13.988Z | |
2017-10-19T16:21:10.444Z | |
2017-10-19T16:21:04.537Z | |
2017-10-16T17:51:19.026Z | |
2017-10-16T17:51:13.704Z | |
2017-10-16T17:51:59.180Z | |
2017-10-16T17:51:36.288Z | |
2017-10-16T17:51:41.993Z | |
2017-10-16T17:51:24.239Z | |
2017-10-16T17:51:45.261Z | |
2017-10-16T17:51:05.595Z | |
2017-10-16T17:51:28.132Z | |
2017-10-16T17:51:04.410Z | |
2017-10-16T17:51:10.971Z | |
2017-10-16T17:51:39.297Z | |
2017-10-16T17:51:31.607Z | |
2017-10-16T17:51:20.837Z | |
2017-10-16T17:52:01.266Z | |
2017-11-06T15:54:18.410Z | |
2017-11-06T15:54:23.104Z | |
2017-11-06T15:54:29.337Z | |
2017-11-06T15:54:33.397Z | |
2017-11-06T15:54:35.060Z | |
2017-11-06T15:54:36.959Z | |
2017-11-06T16:47:02.481Z | |
2017-11-06T16:47:03.662Z | |
2017-11-06T16:47:04.464Z | |
2017-11-08T17:00:56.188Z | |
2017-11-08T17:00:57.605Z | |
2017-11-08T17:00:58.574Z | |
2017-11-08T17:00:59.833Z | |
2017-11-08T17:01:00.882Z | |
2017-11-08T17:01:04.374Z | |
2017-11-08T17:01:05.606Z | |
2017-11-08T17:01:41.907Z | |
2017-10-09T16:32:38.787Z | |
2017-10-09T16:32:41.320Z | |
2017-10-09T16:32:43.936Z | |
2017-10-09T16:32:51.535Z | |
2017-10-09T16:32:57.178Z | |
2017-10-09T16:32:59.446Z | |
2017-10-09T16:33:02.290Z | |
2017-10-09T16:33:07.605Z | |
2017-10-09T16:33:21.245Z | |
2017-10-09T16:33:21.404Z | |
2017-10-09T16:33:28.446Z | |
2017-10-09T16:33:34.599Z | |
2017-10-09T16:33:40.175Z | |
2017-10-09T16:33:42.479Z | |
2017-10-09T16:33:46.558Z | |
2017-10-09T16:33:51.701Z | |
2017-10-09T16:33:56.583Z | |
2017-10-09T16:34:10.091Z | |
2017-10-09T16:34:10.216Z | |
2017-10-09T16:34:15.977Z | |
2017-10-09T16:34:20.967Z | |
2017-10-09T16:34:26.459Z | |
2017-10-09T16:34:32.806Z | |
2017-10-09T16:34:33.428Z | |
2017-10-09T16:34:34.825Z | |
2017-10-09T16:34:41.896Z | |
2017-10-09T16:34:50.728Z | |
2017-10-09T16:35:06.723Z |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment