Skip to content

Instantly share code, notes, and snippets.

@astefanutti
Forked from mbostock/.block
Created December 23, 2015 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save astefanutti/0f0084efb0ceff069c40 to your computer and use it in GitHub Desktop.
Save astefanutti/0f0084efb0ceff069c40 to your computer and use it in GitHub Desktop.
Solar Oscillator

In honor of the vernal equinox, this animation shows the progression of the solar terminator at approximately 5.2 million times its normal rate. Each frame of the animation advances twenty-four hours ahead of real-time so that the seasonal changes of the solar terminator are visible. The blue region is night; the white region is day. Best accompanied with the theme song from Buck Rogers.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #fcfcfa;
}
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #fff;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.land {
fill: #222;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.night {
stroke: steelblue;
fill: steelblue;
fill-opacity: .3;
}
text {
font-family: Menlo, monospace;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/d3.geo.projection.v0.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 570;
var π = Math.PI,
radians = π / 180,
degrees = 180 / π;
var formatDate = d3.time.format.utc("%b. %d, %Y"),
formatTime = d3.time.format.utc("%X UTC");
var circle = d3.geo.circle()
.angle(90);
var projection = d3.geo.kavrayskiy7()
.scale(170)
.translate([width / 2, height / 2])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path);
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
var g = svg.append("g");
g.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
d3.json("/mbostock/raw/4090846/world-50m.json", function(error, world) {
if (error) throw error;
var dayOffset = 0;
var date = svg.append("text")
.attr("dy", "-1.35em");
var time = svg.append("text");
svg.selectAll("text")
.attr("x", 16)
.attr("y", height - 16);
g.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
g.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
var night = svg.append("path")
.attr("class", "night");
d3.timer(function() {
var now = new Date(Date.now() + dayOffset);
date.text(formatDate(now));
time.text(formatTime(now));
night.datum(circle.origin(antipode(solarPosition(now)))).attr("d", path);
dayOffset += 864e5;
});
});
d3.select(self.frameElement).style("height", height + "px");
function antipode(position) {
return [position[0] + 180, -position[1]];
}
function solarPosition(time) {
var centuries = (time - Date.UTC(2000, 0, 1, 12)) / 864e5 / 36525, // since J2000
longitude = (d3.time.day.utc.floor(time) - time) / 864e5 * 360 - 180;
return [
longitude - equationOfTime(centuries) * degrees,
solarDeclination(centuries) * degrees
];
}
// Equations based on NOAA’s Solar Calculator; all angles in radians.
// http://www.esrl.noaa.gov/gmd/grad/solcalc/
function equationOfTime(centuries) {
var e = eccentricityEarthOrbit(centuries),
m = solarGeometricMeanAnomaly(centuries),
l = solarGeometricMeanLongitude(centuries),
y = Math.tan(obliquityCorrection(centuries) / 2);
y *= y;
return y * Math.sin(2 * l)
- 2 * e * Math.sin(m)
+ 4 * e * y * Math.sin(m) * Math.cos(2 * l)
- 0.5 * y * y * Math.sin(4 * l)
- 1.25 * e * e * Math.sin(2 * m);
}
function solarDeclination(centuries) {
return Math.asin(Math.sin(obliquityCorrection(centuries)) * Math.sin(solarApparentLongitude(centuries)));
}
function solarApparentLongitude(centuries) {
return solarTrueLongitude(centuries) - (0.00569 + 0.00478 * Math.sin((125.04 - 1934.136 * centuries) * radians)) * radians;
}
function solarTrueLongitude(centuries) {
return solarGeometricMeanLongitude(centuries) + solarEquationOfCenter(centuries);
}
function solarGeometricMeanAnomaly(centuries) {
return (357.52911 + centuries * (35999.05029 - 0.0001537 * centuries)) * radians;
}
function solarGeometricMeanLongitude(centuries) {
var l = (280.46646 + centuries * (36000.76983 + centuries * 0.0003032)) % 360;
return (l < 0 ? l + 360 : l) / 180 * π;
}
function solarEquationOfCenter(centuries) {
var m = solarGeometricMeanAnomaly(centuries);
return (Math.sin(m) * (1.914602 - centuries * (0.004817 + 0.000014 * centuries))
+ Math.sin(m + m) * (0.019993 - 0.000101 * centuries)
+ Math.sin(m + m + m) * 0.000289) * radians;
}
function obliquityCorrection(centuries) {
return meanObliquityOfEcliptic(centuries) + 0.00256 * Math.cos((125.04 - 1934.136 * centuries) * radians) * radians;
}
function meanObliquityOfEcliptic(centuries) {
return (23 + (26 + (21.448 - centuries * (46.8150 + centuries * (0.00059 - centuries * 0.001813))) / 60) / 60) * radians;
}
function eccentricityEarthOrbit(centuries) {
return 0.016708634 - centuries * (0.000042037 + 0.0000001267 * centuries);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment