D3 v4/5 version of mbostock's Map Pan & Zoom.
Demonstrates map panning and zooming using SVG transforms to avoid the overhead of reprojecting at every zoom iteration.
D3 v4/5 version of mbostock's Map Pan & Zoom.
Demonstrates map panning and zooming using SVG transforms to avoid the overhead of reprojecting at every zoom iteration.
<head> | |
<style> | |
body { | |
margin: 0 | |
} | |
svg { | |
background: #eee; | |
} | |
.sphere { | |
fill: #fff; | |
} | |
.land { | |
fill: #000; | |
} | |
.boundary { | |
fill: none; | |
stroke: #fff; | |
stroke-linejoin: round; | |
stroke-linecap: round; | |
vector-effect: non-scaling-stroke; | |
} | |
</style> | |
<script src="//d3js.org/d3.v5.min.js"></script> | |
<script src="//unpkg.com/topojson@3"></script> | |
</head> | |
<body> | |
<script> | |
const width = window.innerWidth; | |
const height = window.innerHeight; | |
const projection = d3.geoMercator() | |
.translate([width / 2, height / 2]) | |
.scale((width - 1) / 2 / Math.PI); | |
const path = d3.geoPath() | |
.projection(projection); | |
const zoom = d3.zoom() | |
.scaleExtent([1, 8]) | |
.on('zoom', zoomed); | |
const svg = d3.select('body').append('svg') | |
.attr('width', width) | |
.attr('height', height); | |
const g = svg.append('g'); | |
svg.call(zoom); | |
d3.json('//unpkg.com/world-atlas@1/world/110m.json') | |
.then(world => { | |
g.append('path') | |
.datum({ type: 'Sphere' }) | |
.attr('class', 'sphere') | |
.attr('d', path); | |
g.append('path') | |
.datum(topojson.merge(world, world.objects.countries.geometries)) | |
.attr('class', 'land') | |
.attr('d', path); | |
g.append('path') | |
.datum(topojson.mesh(world, world.objects.countries, (a, b) => a !== b)) | |
.attr('class', 'boundary') | |
.attr('d', path); | |
}); | |
function zoomed() { | |
g | |
.selectAll('path') // To prevent stroke width from scaling | |
.attr('transform', d3.event.transform); | |
} | |
</script> |