Render a choropleth map of the US
Now that we have our data, it’s time to start drawing pictures - a choropleth map. That’s a map that uses colored geographical areas to encode data.
In this case, we’re going to show the delta between median household salary in a statistical county and the average salary of a single tech worker on a visa. The darker the blue, the higher the difference.
There’s a lot of gray on this map because the shortened dataset doesn’t span that many counties. There’s going to be plenty in the full choropleth too, but not as much as there is here.
Turns Out™ immigration visa opportunities for techies aren’t evenly distributed throughout the country. Who knew?
Just like before, we’re going to start with changes in our App
component, then build the new bit. This time, a CountyMap
component spread into three files:
CountyMap/index.js
, to make imports easierCountyMap/CountyMap.js
, for overall map logicCountyMap/County.js
, for individual county polygons
Step 1: Prep App.js
You might guess the pattern already: add an import, add a helper method or two, update render
.
// src/App.jsimport Preloader from './components/Preloader';import { loadAllData } from './DataHandling';// markua-start-insertimport CountyMap from './components/CountyMap';// markua-end-insert
That imports the CountyMap
component from components/CountyMap/
. Until we’re done, your browser should show an error overlay about some file or another not existing.
In the App
class itself, we add a countyValue
method. It takes a county entry and a map of tech salaries, and it returns the delta between median household income and a single tech salary.
// src/App.jscountyValue(county, techSalariesMap) {const medianHousehold = this.state.medianIncomes[county.id],salaries = techSalariesMap[county.name];if (!medianHousehold || !salaries) {return null;}const median = d3.median(salaries, d => d.base_salary);return {countyID: county.id,value: median - medianHousehold.medianIncome};}
We use this.state.medianIncomes
to get the median household salary and the techSalariesMap
input to get salaries for a specific census area. Then we use d3.median
to calculate the median value for salaries and return a two-element dictionary with the result.
countyID
specifies the county and value
is the delta that our choropleth displays.
In the render
method, we’ll do three things:
- prep a list of county values
- remove the “data loaded” indicator
- render the map
// src/App.jsrender() {if (this.state.techSalaries.length < 1) {return (<Preloader />);}// markua-start-insertconst filteredSalaries = this.state.techSalaries,filteredSalariesMap = _.groupBy(filteredSalaries, 'countyID'),countyValues = this.state.countyNames.map(county => this.countyValue(county, filteredSalariesMap)).filter(d => !_.isNull(d));let zoom = null;// markua-end-insertreturn (<div className="App container">// markua-start-delete<h1>Loaded {this.state.techSalaries.length} salaries</h1>// markua-end-delete// markua-start-insert<svg width="1100" height="500"><CountyMap usTopoJson={this.state.usTopoJson}USstateNames={this.state.USstateNames}values={countyValues}x={0}y={0}width={500}height={500}zoom={zoom} /></svg>// markua-end-insert</div>);}
We call our dataset ...