Earth's climate.
Drag to rotate, mousewheel to zoom. Click a play/pause to animate. Click a country for details.
Built with d3gl
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>D3GL Earth Climate</title> | |
| <link rel="stylesheet" href="http://dcpos.ch/d3gl/css/demo.css" /> | |
| <link rel="stylesheet" href="http://dcpos.ch/d3gl/css/demo-countries.css" /> | |
| </head> | |
| <body> | |
| <ul id="picker"> | |
| </ul> | |
| <div id="climate-globe-labels"> | |
| <div id="pr"> | |
| Precipitation <br /> | |
| <img src="http://dcpos.ch/d3gl/demos/pr_scale.png" /> | |
| </div> | |
| <div id="tas"> | |
| Temperature <br /> | |
| <img src="http://dcpos.ch/d3gl/demos/tas_scale.png" /> | |
| </div> | |
| </div> | |
| <script type="text/javascript" src="http://dcpos.ch/d3gl/js/d3gl.min.js"></script> | |
| <script type="text/javascript" src="http://dcpos.ch/d3gl/data/country_code_map.js"></script> | |
| <script type="text/javascript"> | |
| var climateDataTypes = ["pr", "tas"]; | |
| var months = ["Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec"]; | |
| var monthIdx = 0; // jan displays by default | |
| function createControls(){ | |
| var selected, hovered, active, playing = false; | |
| d3.select("#picker").selectAll("li") | |
| .data(months) | |
| .enter().append("li") | |
| .text(function(d){ return d; }) | |
| .attr("id", function(d, i) { | |
| return d; | |
| }) | |
| .on('mouseover', function(d) { | |
| if(hovered) $(hovered).removeClass("hovered"); | |
| $(hovered = this).addClass("hovered"); | |
| }) | |
| .on('mouseout', function(d) { | |
| $(this).removeClass("hovered"); | |
| hovered = null; | |
| }) | |
| .on('mousedown', function(d) { | |
| if(active) $(active).removeClass("active"); | |
| $(active = this).addClass("active"); | |
| }) | |
| .on('mouseup', function(d) { | |
| $(this).removeClass("active"); | |
| }) | |
| .on('click', function(d) { | |
| if(selected) $(selected).removeClass("selected"); | |
| $(selected = this).addClass("selected"); | |
| monthIdx = months.indexOf(selected.id); | |
| }); | |
| d3.select("#picker").append("li") | |
| .attr("class", "play") | |
| .html(" ") | |
| .on('click', function(){ | |
| playing = !playing; | |
| $(this).attr("class", playing ? "play pause" : "play"); | |
| }); | |
| setInterval(function(){ | |
| if(!playing) return; | |
| monthIdx = (monthIdx+1)%12; | |
| if(selected) $(selected).removeClass("selected"); | |
| selected = $("#"+months[monthIdx]).addClass("selected"); | |
| }, 90); | |
| selected = $("#Jan").addClass("selected"); | |
| } | |
| function loadData(callback) { | |
| var data = {}; | |
| var nLoaded = 0; | |
| d3.json("http://dcpos.ch/d3gl/data/pr.json", function(datum){ | |
| var domain = [0, 100, 200, 300, 400, 500]; | |
| data["pr"] = {}; | |
| data["pr"].scale = d3.scale.linear() | |
| .domain(domain) | |
| .range([ | |
| "#ffffff", // [-INF, 100) | |
| "#00cc99", // [100, 200) | |
| "#008f6b", // [200, 300) | |
| "#00644b", // [300, 400) | |
| "#004634" // [400, INF] | |
| ]); | |
| data["pr"].data = datum; | |
| if(++nLoaded==2) { | |
| callback(data); | |
| } | |
| }); | |
| d3.json("http://dcpos.ch/d3gl/data/tas.json", function(datum){ | |
| data["tas"] = {}; | |
| data["tas"].scale = d3.scale.quantile() | |
| .domain([-40, 40]) | |
| .range([ | |
| "#000000", // [-40, -30) | |
| "#000099", // [-30, -20) | |
| "#0033cc", // [-20, -10) | |
| "#0066ff", // [-10, 0) | |
| "#0099ff", // [0, 10) | |
| "#00cc66", // [10, 20) | |
| "#ffcc00", // [20, 30) | |
| "#ff6600" // [30, 40] | |
| ]); | |
| data["tas"].data = datum; | |
| if(++nLoaded==2) { | |
| callback(data); | |
| } | |
| }); | |
| } | |
| function createGlobes(data) { | |
| // create the globe | |
| var globe = d3.gl.globe() | |
| .width(360).height(360) | |
| .texture("http://dcpos.ch/d3gl/img/earth-blank.png") | |
| .transparency(function(body){ | |
| return 0.6; | |
| }); | |
| var mouseoverPoint; | |
| globe.shapes("countries") | |
| .data(function(climateDataT) { | |
| return data[climateDataT]["data"]; | |
| }) | |
| .id(function(d){ | |
| // the id returned here must match the color in the shape texture | |
| // such that r*10*10 + g*10 + b = id | |
| return country_code_map["alpha_code_to_country"][d["ISO_code"]]["country-code"]; | |
| }) | |
| .color(function(d){ | |
| if(d==mouseoverPoint) return [50, 50, 50, 255]; | |
| var values = d["monthly_data"]; | |
| if(values.length>0) { | |
| var value = parseInt(d["monthly_data"][monthIdx]["data"]); | |
| var color = data[d["var"]].scale(value); | |
| color = color.slice(1); | |
| var sr = parseInt(color.substring(0, 2), 16); | |
| var sg = parseInt(color.substring(2, 4), 16); | |
| var sb = parseInt(color.substring(4), 16); | |
| return [sr, sg, sb, 255]; | |
| } | |
| return [0, 0, 0, 0]; | |
| }) | |
| .on("mousemove", function(evt) { | |
| if(evt.shape) { | |
| mouseoverPoint = evt.shape; | |
| } else { | |
| mouseoverPoint = null; | |
| } | |
| }) | |
| .on("click", function(evt) { | |
| if(evt.shape) { | |
| showDetails(evt, evt.shape); | |
| } else { | |
| $("#bubble").hide(); | |
| } | |
| }); | |
| d3.select("body").selectAll(".climate-globe") | |
| .data(climateDataTypes).enter() | |
| .append("span").attr("class", "climate-globe") | |
| .call(globe); | |
| } | |
| function showDetails(mouseEvent, data) { | |
| var name = country_code_map["alpha_code_to_country"][data["ISO_code"]]["name"]; | |
| var value = months[monthIdx]+": "; | |
| if(data["monthly_data"].length==0){ | |
| value += "N/A"; | |
| } else { | |
| value += Math.round(data["monthly_data"][monthIdx]["data"]); | |
| value += data["var"]=="pr" ? " mm" : " °C"; | |
| } | |
| var size = name.length > 15 ? "10pt" : "12pt"; | |
| var height = name.length > 15 ? 60 : 40; | |
| $("#bubble").show() | |
| .css("font-size", size) | |
| .css("height", height + "px") | |
| .css("left", mouseEvent.clientX) | |
| .css("top", mouseEvent.clientY + height/3) | |
| .html(name + "<br />" + value); | |
| } | |
| $(function(){ | |
| // create the ui | |
| createControls(); | |
| loadData(createGlobes); | |
| }); | |
| </script> | |
| <div id="bubble"></div> | |
| </body> | |
| </html> |