Plot at least two high-quality static maps at the zip code level, with one using the COVID-19 testing data and one using a related factor such as the number of nursing homes or the elderly population. You can use either plot method for sf or ggplot method. - These maps must be choropleth maps, i.e., polygon filling colors representing quantities. - Maps must have title, legend, graticule (grid lines), neatline (border), and labels on axes (commonly geography coordinates in degrees). - Bonus point: add scale bar and north arrow.
acsCovidData reading
acsPopByZip.gpkg provided for this week’s data, sand
setting layer NYC_COVID_Pop_by_Zip.breaks_covid and
beaks_elderly using classIntervals() via
lecture.
- 0.00001 so that the lowest value is captured in
the first bin.breaks_elderly$brks[1] <- 0 to manually set
the lower bound to 0 so that the legend isn’t a decimal.acsCovidData using
cut() fixed scientific notation in legend.
st_bbox for bounding box in WGS84 so
coord_sf() axis labels display as degrees for both maps.
map_covid and map_elderly
na.value = "grey80" - remove data with missing zip
codesdatum = sf::st_crs(4326) - graticule grid via ggplot2
coord_sfacsCovidData <- sf::st_read('./acsPopByZip.gpkg',
layer = "NYC_COVID_Pop_by_Zip")
## Reading layer `NYC_COVID_Pop_by_Zip' from data source
## `C:\Users\moniq\Desktop\SPRING_26\DATA_ANALYSIS_R\Week_9\R-Spatial\acsPopByZip.gpkg'
## using driver `GPKG'
## Simple feature collection with 180 features and 13 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 913129 ymin: 120020.9 xmax: 1067113 ymax: 272710.9
## Projected CRS: NAD83 / New York Long Island (ftUS)
# Choropleth Map breaks - Positive covid casesa and elderly
breaks_covid <- classIntervals(
c(min(acsCovidData$Positiv, na.rm = TRUE) - 0.00001, acsCovidData$Positiv),
n = 7, style = "quantile"
)
breaks_elderly <- classIntervals(
c(min(acsCovidData$eldrlyP, na.rm = TRUE) - 0.00001, acsCovidData$eldrlyP),
n = 7, style = "quantile"
)
## Warning in classIntervals(c(min(acsCovidData$eldrlyP, na.rm = TRUE) - 1e-05, :
## var has missing values, omitted in finding classes
# Forcing the first break to 0 so legend isnt in negatives for lower bounds
breaks_elderly$brks[1] <- 0
acsCovidData <- acsCovidData %>%
mutate(
covid_cat = cut(Positiv,breaks_covid$brks,dig.lab = 4),
elderly_cat = cut(eldrlyP,breaks_elderly$brks,dig.lab = 6))
nyc_bbox <- sf::st_bbox(sf::st_transform(acsCovidData, 4326))
# covid map
map_covid <- ggplot(acsCovidData) +
geom_sf(aes(fill = covid_cat), color = "white", linewidth = 0.1) +
scale_fill_brewer(palette = "YlOrRd", name = "Positive Cases", na.value = "grey80") +
coord_sf(
xlim = c(nyc_bbox["xmin"], nyc_bbox["xmax"]),
ylim = c(nyc_bbox["ymin"], nyc_bbox["ymax"]),
default_crs = sf::st_crs(4326),
datum = sf::st_crs(4326)
) +
annotation_scale(location = "bl", width_hint = 0.3) +
annotation_north_arrow(location = "tr", which_north = "true",
style = north_arrow_fancy_orienteering()) +
labs(
x = "Longitude",
y = "Latitude",
title = "NYC COVID-19 Positive Cases by ZIP Code",
caption = "Data Source: NYC DOH, 2020"
) +
theme_bw() +
theme(
legend.position = "right",
panel.border = element_rect(color = "black", linewidth = 1.2),
panel.grid = element_line(color = "grey80", linewidth = 0.3)
)
map_covid
# Elderly pop map
map_elderly <- ggplot(acsCovidData) +
geom_sf(aes(fill = elderly_cat), color = "white", linewidth = 0.1) +
scale_fill_brewer(palette = "Blues", name = "Elderly Pop (65+)", na.value = "grey80") +
coord_sf(
xlim = c(nyc_bbox["xmin"], nyc_bbox["xmax"]),
ylim = c(nyc_bbox["ymin"], nyc_bbox["ymax"]),
default_crs = sf::st_crs(4326),
datum = sf::st_crs(4326)
) +
annotation_scale(location = "bl", width_hint = 0.3) +
annotation_north_arrow(location = "tr", which_north = "true",
style = north_arrow_fancy_orienteering()) +
labs(
x = "Longitude",
y = "Latitude",
title = "NYC Elderly Population (65+) by ZIP Code",
caption = "Data Source: ACS 2018, US Census"
) +
theme_bw() +
theme(
legend.position = "right",
panel.border = element_rect(color = "black", linewidth = 1.2),
panel.grid = element_line(color = "grey80", linewidth = 0.3)
)
map_elderly
Use ggplot2 and other ggplot-compatible packages to create a multi-map figure illustrating the possible relationship between COVID-19 confirmed cases or rate and another factor (e.g., the number of nursing homes, neighborhood racial composition, elderly population, etc.). - The maps should be put side by side on one single page. Both maps must have the exactly same size and scale.
combined map is both plots where
plot_layout() to control the grid, and
plot_annotation() is the shared title/caption.
map_covid_left <- map_covid +
labs(title = NULL, caption = NULL) +
theme(legend.position = "bottom",
legend.title.position = "top")
map_elderly_right <- map_elderly +
labs(title = NULL, caption = NULL, y = "") +
theme(legend.position = "bottom",
legend.title.position = "top",
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
# combining maps covid positive left and elderly pop to the right
combined <- map_covid_left + map_elderly_right +
plot_layout(ncol = 2, nrow = 1) +
plot_annotation(
title = "NYC COVID-19 Positive Cases and Elderly Population by ZIP Code",
caption = "Data Sources: NYC DOH 2020; ACS 2018, US Census",
theme = theme(plot.title = element_text(size = 14, hjust = 0.5))
)
combined
Create a web-based interactive map for COIVD-19 data using tmap, mapview, or leaflet package and save it as a HTML file. - The interactive map must have at least three layers (three layers in a single interactive map, not three layers in three separate interactive maps.) - At least one of the three layers has legend (opens and closes together with the corresponding layer). - Layers should have tips (show up when a user moves mouse over a feature like a point or polygon) and popups (when a user click on a feature). - Bonus point: the interactive map is implemented with leaflet and/or extra mapping features (explicitly state what extra features you added). Features implemented in packages by default would not count. For example, mapview would show the current map zoom level and the coordinates of the mouse by default.
acsCovidData_4326, added
health_sf_nyc from lab 2 as a point layer because I wanted
to see points with the polygons, added a filter for hospital data.paste0 to display specific pop-up data for all
four datasets per map.addEasyButton using a font awesome crosshair icon
to re-center map using an onClick() handler function and
map.setView(). R
docs easyButtonaddPolygons() - changed palette, popup and column names
per dataset.addLayersControl() via lecture but also added
hideGroup() to set covid layer by default and a toggle for
the other two maps visibility.# Adding health facilities as point layer
health_sf_nyc <- st_read("nyc_data.gpkg", layer = "health_facilities")
## Reading layer `health_facilities' from data source
## `C:\Users\moniq\Desktop\SPRING_26\DATA_ANALYSIS_R\Week_9\R-Spatial\nyc_data.gpkg'
## using driver `GPKG'
## Simple feature collection with 1293 features and 34 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: 929529 ymin: 127612.4 xmax: 1066447 ymax: 271059.5
## Projected CRS: NAD83 / New York Long Island (ftUS)
hospitals <- health_sf_nyc %>%
filter(Short.Description == 'HOSP')
# Reproject data for leaflet
acsCovidData_4326 <- sf::st_transform(acsCovidData, 4326)
health_sf_nyc_4326 <- sf::st_transform(hospitals, 4326)
# Palettes for covid positive, elderly pop and asian pop
pal_covid_pos <- colorQuantile("YlOrRd", NULL, n = 5)
pal_elderly <- colorQuantile("YlGnBu",
domain = c(min(acsCovidData_4326$eldrlyP, na.rm = TRUE),
max(acsCovidData_4326$eldrlyP, na.rm = TRUE)),
alpha = TRUE)
pal_asian <- colorQuantile("Purples",
domain = c(min(acsCovidData_4326$asianPp, na.rm = TRUE),
max(acsCovidData_4326$asianPp, na.rm = TRUE)),
alpha = TRUE)
# Pop-up labels
covid_popup <- paste0(
"<strong>ZIP Code: </strong>",acsCovidData_4326$ZIPCODE,"<br/>",
"<strong>Place Name: </strong>",acsCovidData_4326$PO_NAME,"<br/>",
"<strong>Confirmed Cases: </strong>",acsCovidData_4326$Positiv,"<br/>",
"<strong>Total Tests: </strong>",acsCovidData_4326$Total)
elderly_popup <- paste0(
"<strong>ZIP Code: </strong>",acsCovidData_4326$ZIPCODE,"<br/>",
"<strong>Place Name: </strong>",acsCovidData_4326$PO_NAME,"<br/>",
"<strong>Elderly Pop (65+): </strong>",acsCovidData_4326$eldrlyP)
asian_popup <- paste0(
"<strong>ZIP Code: </strong>",acsCovidData_4326$ZIPCODE,"<br/>",
"<strong>Place Name: </strong>",acsCovidData_4326$PO_NAME,"<br/>",
"<strong>Asian Population: </strong>",acsCovidData_4326$asianPp)
hospital_popup <- paste0("<strong>Name: </strong>",health_sf_nyc_4326$Facility.Name,"<br/>",
"<strong>Address: </strong>",health_sf_nyc_4326$Facility.Address.1,"<br/>",
"<strong>Phone Number: </strong>",health_sf_nyc_4326$Facility.Phone.Number)
# Highlights and labels
polyHighlightOption <- leaflet::highlightOptions(opacity = 1.0, fillColor = "black")
polyLabelOption <- leaflet::labelOptions(opacity = 1.0, textsize = "11px")
# The Leaflet map
htmlMap <- leaflet(acsCovidData_4326) %>%
# re-center button at top left
addEasyButton(easyButton(
icon = "fa-crosshairs",
title = "Reset View",
onClick = JS("function(btn, map){ map.setView([40.70, -73.98], 10); }")
)) %>%
# Covid positive layer
addPolygons(
stroke = FALSE,
fillColor = ~pal_covid_pos(Positiv),
fillOpacity = 0.8, smoothFactor = 0.5,
popup = covid_popup,
label = paste0(
acsCovidData_4326$Positiv, " cases at ",
acsCovidData_4326$PO_NAME, ", ",
acsCovidData_4326$ZIPCODE),
highlightOptions = polyHighlightOption,
labelOptions = polyLabelOption,
group = "NYC-COVID"
) %>%
# Elderly Pop layer
addPolygons(
stroke = TRUE, color = "grey", weight = 1,
fillColor = ~pal_elderly(eldrlyP),
fillOpacity = 0.8,
popup = elderly_popup,
label = paste0("Elderly Pop: ", acsCovidData_4326$eldrlyP,
" | ", acsCovidData_4326$PO_NAME, ", ",
acsCovidData_4326$ZIPCODE),
highlightOptions = polyHighlightOption,
labelOptions = polyLabelOption,
group = "Elderly Population"
) %>%
# Asian Pop layer
addPolygons(
stroke = TRUE, color = "grey", weight = 1,
fillColor = ~pal_asian(asianPp),
fillOpacity = 0.8,
popup = asian_popup,
label = paste0(
"Asian Pop: ", acsCovidData_4326$asianPp,
" | ", acsCovidData_4326$PO_NAME, ", ",
acsCovidData_4326$ZIPCODE),
highlightOptions = polyHighlightOption,
labelOptions = polyLabelOption,
group = "Asian Population"
) %>%
# Hospital point layer
addCircleMarkers(
data = health_sf_nyc_4326,
radius = 4,
color = "blue",
fillOpacity = 0.7,
stroke = FALSE,
popup = hospital_popup,
label = paste0("Hospital Name: ", health_sf_nyc_4326$Facility.Name),
group = "Hospitals") %>%
# Basemaps
addTiles(group = "OSM") %>%
addProviderTiles("CartoDB.DarkMatter", group = "Carto") %>%
addProviderTiles("CartoDB.Positron", group = "Positron") %>%
# Legend Covid Positive - Bottom right
addLegend("bottomright",
pal = pal_covid_pos,
values = ~Positiv,
title = "COVID-19 Positive <br> Cases by ZIP Code",
group = "NYC-COVID") %>%
# Legend Elderly Pop - Bottom left
addLegend("bottomleft",
pal = pal_elderly,
values = ~eldrlyP,
title = "Elderly Population <br> (65+)",
group = "Elderly Population") %>%
# Legend Asian Pop - Top right
addLegend("topright",
pal = pal_asian,
values = ~asianPp,
title = "Asian Population",
group = "Asian Population") %>%
addLayersControl(
baseGroups = c("Positron", "OSM", "Carto"),
overlayGroups = c("NYC-COVID", "Hospitals", "Elderly Population", "Asian Population"),
options = layersControlOptions(collapsed = FALSE)
) %>%
hideGroup(c("Hospitals", "Elderly Population", "Asian Population"))
htmlMap