Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active February 9, 2016 01:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbostock/09dd5ad7d6bfd40187e0 to your computer and use it in GitHub Desktop.
Save mbostock/09dd5ad7d6bfd40187e0 to your computer and use it in GitHub Desktop.
Map Pan & Zoom III
license: gpl-3.0
.DS_Store
build
node_modules

This example uses d3.behavior.zoom with a canvas transform and pre-projected geometry for map panning and zooming. This approach is faster than reprojecting, but still slower than an SVG transform. A faster variation of this approach is to use dynamic simplification and viewport clipping.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
canvas {
background: #eee;
}
</style>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<body>
<script>
var width = 960,
height = 960;
var zoom = d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.scaleExtent([1, 8]);
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
var context = canvas.node().getContext("2d");
context.lineJoin = "round";
context.lineCap = "round";
context.strokeStyle = "#fff";
var path = d3.geo.path()
.projection(null)
.context(context);
d3.json("world.json", function(error, world) {
if (error) throw error;
var sphere = topojson.feature(world, world.objects.sphere),
land = topojson.feature(world, world.objects.land),
boundary = topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; });
canvas
.call(zoom.on("zoom", zoomed))
.call(zoom.event);
function zoomed() {
var t = zoom.translate(),
s = zoom.scale();
context.clearRect(0, 0, width, height);
context.save();
context.translate(t[0], t[1]);
context.scale(s, s);
context.lineWidth = 1 / s;
context.beginPath();
path(sphere);
context.fillStyle = "#fff";
context.fill();
context.beginPath();
path(land);
context.fillStyle = "#000";
context.fill();
context.beginPath();
path(boundary);
context.stroke();
context.restore();
}
});
d3.select(self.frameElement).style("height", height + "px");
</script>
GENERATED_FILES = \
world.json
all: $(GENERATED_FILES)
clean:
rm -rf -- %(GENERATED_FILES)
.PHONY: all clean
build/ne_50m_admin_0_countries.zip:
mkdir -p $(dir $@)
curl -o $@ 'http://www.nacis.org/naturalearth/50m/cultural/$(notdir $@)'
build/ne_50m_admin_0_countries.shp: build/ne_50m_admin_0_countries.zip
unzip -d $(dir $@) $<
touch $@
world.json: build/ne_50m_admin_0_countries.shp sphere.json
node_modules/.bin/topojson \
-q 1e5 \
--projection='width = 960, height = 960, d3.geo.mercator() \
.translate([width / 2, height / 2]) \
.scale((width - 1) / 2 / Math.PI)' \
-- \
countries=build/ne_50m_admin_0_countries.shp \
sphere=sphere.json | \
node_modules/.bin/topojson-merge \
-o $@ \
--io=countries \
--oo=land
{
"name": "anonymous",
"version": "0.0.1",
"private": true,
"dependencies": {
"topojson": "1"
}
}
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.
@Stonelinks
Copy link

Quick question: what is that sphere.json Makefile target? I'm trying to adapt this to work with the 10m map and it seems to be missing.

Also the URL for the naturalearth data is 404ing. Looks like this works though: http://naciscdn.org/naturalearth/50m/cultural/ne_50m_admin_0_countries.zip

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment