Admiring Jason Davies' work on the Weichel projection
forked from enjalot's block: visualizing map distortion
Admiring Jason Davies' work on the Weichel projection
forked from enjalot's block: visualizing map distortion
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-geo-projection/0.2.15/d3.geo.projection.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script> | |
<style> | |
svg { | |
margin: 22px; | |
} | |
select { | |
margin-left: 20px; | |
} | |
path.foreground { | |
fill: none; | |
stroke: #333; | |
stroke-width: 1.5px; | |
} | |
path.graticule { | |
fill: none; | |
stroke: #aaa; | |
stroke-width: .5px; | |
} | |
#left { | |
} | |
#left .land { | |
fill: #d7c7ad; | |
stroke: #a5967e; | |
} | |
#right .land { | |
fill: #cfcece; | |
stroke: #a5967e; | |
} | |
#left circle { | |
fill: #d8355e; | |
} | |
#right circle { | |
stroke: #d8355e; | |
fill: none; | |
} | |
</style> | |
</head> | |
<body> | |
<svg id="left"></svg> | |
<svg id="right"></svg> | |
<script> | |
var map_width = 400; | |
var map_height = 400; | |
var center = [-90, 37]; | |
var scale0 = (map_width - 1) / 2 / Math.PI * 6 | |
var scale1 = (map_width - 1) / 2 / Math.PI * 3 | |
var projectionLeft = d3.geo.wiechel() | |
//.center(center) | |
.translate([map_width/2, map_height/2]) | |
.rotate([0, -90]) | |
.clipAngle(180 - 1) | |
.precision(.1) | |
.scale(90) | |
.rotate([100, -90]) | |
var projectionRight = d3.geo.orthographic() | |
.center(center) | |
.translate([map_width/5, map_height / 5]) | |
.scale(scale1) | |
.clipAngle(90) | |
var pathLeft = d3.geo.path() | |
.projection(projectionLeft); | |
var pathRight = d3.geo.path() | |
.projection(projectionRight); | |
function zoomed() { | |
var newCenter = projectionLeft.invert([map_width/2,map_height/2]); | |
projectionRight | |
.rotate([-newCenter[0], -newCenter[1]]) | |
update(); | |
} | |
var start = Date.now(); | |
var duration = 20; | |
var pause = false; | |
var dir = 1; | |
d3.timer(function(elapsed) { | |
var now = Date.now(); | |
if(now - start > duration) { | |
start = now; | |
} else { | |
return false; | |
} | |
var dt = elapsed * 0.00005 % 2*Math.PI | |
//var deg = dt * 180/Math.PI * 0.1; | |
//console.log(deg) | |
var deg = 3 | |
var rot = projectionLeft.rotate() | |
rot[0] = (rot[0] + deg)// % 360 | |
rot[1] = (rot[1] - deg)// % 360 | |
var scale = projectionLeft.scale(); | |
if(scale > 200 && dir === 1) { | |
dir = -1; | |
} else if(scale < 80 && dir === -1) { | |
dir = 1; | |
} | |
if(dir === -1) { | |
scale -= 5 | |
} else { | |
scale += 5; | |
} | |
projectionLeft.scale(scale) | |
//console.log(rot) | |
projectionLeft.rotate(rot) | |
zoomed() | |
return pause; | |
}) | |
d3.select("body").on("click", function() { | |
pause = true; | |
}) | |
function update() { | |
d3.selectAll("#left path") | |
.attr("d", pathLeft); | |
d3.selectAll("#right path") | |
.attr("d", pathRight); | |
d3.selectAll("#left circle") | |
.attr({ | |
cx: function(d,i) { return d.x }, | |
cy: function(d,i) { return d.y } | |
}) | |
d3.selectAll("#right circle") | |
.attr({ | |
cx: function(d,i) { | |
var latlon = projectionLeft.invert([d.x, d.y]) | |
return projectionRight(latlon)[0] | |
}, | |
cy: function(d,i) { | |
var latlon = projectionLeft.invert([d.x, d.y]) | |
return projectionRight(latlon)[1] | |
} | |
}) | |
} | |
var graticule = d3.geo.graticule(); | |
var svgLeft = d3.select("#left") | |
.attr("width", map_width) | |
.attr("height", map_height); | |
var svgRight = d3.select("#right") | |
.attr("width", map_width + 40) | |
.attr("height", map_height); | |
svgLeft.append("path") | |
.datum(graticule) | |
.attr("class", "graticule") | |
.attr("d", pathLeft); | |
svgRight.append("path") | |
.datum(graticule) | |
.attr("class", "graticule") | |
.attr("d", pathRight); | |
d3.json("world-110m.json", function(error,world) { | |
if (error) throw error; | |
svgLeft.insert("path", ".graticule") | |
.datum(topojson.feature(world, world.objects.land)) | |
.attr("class", "land") | |
.attr("d", pathLeft); | |
svgRight.insert("path", ".graticule") | |
.datum(topojson.feature(world, world.objects.land)) | |
.attr("class", "land") | |
.attr("d", pathRight); | |
var points = generateRect(500, 75, 75, map_width - 150, map_height - 150); | |
svgLeft.selectAll("circle") | |
.data(points) | |
.enter().append("circle") | |
.attr({ | |
r: 3 | |
}) | |
svgRight.selectAll("circle") | |
.data(points) | |
.enter().append("circle") | |
.attr({ | |
r: 2 | |
}) | |
zoomed(); | |
}); | |
var projections = { | |
"Aitoff": d3.geo.aitoff().scale(90), | |
"Boggs Eumorphic": d3.geo.boggs().scale(90), | |
"Craster Parabolic (Putnins P4)": d3.geo.craster().scale(90), | |
"Cylindrical Equal-Area": d3.geo.cylindricalEqualArea().scale(120), | |
"Eckert I": d3.geo.eckert1().scale(95), | |
"Eckert III": d3.geo.eckert3().scale(105), | |
"Eckert IV": d3.geo.eckert4().scale(105), | |
"Eckert V": d3.geo.eckert5().scale(100), | |
"Equidistant Cylindrical (Plate Carrée)": d3.geo.equirectangular().scale(90), | |
"Fahey": d3.geo.fahey().scale(75), | |
"Foucaut Sinusoidal": d3.geo.foucaut().scale(80), | |
"Gall (Gall Stereographic)": d3.geo.cylindricalStereographic().scale(70), | |
"Ginzburg VIII (TsNIIGAiK 1944)": d3.geo.ginzburg8().scale(75), | |
"Kavraisky VII": d3.geo.kavrayskiy7().scale(90), | |
"Larrivée": d3.geo.larrivee().scale(55), | |
"McBryde-Thomas Flat-Pole Sine (No. 2)": d3.geo.mtFlatPolarSinusoidal().scale(95), | |
"Mercator": d3.geo.mercator().scale(50), | |
"Miller Cylindrical I": d3.geo.miller().scale(60), | |
"Mollweide": d3.geo.mollweide().scale(100), | |
"Natural Earth": d3.geo.naturalEarth().scale(100), | |
"Nell-Hammer": d3.geo.nellHammer().scale(120), | |
"Quartic Authalic": d3.geo.hammer().coefficient(Infinity).scale(95), | |
"Robinson": d3.geo.robinson().scale(90), | |
"Sinusoidal": d3.geo.sinusoidal().scale(90), | |
"van der Grinten (I)": d3.geo.vanDerGrinten().scale(50), | |
"Wagner VI": d3.geo.wagner6().scale(90), | |
"Wagner VII": d3.geo.wagner7().scale(90), | |
"Winkel Tripel": d3.geo.winkel3().scale(90), | |
"Wiechel": d3.geo.wiechel().scale(90) | |
}; | |
function generateRect(num, x, y, width, height) { | |
var points = [] | |
var sideNum = Math.floor(num/4) + 1; | |
// top | |
d3.range(sideNum).forEach(function(i) { | |
points.push({ x: x + i * width/sideNum, y: y }) | |
}) | |
// right | |
d3.range(sideNum).forEach(function(i) { | |
points.push({ x: x + width, y: y + i * height/sideNum }) | |
}) | |
// bottom | |
d3.range(sideNum).forEach(function(i) { | |
points.push({ x: x + width - i * width/sideNum, y: y + height }) | |
}) | |
// left | |
d3.range(sideNum).forEach(function(i) { | |
points.push({ x: x, y: y + height - i * height/sideNum }) | |
}) | |
return points; | |
} | |
</script> | |
</body> | |
<script> | |
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | |
ga('create', 'UA-67666917-1', 'auto'); | |
ga('send', 'pageview'); | |
</script> |