Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active September 11, 2018 10:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Fil/2e885160bf2b4d690e976db0f9de1557 to your computer and use it in GitHub Desktop.
Save Fil/2e885160bf2b4d690e976db0f9de1557 to your computer and use it in GitHub Desktop.
visualizing map distortion d3v4
license: mit
<!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>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment