Problem:
I'm attempting to create an interactive map of the US in which state, county and national boundaries are displayed. Counties are shaded based on data, and hovering over a state should highlight all counties in the state, and the state should be clickable. I want to achieve this by having a SVG with the county shapes inside of state shapes, inside of a US shape.
I can generate a county map based on a CENSUS county shape file, and I can shade the states based on data in an external CSV by prepping the file with TopoJSON command line and using the following code in D3:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
}
</style>
<body>
<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 = 600;
var path = d3.geo.path()
.projection(d3.geo.albersUsa());
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("counties_pa.json", function(error, us) {
if (error) return console.error(error);
var color = d3.scale.threshold()
.domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
.range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);
svg.append('g').attr('class','counties').selectAll("path").data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter().append('path').attr('d',path).attr('style',function(d){return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);});
});
</script>
This is mostly visually acceptable (except it doesn't have discrete state / national boundaries) - but is functionally inadequate. In order to apply CSS to the counties on a state hover, the counties need to be within a state shape, or grouped somehow.
What i've tried:
- Using topojson-merge in the command line to merge the counties into state shapes, and then render the state shapes separately - this helps with having discrete state borders - but I haven't figured a way to nest the counties into the respective state shapes.
What i'm working out now:
Somehow combining a state TopoJSON file and a county TopoJSON file and nesting the counties in the states, then rendering with D3.
Somehow using d3 to take non-nested state and county data and just nest it on the client on the client level.
In the end I would like to learn about the most effective and quickest rendering process to achieve my desired functionality.
Thanks for your help in advance.
I took a punt on your data sources, and here is what it looks like you're trying to achieve: http://bl.ocks.org/benlyall/55bc9474e6d531a1c1fe
Basically, I have generated a TopoJSON file using the following command line:
Some explanation on this:
-o counties_pa.json
sets the name of the output file--id-property=+GEOID
will use that property in the input file as theid
of each output geometry-p
means include all properties from the input file-e POP01.txt
will pull external data in from the filePOP01.txt
. This file is a csv file generated from thePOP01.xls
spreadsheet available from http://www.census.gov/support/USACdataDownloads.html#POP--id-property=+STCOU
means that the id property from the external file (POP01.txt
) is in the STCOU column. This is used to match up with matchingid
s in the input file (which are in theGEOID
property as explained above)-p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP
explicitly lists the properties that I want in the output file, so anything extra won't be included. POP010210D is the column name for the population as at the 2010 census, so I just used that for demonstration purposes.cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp
are the two input files. One for county shapes and one for state shapes. They will each be added to the output file in seperate properties named after their filenames.I did it this way, as you seemed to be colouring your county areas based on population density, so both population and area needed to be in the output file. The population was pulled from the
POP01
spreadsheet and linked to each county based on theGEOID
(which is just the state number concatentated with the county number).I was just looking for a quick and easy way to recreate your dataset, and then add the state boundaries to it so I could post the answer. Not sure how closely this matches your original data, but it seems to work for demonstration purposes.
From that, I took your code above and updated it to:
The new and interesting bits of code are:
Add a
data-state
attribute to each county to determine which state it belongs to:Add the state boundaries (I combined states to the TopoJSON file in the
topojson
command line)Added hover handlers so you can see how I'm determining the grouping of counties into states:
Tied these hover handlers to each county so they get executed at the appropriate times: