Skip to content

Instantly share code, notes, and snippets.

@madelfio
Forked from mbostock/.block
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save madelfio/91f2699043280ccb2dac to your computer and use it in GitHub Desktop.
Save madelfio/91f2699043280ccb2dac to your computer and use it in GitHub Desktop.
Measuring Country Size by Projected Area
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #fcfcfa;
height: 500px;
position: relative;
width: 960px;
}
#projection-menu {
position: absolute;
right: 10px;
top: 10px;
}
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #fff;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.land {
fill: #222;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.details {
text-align: center;
max-height: 250px;
overflow: auto;
}
.details span {
display: inline-block;
margin: 0px 5px;
text-align: right;
}
.details .rank {
width: 40px;
}
.details .country {
text-align: left;
width: 300px;
}
.details .size {
width: 60px;
}
.details .pct {
width: 80px;
}
</style>
<select id="projection-menu"></select>
<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/d3.geo.projection.v0.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500;
var options = [
{name: "Aitoff", projection: d3.geo.aitoff()},
{name: "Albers", projection: d3.geo.albers().scale(145).parallels([20, 50])},
{name: "August", projection: d3.geo.august().scale(60)},
{name: "Baker", projection: d3.geo.baker().scale(100)},
{name: "Boggs", projection: d3.geo.boggs()},
{name: "Bonne", projection: d3.geo.bonne().scale(120)},
{name: "Bromley", projection: d3.geo.bromley()},
{name: "Collignon", projection: d3.geo.collignon().scale(93)},
{name: "Craster Parabolic", projection: d3.geo.craster()},
{name: "Eckert I", projection: d3.geo.eckert1().scale(165)},
{name: "Eckert II", projection: d3.geo.eckert2().scale(165)},
{name: "Eckert III", projection: d3.geo.eckert3().scale(180)},
{name: "Eckert IV", projection: d3.geo.eckert4().scale(180)},
{name: "Eckert V", projection: d3.geo.eckert5().scale(170)},
{name: "Eckert VI", projection: d3.geo.eckert6().scale(170)},
{name: "Eisenlohr", projection: d3.geo.eisenlohr().scale(60)},
{name: "Equirectangular (Plate Carrée)", projection: d3.geo.equirectangular()},
{name: "Hammer", projection: d3.geo.hammer().scale(165)},
{name: "Hill", projection: d3.geo.hill()},
{name: "Goode Homolosine", projection: d3.geo.homolosine()},
{name: "Kavrayskiy VII", projection: d3.geo.kavrayskiy7()},
{name: "Lambert cylindrical equal-area", projection: d3.geo.cylindricalEqualArea()},
{name: "Lagrange", projection: d3.geo.lagrange().scale(120)},
{name: "Larrivée", projection: d3.geo.larrivee().scale(95)},
{name: "Laskowski", projection: d3.geo.laskowski().scale(120)},
{name: "Loximuthal", projection: d3.geo.loximuthal()},
{name: "Mercator", projection: d3.geo.mercator().scale(490 / 2 / Math.PI)},
{name: "Miller", projection: d3.geo.miller().scale(100)},
{name: "McBryde–Thomas Flat-Polar Parabolic", projection: d3.geo.mtFlatPolarParabolic()},
{name: "McBryde–Thomas Flat-Polar Quartic", projection: d3.geo.mtFlatPolarQuartic()},
{name: "McBryde–Thomas Flat-Polar Sinusoidal", projection: d3.geo.mtFlatPolarSinusoidal()},
{name: "Mollweide", projection: d3.geo.mollweide().scale(165)},
{name: "Natural Earth", projection: d3.geo.naturalEarth()},
{name: "Nell–Hammer", projection: d3.geo.nellHammer()},
{name: "Polyconic", projection: d3.geo.polyconic().scale(100)},
{name: "Robinson", projection: d3.geo.robinson()},
{name: "Sinusoidal", projection: d3.geo.sinusoidal()},
{name: "Sinu-Mollweide", projection: d3.geo.sinuMollweide()},
{name: "van der Grinten", projection: d3.geo.vanDerGrinten().scale(75)},
{name: "van der Grinten IV", projection: d3.geo.vanDerGrinten4().scale(120)},
{name: "Wagner IV", projection: d3.geo.wagner4()},
{name: "Wagner VI", projection: d3.geo.wagner6()},
{name: "Wagner VII", projection: d3.geo.wagner7()},
{name: "Winkel Tripel", projection: d3.geo.winkel3()}
];
options.forEach(function(o) {
if (o.name !== 'Mercator') {
o.projection.rotate([0, 0]).center([0, 0]);
}
});
var interval = setInterval(loop, 2000),
i = 0,
n = options.length - 1;
var projection = options[i].projection;
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
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);
var details = d3.select("body").append("div")
.attr("class", "details");
var header = details.append('div').attr('class', 'header');
header.append('span')
.attr('class', 'rank')
.text('');
header.append('span')
.attr('class', 'country')
.text('country');
header.append('span')
.attr('class', 'size')
.text('pixels');
header.append('span')
.attr('class', 'pct')
.text('percent');
queue()
.defer(d3.json, "/d/4090846/world-110m.json")
.defer(d3.tsv, "/d/4090846/world-country-names.tsv")
.await(ready);
function ready (error, world, names) {
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.insert('path', '.graticule')
.datum(topojson.mesh(world, world.objects.countries, function(a, b) {return a !== b;}))
.attr('class', 'boundary')
.attr('d', path);
names.forEach(function(n) {
n.geom = world.objects.countries.geometries.filter(function(g) {
return +g.id == +n.id;
})[0];
n.area = 0;
});
names = names.filter(function(n) {
return n.geom;
});
var areas = details.selectAll('div.area')
.data(names);
var new_areas = areas.enter()
.append('div')
.attr('class', 'area');
new_areas.append('span')
.attr('class', 'rank')
.text(function(d, i) {return i+1;});
new_areas.append('span')
.attr('class', 'country')
.text(function(d) {return d.name;});
new_areas.append('span')
.attr('class', 'size')
.text("0");
new_areas.append('span')
.attr('class', 'pct')
.text("0.0");
window.world = world;
window.names = names;
update(options[i]);
}
var menu = d3.select("#projection-menu")
.on("change", change);
menu.selectAll("option")
.data(options)
.enter().append("option")
.text(function(d) { return d.name; });
function loop() {
var j = Math.floor(Math.random() * n);
menu.property("selectedIndex", i = j + (j >= i));
update(options[i]);
}
function change() {
clearInterval(interval);
update(options[this.selectedIndex]);
}
function update(option) {
svg.selectAll("path").transition()
.duration(750)
.attrTween("d", projectionTween(projection, projection = option.projection));
var total_area = 0;
names.forEach(function(n) {
total_area += (n.area = computeArea(n.geom, projection));
});
details.selectAll('.area .size')
.transition()
.tween('text', function(d) {
var i = d3.interpolate(this.textContent, d.area);
return function(t) {this.textContent = i(t).toFixed(0);}
});
details.selectAll('.area .pct')
.transition()
.tween('text', function(d) {
var i = d3.interpolate(this.textContent, 100*d.area/total_area);
return function(t) {this.textContent = i(t).toFixed(3);}
});
}
function projectionTween(projection0, projection1) {
return function(d) {
var t = 0;
var projection = d3.geo.projection(project)
.scale(1)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
function project(λ, φ) {
λ *= 180 / Math.PI, φ *= 180 / Math.PI;
var p0 = projection0([λ, φ]), p1 = projection1([λ, φ]);
if (t == 0) {return [p0[0], -p0[1]];}
if (t == 1) {return [p1[0], -p1[1]];}
return [(1 - t) * p0[0] + t * p1[0], (1 - t) * -p0[1] + t * -p1[1]];
}
return function(_) {
t = _;
return path(d);
};
};
}
function computeArea(geometry, projection) {
if (!geometry) {return 0;}
var path = d3.geo.path()
.projection(projection);
return path.area(topojson.feature(world, geometry));
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment