Created
March 30, 2012 07:11
-
-
Save cpudney/2248382 to your computer and use it in GitHub Desktop.
Easter Sunday
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
const FIRST_YEAR = 325; | |
const LAST_YEAR = 3000; | |
const WIDTH = 960; | |
const HEIGHT = 500; | |
const INSETS = {'left': 40, | |
'right': 10, | |
'top': 10, | |
'bottom': 60}; | |
const SCALES = {}; | |
// Tick-mark length. | |
const TICK_MARK_LENGTH = 8; | |
// Create the chart once the window has loaded. | |
// | |
window.onload = function() { | |
// Get the data and chart it. | |
plotChart(getEasterSundayData()); | |
var thisYear = new Date().getFullYear(); | |
// Populate menu. | |
var menu = d3.select("#year_menu"); | |
for (var year = FIRST_YEAR; year <= LAST_YEAR; year++) { | |
var option = menu.append("option").text(year); | |
if (year == thisYear) { | |
option.attr("selected", "selected"); | |
highlightYear(thisYear); | |
} | |
} | |
}; | |
// Chart the data set. | |
// | |
// data: the Easter Sunday data. | |
// | |
function plotChart(data) { | |
// Day extents. | |
var dayExtents = d3.extent(data, function(d) { | |
return d.doy; | |
}); | |
dayExtents[1]++; | |
// Scales. | |
SCALES.x = d3.scale.linear() | |
.domain(dayExtents) | |
.range([INSETS.left, WIDTH - INSETS.left - INSETS.right]); | |
SCALES.y = d3.scale.linear() | |
.domain([0, d3.max(data, function(d) { | |
return d.count; | |
})]) | |
.range([0, HEIGHT - INSETS.bottom - INSETS.top]); | |
// Create the root SVG element. | |
var svg = d3.select("#chart").append("svg") | |
.attr("width", WIDTH) | |
.attr("height", HEIGHT); | |
// Plot the bar chart. | |
plotBars(svg, data); | |
plotXAxis(svg, data); | |
plotYAxis(svg); | |
} | |
// Plot chart bars. | |
// | |
// svg: root SVG element. | |
// data: Easter Sunday data set. | |
// | |
function plotBars(svg, data) { | |
// Bar width, y offset. | |
var width = SCALES.x(1) - SCALES.x(0); | |
var yOffset = HEIGHT - INSETS.bottom; | |
svg.selectAll("rect.bar") | |
.data(data) | |
.enter().append("svg:rect") | |
.attr("class", "bar") | |
.attr("width", width) | |
.attr("x", function(d) { | |
return SCALES.x(d.doy); | |
}) | |
.attr("y", function(d) { | |
return yOffset - SCALES.y(d.count); | |
}) | |
.attr("height", function(d) { | |
return SCALES.y(d.count); | |
}); | |
} | |
// Add x-axis annotation. | |
// | |
// svg: root SVG element. | |
// data: Easter Sunday data set. | |
// | |
function plotXAxis(svg, data) { | |
var tickStart = SCALES.y.range()[1] + INSETS.top; | |
var tickEnd = tickStart + TICK_MARK_LENGTH; | |
// X-axis. | |
svg.append("svg:line") | |
.attr("class", "xAxis") | |
.attr("x1", SCALES.x.range()[0]) | |
.attr("x2", SCALES.x.range()[1]) | |
.attr("y1", tickStart) | |
.attr("y2", tickStart); | |
// Tick marks. | |
svg.selectAll('line.xTick') | |
.data(SCALES.x.ticks(data.length)) | |
.enter().append('svg:line') | |
.attr('class', 'xTick') | |
.attr('x1', function(d) { | |
return SCALES.x(d); | |
}) | |
.attr('x2', function(d) { | |
return SCALES.x(d); | |
}) | |
.attr('y1', tickStart) | |
.attr('y2', tickEnd); | |
// Labels. | |
svg.selectAll('text.xLabel') | |
.data(data) | |
.enter().append('svg:text') | |
.attr('class', 'xLabel') | |
.attr('x', function(d) { | |
return SCALES.x(d.doy + 0.5); | |
}) | |
.attr('y', tickEnd) | |
.attr('dy', '0.0em') | |
.attr('transform', function(d) { | |
return 'rotate(90, ' + SCALES.x(d.doy + 0.5) + ', ' + tickEnd + ')'; | |
}) | |
.text(function(d) { | |
return d.text; | |
}); | |
} | |
// Add y-axis annotation. | |
// | |
// svg: root SVG element. | |
// | |
function plotYAxis(svg) { | |
var tickStart = SCALES.x.range()[0]; | |
var tickEnd = tickStart - TICK_MARK_LENGTH; | |
var yOffset = HEIGHT - INSETS.bottom; | |
// Y-axis. | |
svg.append("svg:line") | |
.attr("class", "yAxis") | |
.attr("x1", tickStart) | |
.attr("x2", tickStart) | |
.attr("y1", yOffset - SCALES.y.range()[0]) | |
.attr("y2", yOffset - SCALES.y.range()[1]); | |
// Tick marks. | |
svg.selectAll('line.yTick') | |
.data(SCALES.y.ticks(10)) | |
.enter().append('svg:line') | |
.attr('class', 'yTick') | |
.attr('x1', tickStart) | |
.attr('x2', tickEnd) | |
.attr('y1', function(d) { | |
return yOffset - SCALES.y(d); | |
}) | |
.attr('y2', function(d) { | |
return yOffset - SCALES.y(d); | |
}); | |
// Labels. | |
svg.selectAll('text.yLabel') | |
.data(SCALES.y.ticks(10)) | |
.enter().append('svg:text') | |
.attr('class', 'yLabel') | |
.attr('x', tickEnd) | |
.attr('y', function(d) { | |
return yOffset - SCALES.y(d); | |
}) | |
.attr('dy', '0.4em') | |
.attr("text-anchor", "end") | |
.text(function(d) { | |
return d; | |
}); | |
} | |
// Highlight a year. | |
// | |
// year: the year to highlight. | |
// | |
function highlightYear(year) { | |
var easter = easterSunday(year); | |
easter.setYear(FIRST_YEAR); | |
var doy = dayOfYear(easter); | |
d3.selectAll("rect") | |
.style("fill", function(d) { | |
return d.doy == doy ? "skyblue" : "steelblue"; | |
}); | |
d3.selectAll("text.xLabel") | |
.style("font-weight", function(d) { | |
return d.doy == doy ? "bold" : "normal"; | |
}); | |
} | |
// Get the Easter Sunday data. | |
// | |
function getEasterSundayData() { | |
var dates = {}; | |
var data = []; | |
for (var year = FIRST_YEAR; | |
year < LAST_YEAR; | |
year++) { | |
// Calculate Easter for year. | |
var date = easterSunday(year); | |
// Count dates. | |
date.setYear(FIRST_YEAR); | |
if (date in dates) { | |
data[dates[date]].count++; | |
} | |
else { | |
var record = {}; | |
record.count = 1; | |
record.doy = dayOfYear(date); | |
record.text = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][date.getMonth()] + ' ' + date.getDate(); | |
data.push(record); | |
dates[date] = data.length - 1; | |
} | |
} | |
return data; | |
} | |
// Determines the date of Easter Sunday for a given year. | |
// See: http://javascript.about.com/library/bleaster1.htm | |
// | |
// year: the year to calculate. | |
// | |
function easterSunday(year) { | |
var a = year % 19; | |
var b = Math.floor(year / 100); | |
var c = year % 100; | |
var h = (19 * a + b - Math.floor(b / 4) - Math.floor((b - Math.floor((b + 8) / 25) + 1) / 3) + 15) % 30; | |
var k = (32 + 2 * (b % 4) + 2 * Math.floor(c / 4) - h - c % 4) % 7; | |
var m = Math.floor((a + 11 * h + 22 * k) / 451); | |
var month = Math.floor((h + k - 7 * m + 114) / 31) - 1; | |
var day = ((h + k - 7 * m + 114) % 31) + 1; | |
return new Date(year, month, day) | |
} | |
// Seconds per day. | |
const SECONDS_PER_DAY = 86400000; | |
// Calculates the day of the year for a given date. | |
// date: the date to calculate. | |
// | |
function dayOfYear(date) { | |
return Math.ceil((date - new Date(date.getFullYear(), 0, 1)) / SECONDS_PER_DAY); | |
} |
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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" | |
"http://www.w3.org/TR/html4/loose.dtd"> | |
<html> | |
<head> | |
<title>When is Easter Sunday?</title> | |
<meta http-equiv="X-UA-Compatible" content="chrome=1"> | |
<link href="style.css" media="screen" rel="stylesheet" type="text/css"/> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.8.1"></script> | |
<script type="text/javascript" src="eastersunday.js"></script> | |
</head> | |
<body> | |
<h1>When is Easter Sunday?</h1> | |
<p><label for="year_menu">Year</label> | |
<select id="year_menu" name="year_list" onchange="highlightYear(this.value);"></select> | |
</p> | |
<div id="chart"></div> | |
<p class="attrib">By <a href="http://vislives.com/" target="_blank">Chris Pudney</a> | |
<a href="http://creativecommons.org/licenses/by-sa/3.0/" target="_blank"><img align="top" | |
alt="Creative Commons License" | |
src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png"/></a> | |
<a href="https://gist.github.com/2248382" target="_blank" title="Hosted on GitHub">Source Code</a> | |
</p> | |
</body> | |
</html> |
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
body { | |
margin: 2; | |
padding: 0; | |
background-color: #000000; | |
font-family: sans-serif; | |
font-size: 12px; | |
color: white; | |
overflow: hidden; | |
} | |
a:link { | |
color: #ccccff; | |
} | |
a:visited { | |
color: #cccccc; | |
} | |
a:active { | |
color: #ffccff; | |
} | |
a.hover { | |
color: #ffcccc; | |
} | |
rect { | |
fill: steelblue; | |
stroke: white; | |
shape-rendering: crispEdges; | |
} | |
line { | |
stroke: white; | |
shape-rendering: crispEdges; | |
} | |
text { | |
fill: white; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment