|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
body { |
|
background: #fcfcfa; |
|
} |
|
|
|
svg { |
|
font: 14px sans-serif; |
|
} |
|
|
|
.caption { |
|
font-weight: bold; |
|
} |
|
|
|
.key path { |
|
display: none; |
|
} |
|
|
|
.key line { |
|
stroke: #000; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.stroke { |
|
fill: none; |
|
stroke: #000; |
|
stroke-width: 3px; |
|
} |
|
|
|
.fill { |
|
fill: #fff; |
|
} |
|
|
|
.graticule { |
|
fill: none; |
|
stroke: #ddd; |
|
stroke-width: .5px; |
|
stroke-opacity: .5; |
|
} |
|
|
|
.countries{ |
|
fill: #ddd; |
|
} |
|
|
|
.boundary { |
|
fill: none; |
|
stroke: #ccc; |
|
stroke-width: .5px; |
|
} |
|
.label { |
|
fill: #777; |
|
} |
|
|
|
.year.label { |
|
font: 500 120px "Helvetica Neue"; |
|
fill: #ddd; |
|
} |
|
|
|
.year.label.active { |
|
fill: #aaa; |
|
} |
|
|
|
.overlay { |
|
fill: none; |
|
pointer-events: all; |
|
cursor: ew-resize; |
|
} |
|
|
|
aside { |
|
font-size: small; |
|
right: 0; |
|
position: absolute; |
|
width: 180px; |
|
} |
|
|
|
</style> |
|
<body> |
|
<script src="http://d3js.org/queue.v1.min.js"></script> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script src="http://d3js.org/topojson.v1.min.js"></script> |
|
<script src="http://d3js.org/d3.geo.projection.v0.min.js"></script> |
|
|
|
<div id="map"></div> |
|
<div> |
|
</div> |
|
<script> |
|
var width = 960, |
|
height = 600; |
|
|
|
var color = d3.scale.threshold() |
|
.domain([0, 20, 50, 90]) |
|
.range(["#ffffcc","#fcbba1","#fc9272","#ef3b2c","#a50f15"]); |
|
|
|
var x = d3.scale.linear() |
|
.domain([0, 100]) |
|
.range([0, 150]); |
|
|
|
var xAxis = d3.svg.axis() |
|
.scale(x) |
|
.orient("bottom") |
|
.tickSize(13) |
|
.tickValues(color.domain()); |
|
|
|
// Various accessors that specify the four dimensions of data to visualize. |
|
function key(d) { return d.id; } |
|
|
|
var usageByCountryCode = d3.map(); |
|
var minYear = 2011; |
|
var maxYear = 2011; |
|
|
|
var projection = d3.geo.kavrayskiy7() |
|
.scale(150) |
|
.translate([width / 2 - 40, height / 2 + 30]) |
|
.rotate([-10,0]) |
|
.precision(.1); |
|
|
|
var path = d3.geo.path() |
|
.projection(projection); |
|
|
|
var graticule = d3.geo.graticule(); |
|
|
|
var svg = d3.select("#map").append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
// Add the year label; the value is set on transition. |
|
var label = svg.append("text") |
|
//.attr("class", "year label") |
|
//.attr("text-anchor", "end") |
|
//.attr("y", 88) |
|
//.attr("x", width / 2 + 85) |
|
//.text(minYear); |
|
|
|
// ******** the scale |
|
var g = svg.append("g") |
|
.attr("class", "key") |
|
.attr("transform", "translate(15,50)"); |
|
|
|
g.selectAll("rect") |
|
.data(color.range().map(function(d, i) { |
|
return { |
|
x0: i ? x(color.domain()[i - 1]) : x.range()[0], |
|
x1: i < color.domain().length ? x(color.domain()[i]) : x.range()[1], |
|
z: d |
|
}; |
|
})) |
|
.enter().append("rect") |
|
.attr("height", 12) |
|
.attr("x", function(d) { return d.x0; }) |
|
.attr("width", function(d) { return d.x1 - d.x0; }) |
|
.style("fill", function(d) { return d.z; }); |
|
|
|
g.call(xAxis).append("text") |
|
.attr("class", "caption") |
|
.attr("y", -6) |
|
.text("Foreign currency debt (% of GDP)"); |
|
|
|
var noD = g.append("rect") |
|
.attr("height", 12) |
|
.attr("x", 0) |
|
.attr("y", 40) |
|
.attr("width", 15) |
|
.style("fill", "black"); |
|
|
|
g.append("text") |
|
.attr("class", "caption") |
|
// .attr("text-anchor", "end") |
|
.attr("x", 20) |
|
.attr("y", 50) |
|
.text("no data"); |
|
// ************ |
|
|
|
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"); |
|
|
|
svg.append("path") |
|
.datum(graticule) |
|
.attr("class", "graticule") |
|
.attr("d", path); |
|
|
|
|
|
queue() |
|
.defer(d3.json, "world-50m.json") |
|
.defer(d3.csv, "internetusage.csv", function(d) { usageByCountryCode.set(d["Country Code"], d); }) |
|
.await(ready); |
|
|
|
function ready(error, world) { |
|
var countries = topojson.feature(world, world.objects.countries).features, |
|
neighbors = topojson.neighbors(world.objects.countries.geometries); |
|
|
|
// *********** Add an overlay for the year label. |
|
var box = label.node().getBBox(); |
|
|
|
var overlay = svg.append("rect") |
|
.attr("class", "overlay") |
|
.attr("x", box.x) |
|
.attr("y", box.y) |
|
.attr("width", box.width) |
|
.attr("height", box.height) |
|
.on("mouseover", enableInteraction); |
|
// ************************** |
|
|
|
var state = svg.insert("g", ".graticule") |
|
.attr("class", "countries"); |
|
state.selectAll(".country") |
|
.data(countries) |
|
.enter() |
|
.append("path") |
|
.attr("class", "country") |
|
.attr("d", path) |
|
.attr("fill", function(d) { |
|
var code = usageByCountryCode.get(d.id); |
|
if (typeof code == "undefined") console.log("name=" + d.properties.name + ", code=" + d.id); |
|
return (typeof code != "undefined") ? color(code[minYear]) : "#ddd"; |
|
}) |
|
.append("title") |
|
.text(function(d) { |
|
var code = usageByCountryCode.get(d.id); |
|
/* |
|
return d.properties.name + ((typeof code != "undefined") ? "" : (code[minYear] + "% of GDP")) ; }) |
|
*/ |
|
; |
|
|
|
return d.properties.name + ((typeof code != "undefined") ? (": " + code[minYear] +"% of GDP") : ""); }) |
|
|
|
|
|
// call(usage); |
|
|
|
svg.insert("path", ".graticule") |
|
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; })) |
|
.attr("class", "boundary") |
|
.attr("d", path); |
|
|
|
|
|
// Start a transition that interpolates the data based on year. |
|
svg.transition() |
|
.duration(30000) |
|
.ease("linear") |
|
.tween("year", tweenYear) |
|
.each("end", enableInteraction); |
|
|
|
|
|
// After the transition finishes, you can mouseover to change the year. |
|
function enableInteraction() { |
|
var yearScale = d3.scale.linear() |
|
.domain([minYear, maxYear]) |
|
.range([box.x + 10, box.x + box.width - 10]) |
|
.clamp(true); |
|
|
|
// Cancel the current transition, if any. |
|
svg.transition().duration(0); |
|
|
|
overlay |
|
.on("mouseover", mouseover) |
|
.on("mouseout", mouseout) |
|
.on("mousemove", mousemove) |
|
.on("touchmove", mousemove); |
|
|
|
function mouseover() { |
|
label.classed("active", true); |
|
} |
|
|
|
function mouseout() { |
|
label.classed("active", false); |
|
} |
|
|
|
function mousemove() { |
|
displayYear(yearScale.invert(d3.mouse(this)[0])); |
|
} |
|
} |
|
|
|
// Tweens the entire chart by first tweening the year, and then the data. |
|
// For the interpolated data, the dots and label are redrawn. |
|
function tweenYear() { |
|
var year = d3.interpolateNumber(minYear, maxYear); |
|
return function(t) { displayYear(year(t)); }; |
|
} |
|
|
|
// Updates the display to show the specified year. |
|
function displayYear(year) { |
|
var yr = Math.round(year); |
|
svg.selectAll(".country") |
|
.style("fill", function(d) { |
|
var code = usageByCountryCode.get(d.id); |
|
return (typeof code != "undefined") ? color(code[yr]) : "gray"; |
|
}) |
|
label.text(yr); |
|
} |
|
|
|
|
|
}; |
|
d3.select(self.frameElement).style("height", height + "px"); |
|
|
|
</script> |