|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
.country { |
|
fill: #b8b8b8; |
|
stroke: #fff; |
|
stroke-width: .5px; |
|
stroke-linejoin: round; |
|
} |
|
|
|
.graticule { |
|
fill: none; |
|
stroke: #000; |
|
stroke-opacity: .3; |
|
stroke-width: .5px; |
|
} |
|
|
|
.graticule-outline { |
|
fill: none; |
|
stroke: #333; |
|
stroke-width: 1.5px; |
|
} |
|
|
|
text { |
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
font-size: 18px; |
|
font-weight: bold; |
|
text-anchor: middle; |
|
} |
|
|
|
.arc { |
|
stroke: #000; |
|
fill: none; |
|
} |
|
|
|
.point { |
|
fill: #000; |
|
} |
|
|
|
</style> |
|
<body> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script src="http://d3js.org/topojson.v0.min.js"></script> |
|
<script> |
|
|
|
var width = 960, |
|
height = 500; |
|
|
|
var centroid = d3.geo.path() |
|
.projection(function(d) { return d; }) |
|
.centroid; |
|
|
|
var projection = d3.geo.orthographic() |
|
.scale(248) |
|
.clipAngle(90); |
|
|
|
var path = d3.geo.path() |
|
.pointRadius(1.5) |
|
.projection(projection); |
|
|
|
var graticule = d3.geo.graticule() |
|
.extent([[-180, -90], [180 - .1, 90 - .1]]); |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
var line = svg.append("path") |
|
.datum(graticule) |
|
.attr("class", "graticule") |
|
.attr("d", path); |
|
|
|
svg.append("circle") |
|
.attr("class", "graticule-outline") |
|
.attr("cx", width / 2) |
|
.attr("cy", height / 2) |
|
.attr("r", projection.scale()); |
|
|
|
var title = svg.append("text") |
|
.attr("x", width / 2) |
|
.attr("y", height * 3 / 5); |
|
|
|
var rotate = d3_geo_greatArcInterpolator(); |
|
|
|
d3.json("readme-world-110m.json", function(error, world) { |
|
var countries = topojson.object(world, world.objects.countries).geometries, |
|
i = -1, |
|
n = countries.length; |
|
|
|
var country = svg.selectAll(".country") |
|
.data(countries) |
|
.enter().insert("path", ".graticule") |
|
.attr("class", "country") |
|
.attr("d", path); |
|
|
|
var au0, au1; |
|
countries.forEach(function(d, i) { |
|
if (d.id === "Australia") au0 = i; |
|
if (d.id === "Austria") au1 = i; |
|
}); |
|
|
|
var arc = d3.geo.greatArc() |
|
.precision(1) |
|
.source(centroid(countries[au0])) |
|
.target(centroid(countries[au1])); |
|
|
|
var arcPoints = svg.selectAll("path.point") |
|
.data(arc().coordinates.map(function(d) { return {type: "Point", coordinates: d}; })) |
|
.enter().append("path") |
|
.attr("class", "point") |
|
.attr("d", path); |
|
|
|
step(); |
|
|
|
function step() { |
|
//if (++i >= n) i = 0; |
|
i = i === au0 ? au1 : au0; |
|
|
|
title.text(countries[i].id); |
|
|
|
country.transition() |
|
.style("fill", function(d, j) { return j === i ? "red" : "#b8b8b8"; }); |
|
|
|
d3.transition() |
|
.delay(250) |
|
.duration(1250) |
|
.tween("rotate", function() { |
|
var point = centroid(countries[i]); |
|
rotate.source(projection.rotate()).target([-point[0], -point[1]]).distance(); |
|
return function(t) { |
|
projection.rotate(rotate(t)); |
|
arcPoints.attr("d", path); |
|
country.attr("d", path); |
|
line.attr("d", path); |
|
}; |
|
}) |
|
.transition() |
|
.each("end", step); |
|
} |
|
}); |
|
|
|
var d3_radians = Math.PI / 180; |
|
|
|
function d3_geo_greatArcInterpolator() { |
|
var x0, y0, cy0, sy0, kx0, ky0, |
|
x1, y1, cy1, sy1, kx1, ky1, |
|
d, |
|
k; |
|
|
|
function interpolate(t) { |
|
var B = Math.sin(t *= d) * k, |
|
A = Math.sin(d - t) * k, |
|
x = A * kx0 + B * kx1, |
|
y = A * ky0 + B * ky1, |
|
z = A * sy0 + B * sy1; |
|
return [ |
|
Math.atan2(y, x) / d3_radians, |
|
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians |
|
]; |
|
} |
|
|
|
interpolate.distance = function() { |
|
if (d == null) k = 1 / Math.sin(d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0))))); |
|
return d; |
|
}; |
|
|
|
interpolate.source = function(_) { |
|
var cx0 = Math.cos(x0 = _[0] * d3_radians), |
|
sx0 = Math.sin(x0); |
|
cy0 = Math.cos(y0 = _[1] * d3_radians); |
|
sy0 = Math.sin(y0); |
|
kx0 = cy0 * cx0; |
|
ky0 = cy0 * sx0; |
|
d = null; |
|
return interpolate; |
|
}; |
|
|
|
interpolate.target = function(_) { |
|
var cx1 = Math.cos(x1 = _[0] * d3_radians), |
|
sx1 = Math.sin(x1); |
|
cy1 = Math.cos(y1 = _[1] * d3_radians); |
|
sy1 = Math.sin(y1); |
|
kx1 = cy1 * cx1; |
|
ky1 = cy1 * sx1; |
|
d = null; |
|
return interpolate; |
|
}; |
|
|
|
return interpolate; |
|
} |
|
|
|
</script> |