Learn to create geospatial and treemap visualizations and apply colormaps.
There are three parts to the assignment. You may complete the assignment in a single HTML file or use multiple files (e.g. one for CSS, one for HTML, and one for JavaScript). You must use D3 v6 for this assignment. All visualization should be done using D3 calls except where instructed otherwise. You may use other libraries (e.g. underscore.js or jQuery), but you must credit them in the HTML file you turn in. Extensive documentation for D3 is available, and Vadim Ogievetsky’s example-based introduction that we went through in class is also a useful reference. In addition, Scott Murray’s Interactive Data Visualization for the Web is a good reference. This D3 Map Example may also help.
The assignment is due at 11:59pm on Monday, November 2.
You should submit any files required for this assignment on Blackboard. You may complete the assignment in a single HTML file or use multiple files (e.g. one for HTML, one for CSS, and one for JavaScript). Note that the files should be linked to the main HTML document accordingly. The filename of the main HTML document should be a4.html
. Blackboard may complain about the files; if so, please zip the files and submit the zip file instead.
In this assignment, we will examine Midwest corn harvest data. This data comes from the USDA’s Census of Agriculture through the QuickStats service. Specifically, we are interested in how much corn each state (and region and county) produces. This data has been extracted and is available online. To create maps, we will use data with the outlines of each US state. This file is in a GeoJSON format that D3 can more easily handle. In addition, the boundaries have been simplified. The goal of the assignment is to understand corn production across the Midwest and examine trends over time.
Like Assignment 1, start by creating an HTML web page with the title “Assignment 4”. It should contain the following text:
If you used any additional JavaScript libraries or code references, please append a note to this section indicating their usage to the text above (e.g. “I used the jQuery library to write callback functions.”) Include links to the projects used. You do not need to adhere to any particular style for this text, but I would suggest using headings to separate the sections of the assignment.
A template for the assignment is provided: html, js; save them both as source. You may use this or create your own files.
Create a map that shows the state boundaries for the following states only:
['Illinois', 'Indiana', 'Iowa', 'Minnesota', 'Nebraska', 'North Dakota', 'South Dakota', 'Wisconsin']
You will only need the GeoJSON data for this part of the assignment.
Remember that you will need a projection for the map. Since Iowa is in the middle of the states we will examine, we will use the Iowa North State Plane projection. Noah Veltman has provided a handy list of these in d3.geo
format here.
For the map, you will want to use each state as a separate feature. You can access mapData.features
to obtain this array. Make sure that you filter the list of features to include only those states listed above! Each feature has a number of properties that will be useful for the next steps. If you wish, you could implement a tooltip using the Name
attribute to show a state’s name (for a data item d
, this is stored in d.properties.name
). You can load the mapData via d3.json
. Note that this is an asynchronous call, and you should use promises to call the function to draw the map after the data is loaded.
mapData
is the variable loaded by d3.json
, mapData.features
is a list of all of the states.d3.geoPath
can have an associated projection is used to translate GeoJSON features into paths on screen.You will create two visualizations, but you should work to create a single function to facilitate most of the work, passing data accessor functions and color scales as necessary. Do note that it may be easier to first create the visualizations individually and then refactor them.
Using the corn production data in concert with the GeoJSON data, create a new choropleth map that shows the corn production (as AcresHarvested
) by state. The colormap should accurately convey the amount for each state. Create a legend so a viewer can understand the values.
The corn production data is of the form:
[{Year: 2020, State: "ILLINOIS", AcresHarvested: 11200000},
{Year: 2020, State: "INDIANA", AcresHarvested: 5250000},
{Year: 2020, State: "IOWA", AcresHarvested: 12700000},
{Year: 2020, State: "MINNESOTA", AcresHarvested: 7550000},
...]
For this part, you will need to extract the 2020 data only, and I would suggest writing code to transform the corn production data into a Map
of the form {"ILLINOIS": 11200000, "INDIANA": 5250000, ...}
.
You will need to match the data in the GeoJSON file with the data in the corn production JSON file. Given a GeoJSON feature d
, the state name (name
) is accessed from the properties
object as d.properties.name
. However, note that the state name in the corn data file is in all upper-case letters while the one in the GeoJSON map file is mixed-case. You can convert a string to upper-case using toUpperCase()
.
Finally, to load multiple external data files using D3, use Promise.all. To load two JSON files file1.json
and file2.json
, you would have the following code:
Create a second choropleth visualization, but now show the change in acres harvested from 2019 to 2020. It should be clear from the visualization whether the number of acres increased or decreased and by how much. This will be a different colormap than in part a. Again, add a legend.
You will need to calculate the difference between 2020 and 2019 for each state. To do, one option is to use d3.group
to group the data by state and year and then use that map to calculate the difference for each state. As with Part 1, I would suggest trying to create a Map
of the form {"ILLINOIS": 1000000, "INDIANA": 430000, ...}
before passing the data to your function that creates the map.
d3.scaleSequential
can help with colormapping. Remember to check the type of the values you are displaying to determine a correct colormap.Now, we wish to better understand corn production by county, but in the context of states and agricultural regions. To do so, we can create a treemap using the values for acres harvested and the hierarchy state -> ag district -> county. To help users understand the data, we will label the states and agricultural regions, and allow county names to be discovered as tooltips. This part uses a different data file than the Part 2: midwest-corn-county.json
To create the hierarchy, we can first group the data by state and district abbreviation (DistrictAbbrv
). Then, we can pass this result to d3.hierarchy
to build the tree. Note that maps can be directly without transformation. Make sure to specify which attribute to sum and how to sort. We can now pass this hierarchy to the d3.treemap layout function to calculate the rectangles. Use the squarify layout (this is the default).
From the treemap t
, you can extract all leaves via t.leaves()
to draw the visualization. Use the x0, x1, y0, y1
coordinates to draw each leaf rectangle. The color should reflect the state (all counties in a given state should have the same color). Add tooltips for the county name and labels for the region (DistrictAbbrv
) and state (State
).
.parent
property. All leaves (counties) will be at the same depth so you can extract all nodes for regions and states via the correct mapping of leaves to parents (or grandparents).Set
will eliminate duplicated nodes (e.g. from regions, states)selectAll
statements do not select already created objects! For example, calling svg.selectAll(
text)
twice on the same svg will bind the already created rectangles on the second call. You can attach a class name (text.region
) to the object type to avoid this.foo
of a leaf d
in stored in d.data.foo
, but extracting a state/region label is at d.data[0]
.text-anchor: middle
style property.For extra credit, CS 490 students may complete Part 3. In addition, all students may implement a way for users to interactively update which year (or year range) is shown (year for Part 2a, range for Part 2b or Part 3). Use D3 transitions to animate the change from one year (or year range) to the other.