Skip to content

Instantly share code, notes, and snippets.

@r-suen
Last active August 28, 2017 18:03
Show Gist options
  • Save r-suen/7c85e202502bde0c18a805c218c82bf9 to your computer and use it in GitHub Desktop.
Save r-suen/7c85e202502bde0c18a805c218c82bf9 to your computer and use it in GitHub Desktop.
World Map Wrap

World map wrap with constrained zoom using D3. The constraints prevent the map from moving off screen.

Drag left or right to see the wrap effect. Drag up or down to see the constraint up to the ±85th latitudes. Zoom in and out to see the map constrained to the viewport bounds.

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>World Map Wrap</title>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.0/topojson.min.js'></script>
</head>
<style>
body {
margin: 0;
overflow: hidden;
}
rect {
fill: #5f9ea0;
}
.land {
fill: #d2b48c;
}
.border {
fill: none;
stroke: #654321;
stroke-width: 1;
}
</style>
<body>
<div id='map'></div>
<script>
var dlambda = 0,
zoomtx = 0;
var width = window.innerWidth,
height = window.innerHeight;
var scale = (width > height) ? width : height;
var projection = d3.geoMercator()
.translate([0, 0])
.scale(scale / 2 / Math.PI);
var zoom = d3.zoom()
.scaleExtent([1, 1 << 7])
.on('zoom', zoomed);
var path = d3.geoPath(projection);
var lambda = d3.scaleLinear()
.domain([0, width])
.range([-180, 180]);
var ybound = projection([0, -85])[1] - height / 2;
var svg = d3.select('#map').append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
.call(zoom);
svg.append('rect')
.attr('x', -width / 2)
.attr('y', -height / 2)
.attr('width', width)
.attr('height', height);
var g = svg.append('g');
d3.json('world-110m.json', function(error, data) {
if (error) throw error;
g.append('path')
.datum(topojson.feature(data, data.objects.land))
.attr('d', path)
.attr('class', 'land');
g.append('path')
.datum(topojson.mesh(data, data.objects.countries, function (a, b) { return a !== b; }))
.attr('d', path)
.attr('class', 'border');
});
function zoomed() {
let t = d3.event.transform;
t.y = Math.min(height / 2 * (t.k - 1) + ybound * t.k, Math.max(t.y, height / 2 * (1 - t.k) - ybound * t.k));
if (d3.event.sourceEvent.wheelDelta) {
t.x = Math.min(width / 2 * (t.k - 1), Math.max(t.x, width / 2 * (1 - t.k)));
zoomtx = t.x;
g.attr("transform", t);
svg.selectAll('path').style('stroke-width', 1 / t.k);
} else if (d3.event.sourceEvent.movementX || d3.event.sourceEvent.movementY) {
t.x = zoomtx;
dlambda += d3.event.sourceEvent.movementX / t.k;
projection.rotate([lambda(-width / 2 + dlambda), 0]);
svg.selectAll('path').attr('d', path);
g.attr("transform", t);
}
}
</script>
</body>
</html>
Display the source blob
Display the rendered blob
Raw
Loading
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