For the data I'm using http://bl.ocks.org/mbostock/raw/4090846/us.json For geo I'm using TopoJSON https://github.com/mbostock/topojson
Last active
November 8, 2015 18:11
-
-
Save sadaco/17a5b3e6c5827b90e82b to your computer and use it in GitHub Desktop.
USA map with "click-to-zoom via transform" and "zoom controls"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="initial-scale=1,maximum-scale=1"/> | |
<title>US Map with TopoJSON</title> | |
<link rel="stylesheet" type="text/css" href="../../css/styles.css"/> | |
<script type="text/javascript" src="../../lib/d3.js"></script> | |
<script type="text/javascript" src="../../lib/topojson.js"></script> | |
<style> | |
path { | |
fill: #dddddd; | |
stroke: #ffffff; | |
stroke-width: .5px; | |
} | |
path:hover { | |
fill: #fab440; | |
} | |
#us-drilldown{ | |
margin-top: 50px; | |
} | |
.ClickState { | |
fill: #ccc; | |
cursor: pointer; | |
} | |
.ClickState.active { | |
fill: orange; | |
} | |
.ZoomState { | |
fill: none; | |
stroke: #fff; | |
stroke-linecap: round; | |
stroke-linejoin: round; | |
} | |
//Zoom buttons | |
</style> | |
</head> | |
<body> | |
<div id="us-drilldown"></div> | |
<script type="text/javascript"> | |
var width = 960, | |
height = 500, | |
center = [width / 2, height / 2], | |
active = d3.select(null); | |
//Click zoom | |
var projection = d3.geo.albersUsa() | |
.scale(1000) | |
.translate([width / 2, height / 2]); | |
var zoom = d3.behavior.zoom() | |
.scaleExtent([1, 8]) | |
.on("zoom", zoomed); | |
var path = d3.geo.path() | |
.projection(projection); | |
var svg = d3.select("#us-drilldown").append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.append("g"); | |
svg.append("rect") | |
.on("click", reset); | |
var g = svg.append('g') | |
.call( | |
d3.behavior.zoom() | |
.scaleExtent([1, 10]) | |
.on("zoom", zoom) | |
) | |
.style("stroke-width", "1px"); | |
svg | |
.call(zoom) | |
.call(zoom.event); | |
//http://bl.ocks.org/mbostock/raw/4090846/us.json | |
d3.json("../../data/us.json", function (error, topology) { | |
g.selectAll("path") | |
.data(topojson.feature(topology, topology.objects.states).features) | |
.enter().append("path") | |
.attr("d", path) | |
.attr("class", "ClickState") | |
.on("click", clicked); | |
g.append("path") | |
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; })) | |
.attr("class", "ZoomState") | |
.attr("d", path); | |
}); | |
//Zoom with buttons | |
function zoomed() { | |
g.attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")"); | |
} | |
d3.select(self.frameElement).style("height", height + "px"); | |
// Simplest possible buttons | |
svg.selectAll(".button") | |
.data(['zoom_in', 'zoom_out']) | |
.enter() | |
.append("rect") | |
.attr("x", function(d,i){return 10 + 50*i}) | |
.attr({y: 10, width: 20, height: 20, class: "button"}) | |
.attr("id", function(d){return d}) | |
.style("fill", function(d,i){ return i ? "#dddddd" : "#dddddd"}) | |
var intervalID; | |
d3.selectAll('.button').on('mousedown', function(){ | |
d3.event.preventDefault(); | |
var factor = (this.id === 'zoom_in') ? 1.1 : 1/1.1; | |
intervalID = setInterval(zoom_by, 40, factor); | |
}).on('mouseup', function(){ | |
d3.event.preventDefault(); | |
clearInterval(intervalID); | |
intervalID = undefined; | |
}) | |
function zoom_by(factor){ | |
var scale = zoom.scale(), | |
extent = zoom.scaleExtent(), | |
translate = zoom.translate(), | |
x = translate[0], y = translate[1], | |
target_scale = scale * factor; | |
// If we're already at an extent, done | |
if (target_scale === extent[0] || target_scale === extent[1]) { return false; } | |
// If the factor is too much, scale it down to reach the extent exactly | |
var clamped_target_scale = Math.max(extent[0], Math.min(extent[1], target_scale)); | |
if (clamped_target_scale != target_scale){ | |
target_scale = clamped_target_scale; | |
factor = target_scale / scale; | |
} | |
// Center each vector, stretch, then put back | |
x = (x - center[0]) * factor + center[0]; | |
y = (y - center[1]) * factor + center[1]; | |
// Enact the zoom immediately | |
zoom.scale(target_scale) | |
.translate([x,y]); | |
zoomed(); | |
} | |
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]; | |
g.transition() | |
.duration(750) | |
.style("stroke-width", 1.5 / scale + "px") | |
.attr("transform", "translate(" + translate + ")scale(" + scale + ")"); | |
} | |
function reset() { | |
active.classed("active", false); | |
active = d3.select(null); | |
g.transition() | |
.duration(750) | |
.style("stroke-width", "1.5px") | |
.attr("transform", ""); | |
} | |
function zoom() { | |
g.attr("transform", "translate(" | |
+ d3.event.translate | |
+ ")scale(" + d3.event.scale + ")"); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment