Goals

Learn to create geospatial and treemap visualizations and apply colormaps.

Instructions

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.

Due Date

The assignment is due at 11:59pm on Monday, November 2.

Submission

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.

Details

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.

0. Info

Like Assignment 1, start by creating an HTML web page with the title “Assignment 4”. It should contain the following text:

  • Your name
  • Your student id
  • The course title (“Data Visualization (CSCI 627/490)”), and
  • The assignment title (“Assignment 4”)
  • The text “This assignment is all my own work. I did not copy the code from any other source.” (Your inclusion of this text indicates that you understand the consequences of violating the NIU Academic Integrity Standards.)

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.

1. Midwest State Map (30 pts)

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.

Example Solution for Part 1
Example Solution for Part 1

Hints

  • Use the proper array function to filter the list of features by state.
  • Each state is a feature so if 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.

2. Corn Production by State (40 pts)

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.

a. 2020 Corn Production by State (20 pts)

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:

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:

b. Change in Production from 2019 to 2020 (20 pts)

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.

Hints

  • If you craft your function to create the map in Part 1 well, you can use reuse that function and just update the fill based on the corn production data.
  • The Map constructor can be created from an iterable of key-value pairs.
  • Use Map.get to access a map’s value by key passing the key.
  • d3.scaleSequential can help with colormapping. Remember to check the type of the values you are displaying to determine a correct colormap.
  • To create a good colormap, think about what the domain of the values is.
  • To create a nice legend, consider using (with proper attribution) M. Bostock’s color ramp approach.

3. [CS 627 Only] 2019 Corn Production Treemap (40 points)

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).

Hints

  • After creating the hierarchy, you can get a node’s parent via the .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).
  • A Set will eliminate duplicated nodes (e.g. from regions, states)
  • Make sure your 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.
  • Data for a property foo of a leaf d in stored in d.data.foo, but extracting a state/region label is at d.data[0].
  • Labels can be centered at a particular point by using the text-anchor: middle style property.
Example Solution for Part 3
Example Solution for Part 3

Extra Credit

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.