This interrupted Mollweide projection also features the solar terminator.
-
-
Save johan/4645628 to your computer and use it in GitHub Desktop.
Solar Terminator on Interrupted Mollweide
This file contains hidden or 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> | |
.night { | |
stroke: rgb(35, 65, 90); /* steelblue at 50% brightness */ | |
fill: rgb(35, 65, 90); | |
fill-opacity: .3; | |
} | |
.background { | |
fill: #a4bac7; | |
} | |
.foreground { | |
fill: none; | |
stroke: #333; | |
stroke-width: 1.5px; | |
} | |
.graticule { | |
fill: none; | |
stroke: #fff; | |
stroke-width: .5px; | |
} | |
.graticule :nth-child(2n) { | |
stroke-dasharray: 2,2; | |
} | |
.land { | |
fill: #d7c7ad; | |
stroke: #766951; | |
} | |
.boundary { | |
fill: none; | |
stroke: #a5967e; | |
} | |
</style> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://d3js.org/d3.geo.projection.v0.min.js"></script> | |
<script src="http://d3js.org/topojson.v0.min.js"></script> | |
<script> | |
var width = 960, | |
height = 500; | |
// Interrupted Mollweide, http://bl.ocks.org/4498187 | |
var projection = d3.geo.interrupt(d3.geo.mollweide.raw) | |
.lobes([[ // northern hemisphere | |
[[-180, 0], [-100, 90], [ -40, 0]], | |
[[ -40, 0], [ 30, 90], [ 180, 0]] | |
], [ // southern hemisphere | |
[[-180, 0], [-160, -90], [-100, 0]], | |
[[-100, 0], [ -60, -90], [ -20, 0]], | |
[[ -20, 0], [ 20, -90], [ 80, 0]], | |
[[ 80, 0], [ 140, -90], [ 180, 0]] | |
]]) | |
.translate([width / 2, height / 2]); | |
var circle = d3.geo.circle() | |
.angle(89.9975572); | |
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); | |
var defs = svg.append("defs"); | |
defs.append("path") | |
.datum({type: "Sphere"}) | |
.attr("id", "sphere") | |
.attr("d", path); | |
defs.append("clipPath") | |
.attr("id", "clip") | |
.append("use") | |
.attr("xlink:href", "#sphere"); | |
svg.append("use") | |
.attr("class", "background") | |
.attr("xlink:href", "#sphere"); | |
svg.append("g") | |
.attr("class", "graticule") | |
.selectAll("path") | |
.data(graticule.lines) | |
.enter().append("path") | |
.attr("d", path); | |
svg.append("use") | |
.attr("class", "foreground") | |
.attr("xlink:href", "#sphere"); | |
d3.json("/d/4090846/world-50m.json", function(error, world) { | |
svg.insert("path", ".graticule") | |
.datum(topojson.object(world, world.objects.land)) | |
.attr("clip-path", "url(#clip)") | |
.attr("class", "land") | |
.attr("d", path); | |
svg.insert("path", ".graticule") | |
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; })) | |
.attr("clip-path", "url(#clip)") | |
.attr("class", "boundary") | |
.attr("d", path); | |
var night = svg.append("path") | |
.attr("class", "night") | |
.attr("clip-path", "url(#clip)") | |
.attr("d", path); | |
redraw(); | |
setInterval(redraw, 250); | |
function redraw() { | |
var sunPos = getSolarWGSPosition( Date.now() ); | |
var darknessAngle = 90 - Math.asin( (constants.meanRsun - constants.meanRearth) / sunPos.range ) * constants.rad2deg; | |
night.datum(circle.origin([ -180+sunPos.lon, -sunPos.lat ]).angle(darknessAngle)).attr("d", path); | |
} | |
}); | |
var getSolarWGSPosition = function getSolarWGSPosition(time) { | |
var eci_pos = getSolarECI(time); | |
var wgs_pos = ECItoWGS84(eci_pos, time); | |
return {lat: wgs_pos.latitude, lon: wgs_pos.longitude, range: eci_pos.w}; | |
}; | |
var getSolarECI = function getSolarECI(time) { | |
var mjd, year, T, M, L, e, C, O, Lsa, nu, R, eps; | |
var jd_utc = unix2jd( time / 1000 ); | |
mjd = jd_utc - 2415020.0; | |
year = 1900 + mjd / 365.25; | |
T = (mjd + constants.deltaUTCTT / 86400) / 36525.0; | |
M = (( 358.47583 + ((35999.04975 * T) % 360) - (0.000150 + 0.0000033 * T) * (T*T) ) % 360) * constants.deg2rad; | |
L = ((279.69668 + ((36000.76892 * T) % 360.0) + 0.0003025 * (T*T) ) % 360) * constants.deg2rad; | |
e = 0.01675104 - (0.0000418 + 0.000000126 * T) * T; | |
C = ((1.919460 - (0.004789 + 0.000014 * T) * T) * Math.sin(M) + (0.020094 - 0.000100 * T) * Math.sin(2 * M) + 0.000293 * Math.sin(3 * M)) * constants.deg2rad; | |
O = ((259.18 - 1934.142 * T) % 360) * constants.deg2rad; | |
Lsa = (L + C -((0.00569 - 0.00479 * Math.sin(O)) * constants.deg2rad)) % (2*Math.PI); | |
nu = (M + C) % (2*Math.PI); | |
R = 1.0000002 * (1.0 - (e*e)) / (1.0 + e * Math.cos(nu)); | |
eps = ((23.452294 - (0.0130125 + (0.00000164 - 0.000000503 * T) * T) * T + 0.00256 * Math.cos(O)) * constants.deg2rad); | |
R = constants.au * R; | |
var x = R * Math.cos (Lsa); | |
var y = R * Math.sin (Lsa) * Math.cos (eps); | |
var z = R * Math.sin (Lsa) * Math.sin (eps); | |
var w = R; | |
return { x : x, y : y, z : z, w : w } | |
}; | |
var ECItoWGS84 = function ECItoWGS84(eci, time) { | |
var jd_utc = unix2jd( time / 1000 ); | |
var theta = Math.atan2(eci.y, eci.x); // radians | |
var lon = (theta - thetaG_JD(jd_utc)) % (2*Math.PI); // radians | |
var r = Math.sqrt( (eci.x*eci.x) + (eci.y*eci.y) ); | |
var e2 = constants.f * (2 - constants.f); | |
var lat = Math.atan2(eci.z, r); // radians | |
var sin_phi, phi, c; | |
do { | |
phi = lat; | |
sin_phi = Math.sin(phi); | |
c = 1 / Math.sqrt(1 - e2 * (sin_phi*sin_phi)); | |
lat = Math.atan2(eci.z + constants.eqRearth * c * e2 * sin_phi, r); | |
} while (Math.abs(lat - phi) > 1e-10); | |
var alt = r / Math.cos(lat) - constants.eqRearth * c; // kilometers | |
if (lat > (Math.PI / 2)) { | |
lat -= (2 * Math.PI); | |
} | |
if (lon < -Math.PI) { | |
lon += 2*Math.PI; | |
} | |
return { | |
latitude: lat * constants.rad2deg, | |
longitude: lon * constants.rad2deg, | |
altitude: alt, | |
theta: theta * constants.rad2deg | |
}; | |
}; | |
// ** Astronomical, Geodetic & Mathematical Constants ** // | |
var constants = { | |
au: 149597870700, // [m] Astronomical unit | |
deltaUTCTT: 67.184, | |
deg2rad: 0.017453292519943295, | |
rad2deg: 57.29577951308232, | |
omega_E: 1.00273790934, | |
f: 1/298.257222101, // Earth, reciprocal of flattening IERS 2010 | |
eqRearth: 6378.1366, // Equatorial radius | |
meanRearth: 6371.0, // Mean radius | |
meanRsun: 1.392684e8, // Mean radius sun | |
omega: 7.292115e-5, // Nominal mean angular vel. of Earth rotation | |
}; | |
// Converts a UNIX timestamp to JD (Julian Date) | |
var unix2jd = function unix2jd(timestamp) { | |
return (timestamp / 86400.0) + 2440587.5; | |
}; | |
var thetaG_JD = function thetaG_JD(jd) { | |
// Reference: The 1992 Astronomical Almanac, page B6. | |
var UT, TU, GMST; | |
UT = (jd + 0.5) % 1; | |
jd = jd - UT; | |
TU = (jd - 2451545.0) / 36525; | |
GMST = 24110.54841 + TU * (8640184.812866 + TU * (0.093104 - TU * 6.2e-6)); | |
GMST = (GMST + 86400 * constants.omega_E * UT) % 86400; | |
return (2*Math.PI) * GMST / 86400; | |
}; | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment