|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
h1 { |
|
position: absolute; |
|
top: 280px; |
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
font-size: 18px; |
|
text-align: center; |
|
width: 960px; |
|
pointer-events: none; |
|
} |
|
|
|
</style> |
|
<h1></h1> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script src="http://d3js.org/queue.v1.min.js"></script> |
|
<script src="http://d3js.org/topojson.v1.min.js"></script> |
|
<script> |
|
|
|
var width = 960, |
|
height = 500; |
|
|
|
var projection = d3.geo.orthographic() |
|
.scale(248) |
|
.clipAngle(90); |
|
|
|
var canvas = d3.select("body").append("canvas") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
var c = canvas.node().getContext("2d"); |
|
|
|
// this is our "bitmap" image for color lookups |
|
var hidden = document.createElement("canvas"), |
|
c2 = hidden.getContext('2d'); |
|
hidden.width = width; |
|
hidden.height = height; |
|
|
|
// comment this out to hide the bitmap |
|
document.body.appendChild(hidden); |
|
|
|
var colorToFeature = {}; |
|
|
|
var path = d3.geo.path() |
|
.projection(projection) |
|
.context(c); |
|
|
|
var title = d3.select("h1"); |
|
|
|
queue() |
|
.defer(d3.json, "world-110m.json") |
|
.defer(d3.tsv, "world-country-names.tsv") |
|
.await(ready); |
|
|
|
function ready(error, world, names) { |
|
var globe = {type: "Sphere"}, |
|
land = topojson.feature(world, world.objects.land), |
|
countries = topojson.feature(world, world.objects.countries).features, |
|
borders = topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }), |
|
i = -1, |
|
n = countries.length; |
|
|
|
var hexFormat = d3.format('06x'), |
|
nextColor = (function() { |
|
var c = 512, step = 2048; |
|
return function() { |
|
return '#' + hexFormat(c += step); |
|
}; |
|
})(); |
|
|
|
countries = countries.filter(function(d) { |
|
return names.some(function(n) { |
|
if (d.id == n.id) return d.name = n.name; |
|
}); |
|
}) |
|
.sort(function(a, b) { |
|
return a.name.localeCompare(b.name); |
|
}); |
|
|
|
countries.forEach(function(d) { |
|
d.color = nextColor(); |
|
colorToFeature[d.color] = d; |
|
}); |
|
|
|
var overFeature, mouse; |
|
d3.selectAll('canvas') |
|
.on('mousemove', function() { |
|
mouse = d3.mouse(this); |
|
draw(); |
|
}); |
|
|
|
function draw() { |
|
path.context(c); |
|
c.clearRect(0, 0, width, height); |
|
c.fillStyle = "#bbb", c.beginPath(), path(land), c.fill(); |
|
c.fillStyle = "#f00", c.beginPath(), path(countries[i]), c.fill(); |
|
c.strokeStyle = "#fff", c.lineWidth = .5, c.beginPath(), path(borders), c.stroke(); |
|
c.strokeStyle = "#000", c.lineWidth = 2, c.beginPath(), path(globe), c.stroke(); |
|
|
|
if (mouse) { |
|
var color = c2.getImageData(mouse[0], mouse[1], 1, 1).data; |
|
var hex = '#' + hexFormat(color[0] << 16 | color[1] << 8 | color[2]); |
|
var feature = colorToFeature[hex]; |
|
if (feature != overFeature) { |
|
console.log('over:', hex, feature ? feature.name : 'none'); |
|
overFeature = feature; |
|
} |
|
} |
|
|
|
c2.clearRect(0, 0, width, height); |
|
countries.forEach(function(d) { |
|
if (d === overFeature) { |
|
c.strokeStyle = '#fff'; |
|
c.lineWidth = 1; |
|
drawFeature(c, d, '#6f0'); |
|
c.stroke(); |
|
} |
|
c2.strokeStyle = '#fff'; |
|
c2.lineWidth = 1; |
|
drawFeature(c2, d, d.color); |
|
c2.stroke(); |
|
}); |
|
} |
|
|
|
function drawFeature(ctx, d, fill) { |
|
ctx.fillStyle = fill || d.color; |
|
ctx.beginPath(); |
|
path.context(ctx)(d); |
|
ctx.fill(); |
|
} |
|
|
|
(function transition() { |
|
d3.transition() |
|
.duration(1250) |
|
.each("start", function() { |
|
title.text(countries[i = (i + 1) % n].name); |
|
}) |
|
.tween("rotate", function() { |
|
var p = d3.geo.centroid(countries[i]), |
|
r = d3.interpolate(projection.rotate(), [-p[0], -p[1]]); |
|
return function(t) { |
|
projection.rotate(r(t)); |
|
draw(); |
|
}; |
|
}) |
|
.transition() |
|
.each("end", transition); |
|
})(); |
|
} |
|
|
|
d3.select(self.frameElement).style("height", (height * 2) + "px"); |
|
|
|
</script> |