Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active March 2, 2019 09:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save mbostock/3709000 to your computer and use it in GitHub Desktop.
Save mbostock/3709000 to your computer and use it in GitHub Desktop.
Map Projection Distortions
license: gpl-3.0

A comparison of 41 map projections by four different types of distortion. Lower is better. Data transcribed from the Natural Earth Projection.

<!DOCTYPE html>
<meta charset="utf-8">
<title>Map Projections</title>
<style>
svg {
font: 10px sans-serif;
}
.background path {
fill: none;
stroke: none;
stroke-width: 20px;
pointer-events: stroke;
}
.foreground path {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.axis .title {
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.label {
-webkit-transition: fill 125ms linear;
}
.active .label:not(.inactive) {
font-weight: bold;
}
.label.inactive {
fill: #ccc;
}
.foreground path.inactive {
stroke: #ccc;
stroke-opacity: .5;
stroke-width: 1px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 40, bottom: 20, left: 200},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var dimensions = [
{
name: "name",
scale: d3.scale.ordinal().rangePoints([0, height]),
type: String
},
{
name: "Acc. 40º 150%",
scale: d3.scale.linear().range([0, height]),
type: Number
},
{
name: "Scale",
scale: d3.scale.linear().range([height, 0]),
type: Number
},
{
name: "Areal",
scale: d3.scale.sqrt().range([height, 0]),
type: Number
},
{
name: "Angular",
scale: d3.scale.linear().range([height, 0]),
type: Number
}
];
var x = d3.scale.ordinal()
.domain(dimensions.map(function(d) { return d.name; }))
.rangePoints([0, width]);
var line = d3.svg.line()
.defined(function(d) { return !isNaN(d[1]); });
var yAxis = d3.svg.axis()
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var dimension = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d.name) + ")"; });
d3.tsv("projections.tsv", function(error, data) {
if (error) throw error;
dimensions.forEach(function(dimension) {
dimension.scale.domain(dimension.type === Number
? d3.extent(data, function(d) { return +d[dimension.name]; })
: data.map(function(d) { return d[dimension.name]; }).sort());
});
svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("d", draw);
svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("d", draw);
dimension.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(yAxis.scale(d.scale)); })
.append("text")
.attr("class", "title")
.attr("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d.name; });
// Rebind the axis data to simplify mouseover.
svg.select(".axis").selectAll("text:not(.title)")
.attr("class", "label")
.data(data, function(d) { return d.name || d; });
var projection = svg.selectAll(".axis text,.background path,.foreground path")
.on("mouseover", mouseover)
.on("mouseout", mouseout);
function mouseover(d) {
svg.classed("active", true);
projection.classed("inactive", function(p) { return p !== d; });
projection.filter(function(p) { return p === d; }).each(moveToFront);
}
function mouseout(d) {
svg.classed("active", false);
projection.classed("inactive", false);
}
function moveToFront() {
this.parentNode.appendChild(this);
}
});
function draw(d) {
return line(dimensions.map(function(dimension) {
return [x(dimension.name), dimension.scale(d[dimension.name])];
}));
}
</script>
Acc. 40º 150% Scale Areal Angular name
87.7 0.29 0.37 18.25 Eckert III
87.5 0.25 0.19 20.54 Natural Earth
87.4 0.27 0.17 24.2 Winkel II
86.5 0.23 0.28 19.15 Kavraisky VII
85 0.26 0.18 23.28 Winkel Tripel
84.3 0.27 0.19 21.27 Robinson
83.2 0.25 0.43 16.14 Fahey
81.9 0.36 0 28.73 Eckert IV
81.8 0.26 0.24 22.31 Hölzel
80.4 0.26 0.34 20.41 Wagner VI
80 0.3 0.29 23.47 Eckert V
78.4 0.32 0.07 26.38 McBryde–Thomas Flat-Pole Sine (No. 2)
78.1 0.29 0.23 25.8 Winkel I
77.9 0.28 0.3 22.68 Wagner III
76.5 0.32 0.12 26.88 Wagner II
76.2 0.31 0.25 NaN Denoyer Semi-elliptical
75.2 0.3 0.31 24.31 Putnins P5'
74.3 0.29 0.57 16.84 Equidistant Cylindrical (Plate Carrée)
74.1 0.38 0 30.56 Kavraisky V
74 0.37 0 30.71 Wagner VII
72.4 0.39 0 31.54 Putnins P4'
71.9 0.57 0 30.9 Cylindrical Equal-Area
70.6 0.39 0 32.28 Mollweide
70.5 0.29 0.51 20.36 Ginzburg VIII (TsNIIGAiK 1944)
70.2 0.43 0 30.9 Nell–Hammer
69.1 0.39 0.1 30.79 Putnins P1
68.1 0.36 0.23 30.17 Aitoff
68.1 0.36 0.29 30.3 Eckert I
67 0.44 0 33.33 Foucaut Sinusoidal
63.7 0.44 0 35.2 Boggs Eumorphic
63 0.45 0 35.5 Eckert–Greifendorff
62.9 0.39 1.3 7.63 Miller Cylindrical I
62.4 0.47 0 36 Quartic Authalic
61.7 0.35 1.05 10.06 Larrivée
60.3 0.48 0 36.99 Craster Parabolic (Putnins P4)
58.9 0.46 0 36.39 Goode Homolosine
57.4 0.41 1.46 7.75 van der Grinten (I)
57.4 0.57 4.79 0 Mercator
57.1 0.51 0 39.01 Sinusoidal
49 0.3 0.73 10.62 Gall (Gall Stereographic)
42.2 0.74 0 52.39 Foucaut
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment