|
"use strict"; |
|
|
|
//d3.chart.min.js |
|
(function(window){"use strict";var d3=window.d3;var hasOwnProp=Object.hasOwnProperty;var d3cAssert=function(test,message){if(test){return}throw new Error("[d3.chart] "+message)};d3cAssert(d3,"d3.js is required");d3cAssert(typeof d3.version==="string"&&d3.version.match(/^3/),"d3.js version 3 is required");"use strict";var lifecycleRe=/^(enter|update|merge|exit)(:transition)?$/;var Layer=function(base){d3cAssert(base,"Layers must be initialized with a base.");this._base=base;this._handlers={}};Layer.prototype.dataBind=function(){d3cAssert(false,"Layers must specify a `dataBind` method.")};Layer.prototype.insert=function(){d3cAssert(false,"Layers must specify an `insert` method.")};Layer.prototype.on=function(eventName,handler,options){options=options||{};d3cAssert(lifecycleRe.test(eventName),"Unrecognized lifecycle event name specified to `Layer#on`: '"+eventName+"'.");if(!(eventName in this._handlers)){this._handlers[eventName]=[]}this._handlers[eventName].push({callback:handler,chart:options.chart||null});return this._base};Layer.prototype.off=function(eventName,handler){var handlers=this._handlers[eventName];var idx;d3cAssert(lifecycleRe.test(eventName),"Unrecognized lifecycle event name specified to `Layer#off`: '"+eventName+"'.");if(!handlers){return this._base}if(arguments.length===1){handlers.length=0;return this._base}for(idx=handlers.length-1;idx>-1;--idx){if(handlers[idx].callback===handler){handlers.splice(idx,1)}}return this._base};Layer.prototype.draw=function(data){var bound,entering,events,selection,handlers,eventName,idx,len;bound=this.dataBind.call(this._base,data);d3cAssert(bound&&bound.call===d3.selection.prototype.call,"Invalid selection defined by `Layer#dataBind` method.");d3cAssert(bound.enter,"Layer selection not properly bound.");entering=bound.enter();entering._chart=this._base._chart;events=[{name:"update",selection:bound},{name:"enter",selection:this.insert.bind(entering)},{name:"merge",selection:bound},{name:"exit",selection:bound.exit.bind(bound)}];for(var i=0,l=events.length;i<l;++i){eventName=events[i].name;selection=events[i].selection;if(typeof selection==="function"){selection=selection()}if(selection.empty()){continue}d3cAssert(selection&&selection.call===d3.selection.prototype.call,"Invalid selection defined for '"+eventName+"' lifecycle event.");handlers=this._handlers[eventName];if(handlers){for(idx=0,len=handlers.length;idx<len;++idx){selection._chart=handlers[idx].chart||this._base._chart;selection.call(handlers[idx].callback)}}handlers=this._handlers[eventName+":transition"];if(handlers&&handlers.length){selection=selection.transition();for(idx=0,len=handlers.length;idx<len;++idx){selection._chart=handlers[idx].chart||this._base._chart;selection.call(handlers[idx].callback)}}}};"use strict";d3.selection.prototype.layer=function(options){var layer=new Layer(this);var eventName;layer.dataBind=options.dataBind;layer.insert=options.insert;if("events"in options){for(eventName in options.events){layer.on(eventName,options.events[eventName])}}this.on=function(){return layer.on.apply(layer,arguments)};this.off=function(){return layer.off.apply(layer,arguments)};this.draw=function(){return layer.draw.apply(layer,arguments)};return this};"use strict";function extend(object){var argsIndex,argsLength,iteratee,key;if(!object){return object}argsLength=arguments.length;for(argsIndex=1;argsIndex<argsLength;argsIndex++){iteratee=arguments[argsIndex];if(iteratee){for(key in iteratee){object[key]=iteratee[key]}}}return object}var initCascade=function(instance,args){var ctor=this.constructor;var sup=ctor.__super__;if(sup){initCascade.call(sup,instance,args)}if(hasOwnProp.call(ctor.prototype,"initialize")){this.initialize.apply(instance,args)}};var transformCascade=function(instance,data){var ctor=this.constructor;var sup=ctor.__super__;if(this===instance&&hasOwnProp.call(this,"transform")){data=this.transform(data)}if(hasOwnProp.call(ctor.prototype,"transform")){data=ctor.prototype.transform.call(instance,data)}if(sup){data=transformCascade.call(sup,instance,data)}return data};var Chart=function(selection,chartOptions){this.base=selection;this._layers={};this._attached={};this._events={};if(chartOptions&&chartOptions.transform){this.transform=chartOptions.transform}initCascade.call(this,this,[chartOptions])};Chart.prototype.initialize=function(){};Chart.prototype.unlayer=function(name){var layer=this.layer(name);delete this._layers[name];delete layer._chart;return layer};Chart.prototype.layer=function(name,selection,options){var layer;if(arguments.length===1){return this._layers[name]}if(arguments.length===2){if(typeof selection.draw==="function"){selection._chart=this;this._layers[name]=selection;return this._layers[name]}else{d3cAssert(false,"When reattaching a layer, the second argument "+"must be a d3.chart layer")}}layer=selection.layer(options);this._layers[name]=layer;selection._chart=this;return layer};Chart.prototype.attach=function(attachmentName,chart){if(arguments.length===1){return this._attached[attachmentName]}this._attached[attachmentName]=chart;return chart};Chart.prototype.draw=function(data){var layerName,attachmentName,attachmentData;data=transformCascade.call(this,this,data);for(layerName in this._layers){this._layers[layerName].draw(data)}for(attachmentName in this._attached){if(this.demux){attachmentData=this.demux(attachmentName,data)}else{attachmentData=data}this._attached[attachmentName].draw(attachmentData)}return this};Chart.prototype.on=function(name,callback,context){var events=this._events[name]||(this._events[name]=[]);events.push({callback:callback,context:context||this,_chart:this});return this};Chart.prototype.once=function(name,callback,context){var self=this;var once=function(){self.off(name,once);callback.apply(this,arguments)};return this.on(name,once,context)};Chart.prototype.off=function(name,callback,context){var names,n,events,event,i,j;if(arguments.length===0){for(name in this._events){this._events[name].length=0}return this}if(arguments.length===1){events=this._events[name];if(events){events.length=0}return this}names=name?[name]:Object.keys(this._events);for(i=0;i<names.length;i++){n=names[i];events=this._events[n];j=events.length;while(j--){event=events[j];if(callback&&callback===event.callback||context&&context===event.context){events.splice(j,1)}}}return this};Chart.prototype.trigger=function(name){var args=Array.prototype.slice.call(arguments,1);var events=this._events[name];var i,ev;if(events!==undefined){for(i=0;i<events.length;i++){ev=events[i];ev.callback.apply(ev.context,args)}}return this};Chart.extend=function(name,protoProps,staticProps){var parent=this;var child;if(protoProps&&hasOwnProp.call(protoProps,"constructor")){child=protoProps.constructor}else{child=function(){return parent.apply(this,arguments)}}extend(child,parent,staticProps);var Surrogate=function(){this.constructor=child};Surrogate.prototype=parent.prototype;child.prototype=new Surrogate;if(protoProps){extend(child.prototype,protoProps)}child.__super__=parent.prototype;Chart[name]=child;return child};"use strict";d3.chart=function(name){if(arguments.length===0){return Chart}else if(arguments.length===1){return Chart[name]}return Chart.extend.apply(Chart,arguments)};d3.selection.prototype.chart=function(chartName,options){if(arguments.length===0){return this._chart}var ChartCtor=Chart[chartName];d3cAssert(ChartCtor,"No chart registered with name '"+chartName+"'");return new ChartCtor(this,options)};d3.selection.enter.prototype.chart=function(){return this._chart};d3.transition.prototype.chart=d3.selection.enter.prototype.chart})(this); |
|
|
|
|
|
//atlas.min.js |
|
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory():typeof define==="function"&&define.amd?define(factory):factory()})(this,function(){"use strict";var configuration={};function layer_exit(){var chart=this.chart();return this.remove()}function layer_merge(){var chart=this.chart();if(chart._projection){chart._projection.scale(chart._scale).rotate(chart._rotation).precision(chart._precision).translate(chart._translate)}chart._path.projection(chart._projection).pointRadius(chart._pointRadius);return this.attr("d",chart._path)}function chart_initialize(options){var depth=0;var chart=this;chart.options=options||{};chart._w=chart.base.attr("width")||960;chart._h=chart.base.attr("height")||500;chart.base=chart.base.append("g").attr("class","base");chart._projection=d3.geo.orthographic().clipAngle(90);chart._path=d3.geo.path();chart._graticule=null;chart._sphere=null;chart._precision=Math.sqrt(2);chart._scale=1;chart._translate=[0,0];chart._rotation=[0,0,0];var layerSphere=chart.base.append("g").attr("class","sphere").append("path");var layerGraticule=chart.base.append("g").attr("class","graticule").append("path");chart.options.layers.forEach(function(layer){var layerBase=chart.base.append("g").attr("class","layer-base-"+layer.object+"-"+depth);var layerConfig={dataBind:layer.databind||function(data){var chart=this.chart();var toBind=Array.isArray(data[layer.object])?data[layer.object]:[data[layer.object]];toBind=layer.filter?toBind.filter(layer.filter):toBind;return this.selectAll("."+layer.object).data(toBind)},insert:layer.insert||function(){var chart=this.chart();var selection=this.append("path").attr("class",layer.class||"").classed(layer.object,true).attr("id",layer.id||function(d,i){return i});if(layer.interactions){for(var e in layer.interactions){if(layer.interactions.hasOwnProperty(e)){selection.on(e,layer.interactions[e])}}}return selection},events:layer.events||{merge:layer_merge,exit:layer_exit}};chart.layer("layer-"+layer.object+"-"+depth,layerBase,layerConfig);depth++});chart.options.labels.forEach(function(layer){var labelBase=chart.base.append("g").attr("class","layer-labels-"+layer.object+"-"+depth);var labelConfig={dataBind:function(data){var chart=this.chart();var toBind=Array.isArray(data[layer.object])?data[layer.object]:[data[layer.object]];toBind=layer.filter?toBind.filter(layer.filter):toBind;toBind=toBind.filter(function(d){var c=chart._path.centroid(d);return!(isNaN(c[0])||isNaN(c[1]))});return this.selectAll("."+"label-"+layer.object).data(toBind)},insert:function(){var chart=this.chart();var selection=this.append("text").attr("class",layer.class||"").classed("label-"+layer.object,true);return selection},events:layer.events||{update:function(){this.attr("transform",function(d){return"translate("+chart._path.centroid(d)+")"}).attr("id",layer.id||"").text(layer.text);return this},exit:function(){this.remove();return this}}};chart.layer("labels-"+layer.object+"-"+depth,labelBase,labelConfig);depth++});chart.on("change:projection",function(){if(this.data){if(chart._projection){chart._projection.scale(chart._scale).rotate(chart._rotation).precision(chart._precision).translate(chart._translate);layerGraticule.datum(chart._graticule).attr("d",chart._path).attr("class","graticule");layerSphere.datum(chart._sphere).attr("d",chart._path).attr("class","sphere")}chart._path.projection(chart._projection).pointRadius(chart._pointRadius);this.draw(this.data)}})}configuration.initialize=chart_initialize;function chart_transform(data){var chart=this;if(!(data.type=="Topology"))return data;var t={};this.options.layers.forEach(function(layer){if(!data.objects.hasOwnProperty(layer.object)){return[]}if(data.objects[layer.object].type=="GeometryCollection"){t[layer.object]=topojson.feature(data,data.objects[layer.object]).features}if(data.objects[layer.object].type=="MultiPolygon"){t[layer.object]=topojson.feature(data,data.objects[layer.object])}});this.topology=data;this.data=t;chart.trigger("change:projection");return t}configuration.transform=chart_transform;function center(_){if(arguments.length===0){return this._scale}if(_)this._center=_;this.trigger("change:projection");return this}configuration.center=center;function graticule(_){if(arguments.length===0){return this._graticule}this._graticule=_;this.trigger("change:projection");return this}configuration.graticule=graticule;function height(_){if(arguments.length===0){return this._h}this._h=_;this.trigger("change:projection");return this}configuration.height=height;function path(_){if(arguments.length===0){return this._path}if(_)this._path=_;this.trigger("change:projection");return this}configuration.path=path;function projection(_){if(arguments.length===0){return this._projection}if(_||_===null)this._projection=_;this.trigger("change:projection");return this}configuration.projection=projection;function rotate(_){if(arguments.length===0){return this._rotation}if(_)this._rotation=_;this.trigger("change:projection");return this}configuration.rotate=rotate;function scale(_){if(arguments.length===0){return this._scale}if(_)this._center=_;this.trigger("change:projection");return this}configuration.scale=scale;function sphere(_){if(arguments.length===0){return this._sphere}this._sphere=_;this.trigger("change:projection");return this}configuration.sphere=sphere;function translate(_){if(arguments.length===0){return this._translate}if(_)this._translate=_;this.trigger("change:projection");return this}configuration.translate=translate;function precision(_){if(arguments.length===0){return this._precision}if(_)this._precision=_;this.trigger("change:projection");return this}configuration.precision=precision;function pointRadius(_){if(arguments.length===0){return this._pointRadius}if(_)this._pointRadius=_;this.trigger("change:projection");return this}configuration.pointRadius=pointRadius;function width(_){if(arguments.length===0){return this._w}this._w=_;this.trigger("change:projection");return this}configuration.width=width;function zoomToLayer(_,_filter){if(arguments.length===0){return this._projection}var f=typeof _filter==="undefined"?function(d){return true}:_filter;var chart=this;if(this.data){var layerObject=_;var collection={type:"FeatureCollection",features:this.data[layerObject].filter(f)};var b=collection.features.length==0?[[-1,-1],[1,1]]:chart._path.bounds(collection),s=.9/Math.max((b[1][0]-b[0][0])/chart._w,(b[1][1]-b[0][1])/chart._h),t=[(chart._w-s*(b[1][0]+b[0][0]))/2,(chart._h-s*(b[1][1]+b[0][1]))/2];chart._scale=s;chart._translate=t}chart.trigger("change:projection");return this}configuration.zoomToLayer=zoomToLayer;function rotateToLayer(_,_filter){if(arguments.length===0){return this._projection}var f=typeof _filter==="undefined"?function(d){return true}:_filter;var chart=this;if(this.data){var layerObject=_;var collection={type:"FeatureCollection",features:this.data[layerObject]};var filteredCollection={type:"FeatureCollection",features:this.data[layerObject].filter(f)};var b=[[-1,-1],[1,1]],c=d3.geo.centroid(filteredCollection),s=.9/Math.max((b[1][0]-b[0][0])/chart._w,(b[1][1]-b[0][1])/chart._h),t=[(chart._w-s*(b[1][0]+b[0][0]))/2,(chart._h-s*(b[1][1]+b[0][1]))/2];chart._scale=s;chart._translate=t;c=[-c[0],-c[1]];chart._rotation=c}chart.trigger("change:projection");return this}configuration.rotateToLayer=rotateToLayer;d3.chart("atlas",configuration)}); |
|
|
|
d3.select(self.frameElement).style("height", "960px"); |
|
|
|
(function() { |
|
var margin = {top: 0, right: 0, bottom: 0, left: 0}, |
|
padding = {top: 0, right: 0, bottom: 0, left: 0}, |
|
outerWidth = 960, |
|
outerHeight = 960, |
|
innerWidth = outerWidth - margin.left - margin.right, |
|
innerHeight = outerHeight - margin.top - margin.bottom, |
|
width = innerWidth - padding.left - padding.right, |
|
height = innerHeight - padding.top - padding.bottom; |
|
|
|
var smallFontScale = d3.scale.linear().domain([height/2, 0]).range([8, 18]); |
|
var bigFontScale = d3.scale.linear().domain([height/2, 0]).range([8, 22]); |
|
var opacityScale = d3.scale.linear().domain([.8*height/2, 0]).range([0, 1]); |
|
var tightOpacityScale = d3.scale.linear().domain([.6*height/2, 0]).range([0, .6]); |
|
var hexagonOpacity = d3.scale.linear().domain([.85*height/2, 0]).range([0, .8]); |
|
var hexagonStroke = d3.scale.sqrt().range([.5, 8]); |
|
var z = d3.scale.linear() |
|
.range([4, 250]); |
|
var data; |
|
|
|
var time0 = Date.now(), |
|
time1; |
|
|
|
var fps = d3.select("#fps span"); |
|
|
|
var p = [outerWidth/2, outerHeight/2], vx = -40, vy = 0; |
|
|
|
// map screen distance to incremental change in globe rotation (per unit time) |
|
// exponential function mimics a "gimbal-like" feel with softened control around center |
|
var dλ = d3.scale.pow() |
|
.domain([-outerWidth/2, outerWidth/2]) |
|
.range([15, -15]) |
|
.exponent(2.2); |
|
|
|
var dφ = d3.scale.pow() |
|
.domain([-outerHeight/2, outerHeight/2]) |
|
.range([-15, 15]) |
|
.exponent(2.2); |
|
|
|
// clamp globe longitude rotation (if needed) |
|
var λ = d3.scale.linear().clamp(true) |
|
.domain([-180, 180]) |
|
.range([-180, 180]); |
|
|
|
// clamp globe latitude rotation |
|
var φ = d3.scale.linear().clamp(true) |
|
.domain([-90, 90]) |
|
.range([-90, 90]); |
|
|
|
var radius = d3.scale.linear() |
|
.range([4, 25]); |
|
|
|
var center = [165, 0]; |
|
|
|
var bins; |
|
|
|
var hexbin = d3.hexbin() |
|
.radius(1); //spherical coordinates, degrees |
|
|
|
var background = d3.select("#nuclear-testing"); |
|
|
|
var svg = background.append("g") |
|
.attr("transform", "translate(" + (margin.left + padding.left) + "," + (margin.top + padding.top) + ")") |
|
.attr("id", "globe"); |
|
|
|
|
|
// cache a layer added to the map |
|
var g_hexagons; |
|
|
|
var powers = { |
|
"FRA": "France", |
|
"CHN": "China", |
|
"PAK": "Pakistan", |
|
"IND": "India", |
|
"USA": "United States", |
|
"RUS": "Russia", |
|
"GBR": "UK", |
|
"PRK": "North Korea", |
|
"DZA": "" |
|
} |
|
|
|
var options = {}; |
|
options.layers = []; |
|
options.labels = []; |
|
|
|
// define a layer and labels |
|
options.layers.push({ |
|
class: "land", |
|
object: "land" |
|
}); |
|
|
|
options.layers.push({ |
|
class: "country", |
|
id: function(d) {return d.properties["adm0_a3"]}, |
|
object: "countries", |
|
filter: function(d) {return powers.hasOwnProperty(d.properties["adm0_a3"])}, |
|
}); |
|
|
|
// country labels |
|
options.labels.push({ |
|
id: function(d) {return d.properties["adm0_a3"]}, |
|
object: "countries", |
|
class: "country", |
|
text: function(d) {return powers[d.properties["adm0_a3"]]}, |
|
filter: function(d) {return powers.hasOwnProperty(d.properties["adm0_a3"])} |
|
}); |
|
|
|
// placeholder layer, not tied to topojson object; used for drawing order |
|
options.layers.push({ |
|
object: "hexagons" |
|
}); |
|
|
|
// locators for important events |
|
options.layers.push({ |
|
object: "events", |
|
class: "event", |
|
text: function(d) {return d.properties.blurb} |
|
}); |
|
|
|
// labels for important events -- tied to "events" topology object |
|
options.labels.push({ |
|
object: "events", |
|
class: "blurb", |
|
text: function(d) {return d.properties.blurb} |
|
}); |
|
|
|
// d3.chart grafts itself onto and modifies the d3 selection. |
|
// Sets up all of the layer groups before the data is bound |
|
var globe = svg.chart("atlas", options) |
|
.width(width) |
|
.height(height) |
|
.rotate(center) |
|
.sphere({type: "Sphere"}) |
|
.precision(.3) |
|
.graticule(d3.geo.graticule().step([20, 20])) |
|
.projection(d3.geo.orthographic().clipAngle(90)) |
|
.pointRadius(function(d) { |
|
if(d.properties) { |
|
return (d.properties.blurb) ? 30 : 5; |
|
} |
|
}) |
|
/* |
|
Define a callback to be called when "change:projection" events are triggered |
|
within the chart. |
|
|
|
Mutators like .rotate() and .zoomToLayer() trigger change:projection internally. |
|
This approach allows the *this* context passed to the callback to refer to the chart instance. |
|
|
|
This is useful when we want access things like the internal projection |
|
and d3.geo.path instance for calculating the centroid of a feature. |
|
*/ |
|
globe.on("change:projection", function() { |
|
var chart = this; |
|
var path = chart._path; |
|
var projection = chart._projection; |
|
|
|
if(!chart.data) return; |
|
|
|
svg.selectAll(".label-countries") |
|
.each(function(d) { |
|
var c = path.centroid(d); |
|
var dx = width/2 - c[0]; |
|
var dy = height/2 - c[1]; |
|
d.distance = Math.sqrt(dx * dx + dy * dy); |
|
}) |
|
.style("fill-opacity", function(d) {return tightOpacityScale(d.distance)}) |
|
.style("font-size", function(d) {return smallFontScale(d.distance)}) |
|
|
|
svg.selectAll(".event") |
|
.each(function(d) { |
|
var c = path.centroid(d); |
|
var dx = width/2 - c[0]; |
|
var dy = height/2 - c[1]; |
|
d.distance = Math.sqrt(dx * dx + dy * dy); |
|
}) |
|
.style("stroke-opacity", function(d) {return opacityScale(d.distance)}) |
|
|
|
svg.selectAll(".blurb") |
|
.attr("x", 30) |
|
.attr("y", 60) |
|
.each(function(d) { |
|
var c = path.centroid(d); |
|
var dx = width/2 - c[0]; |
|
var dy = height/2 - c[1]; |
|
d.distance = Math.sqrt(dx * dx + dy * dy); |
|
}) |
|
.style("opacity", function(d) {return opacityScale(d.distance)}) |
|
.style("font-size", function(d) {return bigFontScale(d.distance)}) |
|
}) |
|
|
|
queue() |
|
.defer(d3.json, "combined.json") |
|
.await(ready); |
|
|
|
// The default export, called when the required data (topojson file) is ready. |
|
function ready(error, topology) { |
|
data = topology; |
|
|
|
var locations = topojson.feature(topology, topology.objects.nuclear).features; |
|
locations.forEach(function(d) { |
|
var p = d.geometry.coordinates; |
|
d[0] = p[0], d[1] = p[1]; |
|
}); |
|
|
|
globe.draw(data) |
|
.rotateToLayer("land"); |
|
|
|
var projection = globe.projection(); |
|
var path = globe.path(); |
|
|
|
bins = hexbin(locations).sort(function(a, b) { return b.length - a.length;}); |
|
|
|
// calculate total yield for each bin |
|
bins.map(function(bin) { |
|
var sum = 0; |
|
var length = bin.length; |
|
for(var i = 0; i < length; i++) { |
|
sum += +(bin[i].properties.yield) |
|
} |
|
bin.totalyield = sum; |
|
}) |
|
|
|
var yieldExtent = d3.extent(bins, function(bin) { |
|
return bin.totalyield; |
|
}) |
|
|
|
// set scale domains for nuclear test symbology |
|
radius.domain(yieldExtent); |
|
z.domain(yieldExtent); |
|
hexagonStroke.domain([1, d3.max(bins, function(bin) {return bin.length})]); |
|
|
|
|
|
// find dominate country in bin and use that for color coding |
|
bins.map(function(d) { |
|
var length = d.length; |
|
for(var i = 0; i < length; i++) { |
|
var country = d[i].properties.country; |
|
} |
|
d.properties = {}; |
|
d.properties.country = country; |
|
}) |
|
|
|
g_hexagons = d3.select(".layer-base-hexagons-2"); |
|
updateHexagons(projection, path); |
|
|
|
background.on("mousemove", function() { |
|
p = d3.mouse(this); |
|
vx = p[0] - outerWidth/2; |
|
vy = p[1] - outerHeight/2; |
|
}); |
|
|
|
d3.timer(function(){ |
|
center[0] = center[0] + dλ(vx); |
|
center[1] = φ(center[1] + dφ(vy)); // clamped to avoid rotating past +/- 90 degrees |
|
globe.rotate([center[0], center[1]]); |
|
updateHexagons(projection, path); |
|
time1 = Date.now(); |
|
fps.text(Math.round(1000 / (time1 - time0))); |
|
time0 = time1; |
|
}) |
|
|
|
// NORTH EAST HIGHLIGHT (from sun) |
|
svg.append("circle") |
|
.attr("cx", width / 2).attr("cy", height / 2) |
|
.attr("r", globe.scale()) |
|
.attr("class","noclicks") |
|
.style("fill", "url(#globe_highlight)"); |
|
|
|
// SOUTH WEST SHADING |
|
svg.append("circle") |
|
.attr("cx", width / 2).attr("cy", height / 2) |
|
.attr("r", globe.scale()) |
|
.attr("class","noclicks") |
|
.style("fill", "url(#globe_shading)"); |
|
} |
|
|
|
// ENTER, UPDATE, EXIT for hexagons (bins generated from hexbining of nuclear tests) |
|
function updateHexagons(projection, path) { |
|
var hexagons = g_hexagons.selectAll(".hexagon") |
|
.data(bins.filter(function(d) {return visible(d, path)})) |
|
|
|
hexagons.enter().append("circle") |
|
|
|
hexagons.attr("r", function(d) { return radius(d.totalyield); }) |
|
.each(function(d, i) { |
|
var test = { |
|
"type": "Point", |
|
"coordinates": [d.x, d.y] // spherical coordinates |
|
} |
|
var centroid = path.centroid(test); |
|
var dx = centroid[0] - width/2; |
|
var dy = height/2 - centroid[1]; //dy is pos when centroid is above equator |
|
// d.angle = Math.atan2(dx, dy); // angle of spike |
|
// d.z = z(d.totalyield) |
|
// d.tip = [d.z * Math.cos(d.angle), -1 * d.z * Math.sin(d.angle)] |
|
d.distance = Math.sqrt(dx * dx + dy * dy); |
|
}) |
|
.classed("hexagon", true) |
|
.attr("id", function(d) {return d.properties.country}) |
|
.attr("transform", function(d) { return "translate(" + projection([d.x, d.y])[0] + "," + projection([d.x, d.y])[1] + ")"; }) |
|
.style("stroke-width", function(d) {return hexagonStroke(d.length)}) |
|
.style("fill-opacity", function(d) {return hexagonOpacity(d.distance)}) |
|
.style("stroke-opacity", function(d) {return hexagonOpacity(d.distance)}) |
|
|
|
hexagons.exit().remove(); |
|
} |
|
|
|
// Run a point through the geometry pipeline to test for orthographic clipping |
|
function visible(d, path) { |
|
var test = { |
|
"type": "Point", |
|
"coordinates": [d.x, d.y] // spherical coordinates |
|
} |
|
var c = path.centroid(test); |
|
return !(isNaN(c[0]) || isNaN(c[1])); |
|
} |
|
|
|
})(); |