|
<!DOCTYPE html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<script src="//d3js.org/d3.v4.min.js"></script> |
|
<script src="//d3js.org/d3-geo-projection.v2.min.js"></script> |
|
<script src="//d3js.org/topojson.v1.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 { |
|
cursor: move; |
|
} |
|
#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> |
|
<select></select> |
|
|
|
<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 zoom = d3.zoom() |
|
.on("zoom", zoomed); |
|
|
|
|
|
var projectionLeft = d3.geoAitoff() |
|
.center(center) |
|
|
|
var projectionRight = d3.geoOrthographic() |
|
.center(center) |
|
.translate([map_width/5, map_height / 5]) |
|
.scale(scale1) |
|
.clipAngle(90) |
|
|
|
var pathLeft = d3.geoPath() |
|
.projection(projectionLeft); |
|
var pathRight = d3.geoPath() |
|
.projection(projectionRight); |
|
|
|
function zoomed() { |
|
projectionLeft |
|
.translate([d3.event.transform.x, d3.event.transform.y]) |
|
.scale(d3.event.transform.k) |
|
|
|
var newCenter = projectionLeft.invert([map_width/2,map_height/2]); |
|
projectionRight .rotate([-newCenter[0], -newCenter[1]]) |
|
|
|
update(); |
|
} |
|
|
|
function update() { |
|
d3.selectAll("#left path") |
|
.attr("d", pathLeft); |
|
d3.selectAll("#right path") |
|
.attr("d", pathRight); |
|
|
|
d3.selectAll("#left circle") |
|
.attr('transform', function(d,i) { |
|
return 'translate(' + [ d.x, d.y ] + ')'; |
|
}) |
|
|
|
d3.selectAll("#right circle") |
|
.attr('transform', function(d,i) { |
|
try { |
|
var latlon = projectionLeft.invert([d.x, d.y]) |
|
return 'translate(' + projectionRight(latlon) + ')'; |
|
} catch(e) { |
|
return 'translate(-100,-100)'; |
|
} |
|
}) |
|
} |
|
|
|
|
|
|
|
var graticule = d3.geoGraticule(); |
|
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(100, 25, 25, map_width - 50, map_height - 50); |
|
|
|
svgLeft.selectAll("circle") |
|
.data(points) |
|
.enter().append("circle") |
|
.attr('r', 3) |
|
|
|
svgRight.selectAll("circle") |
|
.data(points) |
|
.enter().append("circle") |
|
.attr('r', 2) |
|
|
|
svgLeft |
|
.call(zoom) |
|
.call( |
|
zoom.transform, |
|
d3.zoomIdentity.translate(map_width/2,map_height/2) |
|
.scale(scale0) |
|
) |
|
; |
|
}); |
|
|
|
var projections = { |
|
"Aitoff": d3.geoAitoff().scale(90), |
|
"Boggs Eumorphic": d3.geoBoggs().scale(90), |
|
"Craster Parabolic (Putnins P4)": d3.geoCraster().scale(90), |
|
"Cylindrical Equal-Area": d3.geoCylindricalEqualArea().scale(120), |
|
"Eckert I": d3.geoEckert1().scale(95), |
|
"Eckert III": d3.geoEckert3().scale(105), |
|
"Eckert IV": d3.geoEckert4().scale(105), |
|
"Eckert V": d3.geoEckert5().scale(100), |
|
"Equidistant Cylindrical (Plate Carrée)": d3.geoEquirectangular().scale(90), |
|
"Fahey": d3.geoFahey().scale(75), |
|
"Foucaut Sinusoidal": d3.geoFoucaut().scale(80), |
|
"Gall (Gall Stereographic)": d3.geoCylindricalStereographic().scale(70), |
|
"Ginzburg VIII (TsNIIGAiK 1944)": d3.geoGinzburg8().scale(75), |
|
"Kavraisky VII": d3.geoKavrayskiy7().scale(90), |
|
"Larrivée": d3.geoLarrivee().scale(55), |
|
"McBryde-Thomas Flat-Pole Sine (No. 2)": d3.geoMtFlatPolarSinusoidal().scale(95), |
|
"Mercator": d3.geoMercator().scale(50), |
|
"Miller Cylindrical I": d3.geoMiller().scale(60), |
|
"Mollweide": d3.geoMollweide().scale(100), |
|
"Natural Earth": d3.geoNaturalEarth().scale(100), |
|
"Nell-Hammer": d3.geoNellHammer().scale(120), |
|
"Quartic Authalic": d3.geoHammer().coefficient(Infinity).scale(95), |
|
"Robinson": d3.geoRobinson().scale(90), |
|
"Sinusoidal": d3.geoSinusoidal().scale(90), |
|
"van der Grinten (I)": d3.geoVanDerGrinten().scale(50), |
|
"Wagner VI": d3.geoWagner6().scale(90), |
|
"Wagner VII": d3.geoWagner7().scale(90), |
|
"Winkel Tripel": d3.geoWinkel3().scale(90), |
|
"Wiechel": d3.geoWiechel().scale(90) }; |
|
var selector = d3.select("select") |
|
selector.selectAll("option") |
|
.data(Object.keys(projections)) |
|
.enter().append("option") |
|
.attr('value', function(d) { return d }).text(function(d) { return d }) |
|
|
|
selector.on("change", function(d) { |
|
console.log("sup", d3.event) |
|
var proj = d3.event.target.selectedOptions[0].value; |
|
projectionLeft = projections[proj].center(center); |
|
pathLeft = d3.geoPath() |
|
.projection(projectionLeft); |
|
svgLeft |
|
.call(zoom) |
|
.call( |
|
zoom.transform, |
|
d3.zoomIdentity.translate(map_width/2,map_height/2) |
|
.scale(scale0) |
|
) |
|
; |
|
}) |
|
|
|
|
|
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> |