Last active
August 29, 2015 14:12
-
-
Save noahgibbs/9572e3d3fb503db18dcc to your computer and use it in GitHub Desktop.
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
<h1>D3 Shaded Globe</h1> | |
<!-- Add scripts for topoJson, jQuery --> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://d3js.org/topojson.v1.min.js"></script> | |
<script src="http://datamaps.github.io/scripts/datamaps.world.min.js"></script> | |
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" ></script> | |
<div class="world-map" | |
style="position: relative; height: 500px"> | |
<svg></svg> | |
</div> | |
<script> | |
function is_clipped (proj, lat, long) { | |
var clip_test_path = d3.geo.path().projection(proj); | |
return typeof(clip_test_path({ type: "MultiPoint", coordinates: [[ long, lat ]] })) == "undefined"; | |
} | |
var dark_ocean_color = '#0020CC'; | |
var medium_ocean_color = "#2040D0"; | |
var light_ocean_color = "#80B0DD"; | |
var extra_dark_ocean_color = '#000080'; | |
var dark_land_color = '#222222'; | |
var medium_land_color = "#444444"; | |
var light_land_color = "#BBBBBB"; | |
var sunlight_fadein_duration = 2000; | |
var sunlight_fadeout_duration = 5000; | |
var t0 = Date.now(); | |
var elt = $(".world-map")[0] | |
var width = $(elt).width(); | |
var height = $(elt).height(); | |
var projection = d3.geo.orthographic() | |
.scale(height / 2.1) | |
.translate([width / 2, height / 2]) | |
.clipAngle(90) | |
.precision(.5); | |
var path = d3.geo.path() | |
.projection(projection) | |
.pointRadius(3.0); | |
var svg = d3.select(".world-map svg") | |
.attr("width", width) | |
.attr("height", height); | |
var topo = Datamap.prototype.worldTopo; | |
var land = topojson.feature(topo, topo.objects.world).features; | |
var globe_group = svg.append("g"); | |
var land_group = svg.append("g"); | |
var svg_defs = svg.append("defs"); | |
var ocean_gradient = svg_defs.append("radialGradient").attr("id", "ocean_gradient").attr("cx", 0).attr("cy", 0).attr("r", 0).attr("gradientUnits", "userSpaceOnUse"); | |
var ocean_stop1 = ocean_gradient.append("stop").attr("offset", "0%").style("stop-color", light_ocean_color); | |
var ocean_stop2 = ocean_gradient.append("stop").attr("offset", "50%").style("stop-color", medium_ocean_color); | |
var ocean_stop3 = ocean_gradient.append("stop").attr("offset", "100%").style("stop-color", dark_ocean_color); | |
var land_gradient = svg_defs.append("radialGradient").attr("id", "land_gradient") .attr("cx", 0).attr("cy", 0).attr("r", 0).attr("gradientUnits", "userSpaceOnUse"); | |
var land_stop1 = land_gradient.append("stop").attr("offset", "0%").style("stop-color", light_land_color); | |
var land_stop2 = land_gradient.append("stop").attr("offset", "70%").style("stop-color", medium_land_color); | |
var land_stop3 = land_gradient.append("stop").attr("offset", "100%").style("stop-color", dark_land_color); | |
// Globe origin and velocity | |
var origin = [0.0, 0.0]; | |
var velocity = [0.03, 0.0]; | |
var last_iter_lightness = ""; | |
console.debug("Changed[6]"); | |
d3.timer(function() { | |
width = $(elt).width(); | |
height = $(elt).height(); | |
d3.select(elt).select("svg").attr("width", width); // Resize svg on window resize | |
// Rotation occurs independent of display mode, unless paused | |
dt = Date.now() - t0; | |
projection.rotate([velocity[0] * dt + origin[0], velocity[1] * dt + origin[1]]) | |
.scale(height / 2.1) | |
.translate([width / 2, height / 2]); | |
gr = gradients_for_brightest_and_darkest(path, projection, new Date() - 0, width, height); | |
// Single big globe circle, with oceans | |
globe_group.selectAll("circle").data([1]).enter() | |
.append("circle") | |
.attr("r", height / 2.1) | |
.attr("cx", "50%") | |
.attr("cy", "50%") | |
.attr("stroke", "black") | |
.attr("fill", "url(#ocean_gradient)"); | |
// Country boundaries and land | |
land_sel = land_group.selectAll("path") | |
.data(land, function (obj) { return obj.id; }); | |
land_sel.enter() | |
.append("path") | |
.attr("d", path) | |
.attr("stroke", "darkgray") | |
.attr("fill", "url(#land_gradient)"); | |
land_sel.attr("d", path); // Update when needed | |
return null; // Don't cancel timer | |
}); | |
function gradients_for_brightest_and_darkest(path, proj, current_t, width, height) { | |
// Calculate brightest and darkest longitudes | |
var day_in_millis = 24 * 60 * 60 * 1000; | |
var half_day = day_in_millis / 2; | |
var offset_from_day_start = current_t % day_in_millis; | |
var offset_from_noon = offset_from_day_start - half_day; | |
// The sun moves one degree of longitude every 240 seconds | |
var degrees_from_noon = parseInt(offset_from_noon / 240000.0); | |
if(degrees_from_noon >= 180.0) | |
degrees_from_noon -= 360.0; | |
lightest_longitude = -degrees_from_noon; | |
// If the darkest is clipped, the lightest isn't and vice-versa... | |
// At least with an Albers projection. If we change the projection, | |
// we'll need a different fill gradient. | |
this_iter_lightness = "dark" | |
if(!is_clipped(proj, 30.0, lightest_longitude)) { | |
// Lightest spot visible, use gradient fill | |
this_iter_lightness = "light"; | |
projected_lightest = proj([lightest_longitude, 30.0]); | |
ocean_gradient.attr("cx", "50%").attr("cy", "50%").attr("r", height / 2.1) | |
.attr("fx", projected_lightest[0]).attr("fy", projected_lightest[1]); | |
land_gradient.attr("cx", width / 2.0).attr("cy", height / 2.0).attr("r", height / 2.1) | |
.attr("fx", projected_lightest[0]).attr("fy", projected_lightest[1]); | |
} | |
// First iteration, assume we *didn't* just transition | |
if(last_iter_lightness == "") last_iter_lightness = this_iter_lightness; | |
if(last_iter_lightness != this_iter_lightness) { | |
if(this_iter_lightness == "light") { | |
// Transition to light gradients | |
ocean_stop1.transition().duration(sunlight_fadein_duration).style("stop-color", light_ocean_color); | |
ocean_stop2.transition().duration(sunlight_fadein_duration).style("stop-color", medium_ocean_color); | |
ocean_stop3.transition().duration(sunlight_fadein_duration).style("stop-color", dark_ocean_color); | |
land_stop1.transition().duration(sunlight_fadein_duration).style("stop-color", light_land_color); | |
land_stop2.transition().duration(sunlight_fadein_duration).style("stop-color", medium_land_color); | |
land_stop3.transition().duration(sunlight_fadein_duration).style("stop-color", dark_land_color); | |
} else { | |
ocean_stop1.transition().duration(sunlight_fadeout_duration).style("stop-color", dark_ocean_color); | |
ocean_stop2.transition().duration(sunlight_fadeout_duration).style("stop-color", dark_ocean_color); | |
ocean_stop3.transition().duration(sunlight_fadeout_duration).style("stop-color", extra_dark_ocean_color); | |
land_stop1.transition().duration(sunlight_fadeout_duration).style("stop-color", dark_land_color); | |
land_stop2.transition().duration(sunlight_fadeout_duration).style("stop-color", dark_land_color); | |
land_stop3.transition().duration(sunlight_fadeout_duration).style("stop-color", dark_land_color); | |
} | |
last_iter_lightness = this_iter_lightness; | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment