Skip to content

Instantly share code, notes, and snippets.

@fafnirical
Forked from MaciejKus/README.md
Last active March 6, 2018 01:22
Show Gist options
  • Save fafnirical/b208477cdb4b7116bc25e8f4a2abd494 to your computer and use it in GitHub Desktop.
Save fafnirical/b208477cdb4b7116bc25e8f4a2abd494 to your computer and use it in GitHub Desktop.
d3 map with states and countries

A d3 world map which shows the states of Canada and the USA on zoom. Also has rotation, but only when scale is set to 1 (zoomed out).

I combined a world topo.json file with a states topo.json file using something like this:

node_modules/.bin/topojson --allow-empty -p name -o combined2.json -- data/countries.topo.json data/states_usa.topo.json
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.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
body {
background-color: white;
}
svg {
border: 2px solid black;
background-color: white;
}
.selected {
fill: red;
}
.boundary {
fill: #DEB887;
stroke: black;
stroke-width: 1px;
}
.hidden {
display: none;
}
div.tooltip {
color: #222;
background: #fff;
border-radius: 3px;
box-shadow: 0px 0px 2px 0px #a6a6a6;
padding: .2em;
text-shadow: #f5f5f5 0 1px 0;
opacity: 0.9;
position: absolute;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 962,
height = 502;
//countries which have states, needed to toggle visibility
//for USA/ etc. either show countries or states, not both
var usa, canada;
var states; //track states
//track where mouse was clicked
var initX;
//track scale only rotate when s === 1
var s = 1;
var mouseClicked = false;
var projection = d3.geo.albersUsa()
.scale(153)
.translate([width/2,height/1.5])
var zoom = d3.behavior.zoom()
.scaleExtent([1, 20])
.on("zoom", zoomed);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
//track where user clicked down
.on("mousedown", function() {
d3.event.preventDefault();
//only if scale === 1
if(s !== 1) return;
initX = d3.mouse(this)[0];
mouseClicked = true;
})
.on("mouseup", function() {
if(s !== 1) return;
mouseClicked = false;
})
.call(zoom);
//for tooltip
var offsetL = document.getElementById('map').offsetLeft+10;
var offsetT = document.getElementById('map').offsetTop+10;
var path = d3.geo.path()
.projection(projection);
var tooltip = d3.select("#map")
.append("div")
.attr("class", "tooltip hidden");
//need this for correct panning
var g = svg.append("g");
//det json data and draw it
d3.json("combined2.json", function(error, world) {
if(error) return console.error(error);
//countries
g.append("g")
.attr("class", "boundary")
.selectAll("boundary")
.data(topojson.feature(world, world.objects.countries).features)
.enter().append("path")
.attr("name", function(d) {return d.properties.name;})
.attr("id", function(d) { return d.id;})
.on('click', selected)
.on("mousemove", showTooltip)
.on("mouseout", function(d,i) {
tooltip.classed("hidden", true);
})
.attr("d", path);
usa = d3.select('#USA');
canada = d3.select('#CAN');
//states
g.append("g")
.attr("class", "boundary state hidden")
.selectAll("boundary")
.data(topojson.feature(world, world.objects.states).features)
.enter().append("path")
.attr("name", function(d) { return d.properties.name;})
.attr("id", function(d) { return d.id;})
.on('click', selected)
.on("mousemove", showTooltip)
.on("mouseout", function(d,i) {
tooltip.classed("hidden", true);
})
.attr("d", path);
states = d3.selectAll('.state');
});
function showTooltip(d) {
label = d.properties.name;
var mouse = d3.mouse(svg.node())
.map( function(d) { return parseInt(d); } );
tooltip.classed("hidden", false)
.attr("style", "left:"+(mouse[0]+offsetL)+"px;top:"+(mouse[1]+offsetT)+"px")
.html(label);
}
function selected() {
d3.select('.selected').classed('selected', false);
d3.select(this).classed('selected', true);
}
function zoomed() {
var t = d3.event.translate;
s = d3.event.scale;
var h = 0;
t[0] = Math.min(
(width/height) * (s - 1),
Math.max( width * (1 - s), t[0] )
);
t[1] = Math.min(
h * (s - 1) + h * s,
Math.max(height * (1 - s) - h * s, t[1])
);
zoom.translate(t);
if(s === 1 && mouseClicked) {
return;
}
g.attr("transform", "translate(" + t + ")scale(" + s + ")");
//adjust the stroke width based on zoom level
d3.selectAll(".boundary")
.style("stroke-width", 1 / s);
//toggle state/USA visability
if(s > 1.5) {
states
.classed('hidden', false);
usa
.classed('hidden', true);
canada
.classed('hidden', true);
} else {
states
.classed('hidden', true);
usa
.classed('hidden', false);
canada
.classed('hidden', false);
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment