Skip to content

Instantly share code, notes, and snippets.

@tanyaschlusser
Last active August 29, 2015 14:21
Show Gist options
  • Save tanyaschlusser/b4aba0427771ec6e633f to your computer and use it in GitHub Desktop.
Save tanyaschlusser/b4aba0427771ec6e633f to your computer and use it in GitHub Desktop.
Clickable U.S. Map

Mike Bostock's example with US counties is modified for states, adding a state ID indicator on hover and a click response: the beginnings of a map-based user interface. For accessibility, a supplementary alphabetized list of states is also provided.

Where to get the map

Mike Bostock has a github repo with code to automatically pull census shapefiles and convert them to TopoJSON

Merging in an external file with additional data

The chart is at a coarser resolution than in Mike Bostock's demo, and does not include the different 'land' and 'state' objects. File size is only 20kb. I tried an order of magnitude smaller (quantization of 1e3) and the state boundaries did not close. At even this resolution Hawaii is barely noticeable. file size is not much different after adding the additional state properties. The new Makefile entry (in the us-atlas project)

topo/us-states-100k-ungrouped.json: shp/us/states.shp
	mkdir -p $(dir $@)
	node_modules/.bin/topojson \
		-o $@ \
		--no-pre-quantization \
		--post-quantization=1e4 \
		--simplify=7e-5 \
		-e fips2abbr.tsv \
		--properties STATE,STATE_FIPS,ORDER_ADM,MONTH_ADM,DAY_ADM,YEAR_ADM,ABBR=ABBR \
		--id-property=+STATE_FIPS \
		-- $<
STATE_FIPS ABBR
53 WA
10 DE
11 DC
55 WI
54 WV
15 HI
12 FL
56 WY
33 NH
34 NJ
35 NM
48 TX
22 LA
02 AK
37 NC
38 ND
31 NE
47 TN
36 NY
42 PA
44 RI
32 NV
51 VA
08 CO
78 VI
06 CA
01 AL
05 AR
50 VT
17 IL
13 GA
18 IN
19 IA
40 OK
04 AZ
16 ID
09 CT
23 ME
24 MD
25 MA
39 OH
49 UT
29 MO
27 MN
26 MI
20 KS
30 MT
28 MS
72 PR
45 SC
21 KY
41 OR
46 SD
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<link rel=stylesheet type=text/css href="style.css">
<title>Clickable U.S. Map</title>
</head>
<body>
<section>
<div id="map"></div>
<div id="lookup"></div>
<div id="information"></div>
</section>
<script>
// from: http://bl.ocks.org/mbostock/4090848
var width = 450,
height = 300;
var str_ordinal = function(n) {
var suffix = 'th';
if ( (n - 11) % 100 == 0 || (n - 12) % 100 == 0 || (n - 13) % 100 == 0 ) {
// ends with 11 or 12 or 13
suffix = 'th';
} else if ( (n - 1) % 10 == 0 ) {
// ends with 1
suffix = 'st';
} else if ( (n - 2) % 10 == 0 ) {
suffix = 'nd';
} else if ( (n - 3) % 10 == 0 ) {
suffix = 'rd';
}
return n + suffix;
};
var add_info = function(p){
var this_state = p.STATE;
d3.select("#information").html(
'<h1>' + p.STATE + '</h1>' +
'The ' + str_ordinal(p.ORDER_ADM) + ' state, joining' +
' on ' + p.MONTH_ADM + ' ' + p.DAY_ADM + ', ' + p.YEAR_ADM
);
d3.selectAll("g.state").classed("highlight",
function(dd) { return dd.properties.STATE == this_state ? true : false; });
d3.selectAll("li.state").classed("highlight",
function(dd) { return dd.STATE == this_state ? true : false; });
};
//---------------------------------------------- The clickmap
var clickmap = d3.select("#map").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("us-states-100k-ungrouped.json", function(error, us) {
if (error) return console.error(error);
var projection = d3.geo.albersUsa()
.scale(450)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var states = clickmap.selectAll("g.state")
.data(topojson.feature(us, us.objects.states).features
.filter(function(d) { return d.properties.STATE_FIPS != '72'; })
)
.enter().append("g")
// Exclude Puerto Rico because at this scale it is just 1 point
// and there will be a rendering error because there is no centroid.
.attr("class", "state")
.on("mouseover", function(d) {
var this_state = d.properties.STATE;
d3.selectAll("li.state").classed("falsehover",
function(dd) { return dd.STATE == this_state ? true : false; });
})
.on("mouseout", function(d) {
d3.selectAll("li.state").classed("falsehover", false);
})
.on("click", function(d) { add_info(d.properties); });
states.append("path")
.attr("d", path)
.attr("id", function(d) { return d.properties.STATE; })
.on("mousedown",
function(d){
d3.select("#" + d.properties.STATE).classed("click", true);
})
.on("mouseup",
function(d){
d3.select("#" + d.properties.STATE).classed("click", false);
});
states.append("text")
.attr("class", "label")
.attr("x", function(d){ return path.centroid(d)[0]; })
.attr("y", function(d){ return path.centroid(d)[1]; })
.attr("dy", "5px")
.attr("font-family", "Sans-Serif" )
.text(function(d){
return d.properties.ABBR; })
//---------------------------------------------- The lookup list (for accessibility)
var lookups = d3.select("#lookup")
.append("ul").selectAll("li")
.data(topojson.feature(us, us.objects.states).features
.map(function(f){ return f.properties; })
.filter(function(d) { return d.STATE_FIPS != '72'; })
.sort(function(a, b){ return a.STATE.localeCompare(b.STATE); }))
.enter().append("li")
.attr("class", "state")
.text(function(d) { return d.ABBR; })
.on("mouseover", function(d) {
var this_state = d.STATE;
d3.selectAll("g.state").classed("falsehover",
function(dd) {
return dd.properties.STATE == this_state ? true : false; });
})
.on("mouseout", function(d) {
d3.selectAll("g.state").classed("falsehover", false);
})
.on("click", add_info);
// Pre-fill the info window with Delaware, the first state
add_info(states.filter(
function(d) { return d.properties.STATE_FIPS == 10; }).datum().properties);
});
</script>
</body>
</html>
body {
font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
display: inline;
margin: 10px;
}
li {
color: #888;
display: inline-block;
font-size: 9px;
padding: 0;
margin: 3px;
width: 18px;
}
li:hover {
color: #242;
background: #dfd;
}
section {
display: flex;
height: 300px;
width: 900px;
}
text {
text-anchor: middle;
font-size: 8px;
}
ul {
list-style-type: none;
padding: 0;
}
.click {
stroke: #dfd;
fill: #cfe;
stroke-width: 3px;
}
.state {
fill: #bfd;
stroke: #242;
}
.label {
opacity: 0;
}
li.falsehover {
color: #242;
background: #dfd;
}
li.highlight {
background: #bff;
}
.state.highlight {
stroke: #242;
fill: #bff;
}
.state.falsehover {
fill: #dfd;
}
.state.falsehover .label {
opacity: 1;
}
.state:hover {
fill: #dfd;
}
.state:hover .label {
opacity: 1;
}
#map {
border: 1px solid #888;
width: 450px;
height: 300px;
}
#lookup {
width: 90px;
height: 300px;
padding: 0;
}
#information {
width: 280px;
height: 290px;
padding: 5px 10px;
}
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment