Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save adg29/fd53a826b4b83ebc5c1e to your computer and use it in GitHub Desktop.
Save adg29/fd53a826b4b83ebc5c1e to your computer and use it in GitHub Desktop.
Programmatic Zoom In + Out to Topojson Bounding Box
<!DOCTYPE html>
<meta charset="utf-8">
<title>
Programmatic Zoom In + Out to Topojson Bounding Box
</title>
<style>
.background {
fill: none;
pointer-events: all;
}
.feature {
fill: #ccc;
cursor: pointer;
}
.feature.active {
fill: orange;
}
.mesh {
fill: none;
stroke: #fff;
stroke-linecap: round;
stroke-linejoin: round;
}
button{
display: block;
font-size: 1.3em;
}
</style>
<body>
<div class="buttons">
<button id="zoom_in" data-zoom="+1">Zoom In</button>
<button id="zoom_out" data-zoom="-1">Zoom Out</button>
<h1 id="current">United States National View</h1>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500,
active = d3.select(null);
var projection = d3.geo.albersUsa()
.scale(1000)
.translate([width / 2, height / 2]);
var zoom = d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.scaleExtent([1, 8])
.on("zoom", zoomed);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.on("click", stopped, true);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", reset);
var g = svg.append("g");
svg
.call(zoom) // delete this line to disable free zooming
.call(zoom.event);
d3.selectAll("button[data-zoom]")
.on("click", stopped, true)
.on("click", zoomButton);
function interpolateZoom (translate, scale) {
var self = this;
return d3.transition()
.duration(350)
.tween("zoom", function () {
var iTranslate =
d3.interpolate(zoom.translate(), translate),
iScale =
d3.interpolate(zoom.scale(), scale);
return function (t) {
/* zoom
.scale(iScale(t))
.translate(iTranslate(t));*/
svg.transition()
.duration(150)
.call(zoom.translate(iTranslate(t)).scale(iScale(t)).event);
//clickZoomed();
};
});
}
function zoomButton() {
var clicked = d3.event.target,
direction = 1,
factor = 0.5,
target_zoom = 1,
center = [width / 2, height / 2],
extent = zoom.scaleExtent(),
translate = zoom.translate(),
translate0 = [],
l = [],
view = {x: translate[0], y: translate[1], k: zoom.scale()};
d3.event.preventDefault();
direction = (this.id === 'zoom_in') ? 1 : -1;
target_zoom = zoom.scale() * (1 + factor * direction);
if (target_zoom < extent[0] || target_zoom > extent[1])
{
return false;
}
translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
view.k = target_zoom;
l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y];
view.x += center[0] - l[0];
view.y += center[1] - l[1];
interpolateZoom([view.x, view.y], view.k);
}
function zoomClicked() {
projection
.translate(projection.translate())
.scale( zoom.scale(zoom.scale() * Math.pow(2, +this.getAttribute("data-zoom")))
);
g.selectAll("path").attr("d", path);
}
d3.json("/adg29/raw/08d789b23d4b634588fb/us.json", function(error, us) {
var data = topojson.feature(us, us.objects.states).features;
var state_names = {};
d3.tsv("/adg29/raw/08d789b23d4b634588fb/us-state-names.tsv", function(tsv){
// extract just the names and Ids
tsv.forEach(function(d,i){
state_names[d.id] = d.name;
});
console.log(state_names);
g.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.attr("class", "feature state")
.attr('data-name',function(d){
console.log(d.id);
return state_names[d.id];
})
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path);
});
});
function clicked(d) {
if (active.node() === this) return reset();
active.classed("active", false);
active = d3.select(this).classed("active", true);
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
var clicked_state = active.attr('data-name');
console.log(bounds,dx,dy,x,y,scale,translate,clicked_state);
d3.select('#current').text(clicked_state);
svg.transition()
.duration(750)
.call(zoom.translate(translate).scale(scale).event);
}
function reset() {
active.classed("active", false);
active = d3.select(null);
svg.transition()
.duration(750)
.call(zoom.translate([0, 0]).scale(1).event);
}
function clickZoomed( ) {
g.style("stroke-width", 1.5 / zoom.scale() + "px");
g.attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")");
}
function zoomed() {
g.style("stroke-width", 1.5 / d3.event.scale + "px");
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
// If the drag behavior prevents the default click,
// also stop propagation so we don’t click-to-zoom.
function stopped() {
if (d3.event.defaultPrevented) d3.event.stopPropagation();
}
</script>
</body>

Programmatic Zoom In + Out to Topojson Bounding Box

Adds zoom in + out buttons to topojson features. Adapted from mbostock’s block #9656675

Uses zoom transitions to smoothly interpolate between different views. This example also allows you to freely pan and zoom with the mouse (or touch).

http://bl.ocks.org/mbostock/9656675

A Pen by Alan David Garcia on CodePen.

License.

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