Skip to content

Instantly share code, notes, and snippets.

@jasondavies
Forked from jasondavies/README.md
Created September 17, 2012 18:54
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 jasondavies/3739100 to your computer and use it in GitHub Desktop.
Save jasondavies/3739100 to your computer and use it in GitHub Desktop.
Interrupted Projection Transitions
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
height: 500px;
position: relative;
width: 960px;
font-family: sans-serif;
}
#projection-menu {
position: absolute;
right: 10px;
top: 10px;
}
#gores {
position: absolute;
left: 10px;
top: 10px;
}
.background {
fill: #a4bac7;
}
.foreground {
fill: none;
stroke: #333;
stroke-width: 1.5px;
}
.graticule {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.graticule:nth-child(2n) {
stroke-dasharray: 2,2;
}
.land {
fill: #d7c7ad;
stroke: #766951;
}
.boundary {
fill: none;
stroke: #a5967e;
}
</style>
<body>
<select id="projection-menu"></select>
<input type="range" id="gores" step="1" min="1" max="12" value="3">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://raw.github.com/d3/d3-plugins/master/geo/projection/projection.js"></script>
<script>
var π = Math.PI;
var width = 960,
height = 500;
var projection;
var interrupt, x = [], y;
var path = d3.geo.path();
function goreProjection() {
return d3.geo.projection(function(λ, φ) {
var coordinates = [λ *= 180 / π, φ *= 180 / π];
for (var i = 1; i < x.length; i++) {
if (λ < x[i] + 1e-6) {
coordinates = projection([λ - (x[i - 1] + x[i]) / 2, φ]);
coordinates[0] += (y[i - 1][0] + y[i][0]) / 2;
coordinates[1] = -coordinates[1];
break;
}
}
return coordinates;
}).scale(1).precision(0);
}
var graticule = d3.geo.graticule();
function lobes(n) {
var dλ = (180 - -180) / n;
return {
type: "Polygon",
coordinates: [hemilobes(n, -180, 180, 0, 90).concat(hemilobes(n, 180, -180, 0, -90))].map(resample),
properties: {interruptions: d3.range(-180, 180 + dλ / 2, dλ)}
};
}
function hemilobes(n, λ0, λ1, φ0, φ1) {
var d = [],
dλ = (λ1 - λ0) / n,
δ = dλ / 1e6;
λ0 += δ;
for (var i = 0; i < n; i++) {
d.push([λ0, φ0], [λ0, φ1], [λ0 += dλ - 2 * δ, φ1], [λ0, φ0]);
λ0 += 2 * δ;
}
return d;
}
function resample(coordinates) {
var n = coordinates.length;
if (!n) return coordinates;
var samples = 50,
a = coordinates[0],
b,
resampled = [];
for (var i = 0; ++i < n;) {
b = coordinates[i];
dx = (b[0] - a[0]) / samples;
dy = (b[1] - a[1]) / samples;
resampled.push(a);
for (var j = 1; j < samples; j++) {
resampled.push([dx ? a[0] + j * dx : a[0], dy ? a[1] + j * dy : a[1]]);
}
a = b;
}
if (b) resampled.push(b);
return resampled;
}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var clipPath = svg.append("defs").append("clipPath")
.attr("id", "clip").append("path");
var background = svg.append("path")
.attr("class", "background");
var graticules = svg.selectAll(".graticule")
.data(graticule.lines)
.enter().append("path")
.attr("class", "graticule");
var foreground = svg.append("path")
.attr("class", "foreground");
d3.select("#gores")
.on("change", function() {
gores();
svg.selectAll("path").attr("d", path);
});
function gores() {
var n = +d3.select("#gores").property("value");
d3.select("#gores-output").property("value", n);
interrupt = lobes(n);
x = interrupt.properties.interruptions;
y = x.map(function(x) { return projection([x, 0]); });
path.projection(goreProjection());
clipPath.datum(interrupt);
background.datum(interrupt);
foreground.datum(interrupt);
}
d3.json("/d/3682676/readme-boundaries.json", function(err, collection) {
svg.insert("g", ".graticule")
.attr("class", "boundary")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(collection.geometries)
.enter().append("path")
.attr("d", path);
});
d3.json("/d/3682676/readme-land.json", function(err, collection) {
svg.insert("g", ".graticule,.boundary")
.attr("class", "land")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(collection.geometries)
.enter().append("path")
.attr("d", path);
});
var options = [
{name: "Aitoff", projection: d3.geo.aitoff()},
// {name: "Albers", projection: d3.geo.albers().scale(145).parallels([20, 50]).origin([0, 0]).translate([width * .500, height * .750])},
// {name: "Bonne", projection: d3.geo.bonne().scale(120).translate([width * .500, height * .425])},
// {name: "Collignon", projection: d3.geo.collignon().scale(93)},
{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: "Equirectangular (Plate Carrée)", projection: d3.geo.equirectangular().scale(950)},
// {name: "Hammer", projection: d3.geo.hammer().scale(165)},
{name: "Goode Homolosine", projection: d3.geo.homolosine()},
{name: "Kavrayskiy VII", projection: d3.geo.kavrayskiy7()},
// {name: "Lambert cylindrical equal-area", projection: d3.geo.cylindricalEqualArea()},
{name: "Larrivée", projection: d3.geo.larrivee().scale(95)},
// {name: "Mercator", projection: d3.geo.mercator().scale(490)},
// {name: "Miller", projection: d3.geo.miller().scale(100)},
{name: "Mollweide", projection: d3.geo.mollweide().scale(165)},
{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: "van der Grinten", projection: d3.geo.vanDerGrinten().scale(75)},
{name: "Wagner VI", projection: d3.geo.wagner6()},
{name: "Winkel Tripel", projection: d3.geo.winkel3()}
];
var interval = setInterval(loop, 1500),
i = 0,
n = options.length - 1;
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) {
projection = option.projection;
gores();
svg.selectAll("path").transition()
.duration(750)
.attr("d", path);
}
loop();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment