Lab 5: Maps & More Maps

In this lab, you’ll be using everything you’ve learned about tables (and some of what you’re learning about lists!) to create a data visualization tool. Specifically, you’ll use a dataset of active Providence business licenses published by the Rhode Island government to mark locations on a map.

A key goal of this lab is to give you practice breaking large problems into separate tasks, each of which gets handled with its own collection of functions, expressions, and operators. As you work the problems, practice writing out the lists of tasks and using those to guide your work.

Part 1: Making a Simple Map

Question 1.1: Loading the data

Before you start working with the data, take a look at the Providence Business License dataset. The first sheet (inside the file) is the original dataset and the second is a pruned version with only the columns you’ll need for the lab. Load the pruned sheet into Pyret by pasting the following code into the definitions window:

include image
include shared-gdrive("cs111-2019.arr", "1JXQztpDCkTv2WRnQCjO7RGUgFv3pfgsB")
include gdrive-sheets
import data-source as ds
include shared-gdrive("stencil.arr", "1fQVCMlnKAufUH6ieWfDtQLCYwR16dpId")
include image-structs
import lists as L

##############
# SETUP CODE #
##############

ssid = "18eKc_o-eZ8TWno6ePYkcMGKhx-Y2Fe8LEERqV5gClaU"
data-sheet = load-spreadsheet(ssid)
pvd-businesses = 
  load-table: license-id, name, extended-hours, hotel, parking, 
    restaurant, food-and-liquor, food-dispenser, lat, long
    source: data-sheet.sheet-by-name("Licenses_Pruned", true)
    sanitize license-id using ds.string-sanitizer
    sanitize name using ds.string-sanitizer
    sanitize extended-hours using ds.bool-sanitizer
    sanitize hotel using ds.bool-sanitizer
    sanitize parking using ds.bool-sanitizer
    sanitize restaurant using ds.bool-sanitizer
    sanitize food-and-liquor using ds.bool-sanitizer
    sanitize food-dispenser using ds.bool-sanitizer
    sanitize lat using ds.num-sanitizer
    sanitize long using ds.num-sanitizer
  end

Question 1.2: Generate a Map

We have provided you with a function table-to-map(t :: Table) -> Image that takes in a table with columns named "x", "y", and "color". Treat table-to-map like a built-in function (you can call it just as you called filter-by or any other built-in function from previous labs). Each row of the table represents a business at location (x,y) on the map. table-to-map takes a map of Providence and places a dot of the specified color at location (x,y) on the map. Its final output looks something like this:

In order to use table-to-map, you will need to convert the longitude and latitude values in the table to x- and y-coordinate values. Only locations that fit within the bounds of the Providence map can be included. We’ve defined the following values for you (in the stencil.arr file that the setup code includes):

LAT-MIN - the minimum latitude that fits the map
LAT-MAX - the maximum latitude that fits the map

LON-MIN - the minimum longitude that fits the map
LON-MAX - the maximum longitude that fits the map

HEIGHT - the height of the map
WIDTH - the width of the map

Use these to write functions that scale latitudes to y values and longitudes to x values relative to the map dimensions. Use these functions to add columns named x and y to the providence-business-licenses table with these scaled values. Also add a column labeled color; for now, set each row’s color to “black”. (This extended table can now be used as an input to table-to-map.)

To check whether your function is correct, compare your map to the one above. They should have dots in the same places.


CHECKPOINT: Call over a TA once you reach this point.


Part 2: Analyzing the Data

Now that you have a map of good ol’ Providence (albeit not a very pretty one), we need to put it to good use. Your detective friends Frank and Joe Hardy (of the famed Hardy Boys mysteries) have taken you up on your invitation to visit and have just landed in Providence. They want to know what they should do with themselves while they are here!

Question 2.2: Generate a tourist map

Write a function generate-tourist-map(needs-hotel :: Boolean, has-car :: Boolean, stays-up-late :: Boolean, eats-out :: Boolean) -> Image that takes in a series of Booleans and returns a map marked with the places that Frank and Joe Hardy might want to visit.

If a place has more than one relevant characteristic, it should be colored according to the characteristic that’s most important to Frank and Joe. They care most about hotels, followed by eating, followed by places that have parking, followed by open late.

There are two high-level ways to go about this problem: use booleans to compute the desired color for each row, or use multiple transform-column passes over the table, each handling a different kind of place/different map color. You may take either approach in your solution. Your list of tasks should reflect your choice.

NOTE: Look at the list of predefined colors in the Pyret Documentation to see what options you have.


CHECKPOINT: Call over a TA once you reach this point.


Part 3: Donuts!

It turns out that your friends Frank and Joe Hardy are big advocates for local businesses and want to try out all the local donut shops in Providence.

How do we distinguish local shops from those in national chains? One way is to find all the donut (or doughnut) shops and see which ones have names that appear many times in the data – local shops will have fewer entries in the list. A frequency-bar chart would show us this answer visually, but what if we had to compute this from a list of names of donut shops, rather than read it off of a frequency-bar-chart?

Question 3.1

Here’s a collection of operations on tables and lists (assuming we imported lists as L). Write a function using operations from the following collection that produces a list of names of local donut shops. We’ll consider a donut shop to be local if it has no more than 2 locations in the providence-business table.

Write a function to determine whether a row describes a donut shop, and then filter the providence-business table to only contain the donut shops. Look for the donut shop (hint hint) that isn’t local (you can do this by eye), and remove it from the table.

Remember to write out your list of tasks first and use that to guide your work.

Question 3.2

Given the list of names of local donut shops, generate a map that highlights all donut shops, marking local stores with a red dot and non-local stores with a black dot.


CHECKPOINT: Call over a TA once you reach this point.


Part 4: A Different Kind of Map

Unsatisfied with how few local donut shops there are in Providence, Frank and Joe Hardy decide to open their own (Hardy Boys’ Old Mill Donuts)! After conducting a survey of existing options, they’ve created a list of the most popular donut options.

Question 4.1

Write a function create-donuts(donut-colors :: List<String>) -> List<Image> that takes in a list of strings representing valid colors and produces a list of “donuts”. Each donut should look like the following:

but in color that corresponds to its location in the list. Each donut should have a randomly generated size such that its radius is between 15-35.

HINT: Take a look at the List operations from part 3.1.

Question 4.2

Frank and Joe Hardy have discovered that the ideal donut size actually has a radius between 20-30. They want to discard all of the donut images that are outside of that range and make display signs out of the rest.

Write a function generate-donut-signs(donut-images :: List<Image>) -> List<Image> that takes in the output list from create-donut. This function should remove all of the donuts that lie outside of the size range and put the remaining donuts on rectangular backgrounds. This is what an output list item should look like:


CHECKPOINT: Call over a TA once you reach this point.


Success!

The Hardy Boys doughnut want you to go! Business at Old Mill Donuts is booming, but you’ve already gotten a call about a new case that desperately needs your attention.