Maps

April 14, 2026 (II)

Map-making

There are a variety of tools, within and external to R. Some cost money, others are free. We will all too briefly look at three mechanisms: base1/elder R functions, ggplot2 functions, and leaflet. We will show some caution and give up and move on when the time seems right. To get started we will attempt to load, or first install a few packages. One in particular, mapdata, is out of date and no longer maintained. Most importantly this will be a place to relatively simply explore basic map ideas. We will will move on pretty quickly.

Listing 1: This is a pretty clever way to install necessary packages on the fly if you are collaborating and a collaborator might not have the packages or you switch between computers or perform updates and have to reinstall packages.
if(!"mapdata"%in%installed.packages()){install.packages("mapdata")}

We load the packages, including ggplot2, if not somewhat prematurely.

library(maps)
library(mapproj)
library(mapdata)
library(ggplot2)

In class we examined each line of Figure 1 in isolation, but show the final result here to save space.

par(mfrow = c(2, 2), mar = rep(0, 4))
map()
map(database = "state")
map(database = "county")
map(database = "county", region = "oklahoma")
A world; a United States, lower-48 state map; a United States, lower-48 county map; and an Oklahoma state map with counties.
Figure 1: A world; a United States, lower-48 state map; a United States, lower-48 county map; and an Oklahoma state map with counties.

Presumably this is only useful at the World map-scale, but you can set the borders of the map so long as the total coverage is \(360^{\circ}\).

par(mfrow = c(2, 2), mar = rep(0, 4))
map(); map.axes(las = 1)
map(wrap = c(0, 360)); map.axes(las = 1)
map(wrap = c(-90, 270)); map.axes(las = 1)
map(wrap = c(-180, 180)); map.axes(las = 1)
A world map shown with _left_ and _right_ boundaries at different positions.
Figure 2: A world map shown with left and right boundaries at different positions.

Using the location of the Oklahoma State Capitol Building, in Figure 3 we annotate the map with a black dot, mainly to show the compatibility with base R graphing features. The coordinates of its location were converte from \(\text{D}^\circ\text{M}'\text{S}"\) to $ + + $, with a good guess on the sign having looked at the results of map.axes(las = 1) and interpreting “W” as negative signed.

map(database = "county", region = "Oklahoma"); map.axes(las = 1)
points(-(97 + 30/60 + 11/3600), 35 + 29/60 + 32/3600, pch = 19)
An Oklahoma State County map with a point plotted at the location of the Oklahoma State Capitol Building.
Figure 3: An Oklahoma State County map with a point plotted at the location of the Oklahoma State Capitol Building.

Drink data

Given those basic maps, and though curious, we avoided making filled chloropleth or cartogram maps, deferring to something more easily done with ggplot2.

dat <- read.delim("../data/drink-names.csv")
head(dat)
          Region    pop   soda  coke other  Total Percent
1          Total 157659 164145 58490 21120 401414  100.00
2        Alabama    153    582  2849   665   4249    1.06
3         Alaska    324    636    60    92   1112    0.28
4        Alberta   2185     69    55    48   2357    0.59
5 American Samoa      8     11     1    40     60    0.01
6        Arizona    586   2799   437   174   3996    1.00
dat <- dat[-1, ]
head(dat)
          Region  pop soda coke other Total Percent
2        Alabama  153  582 2849   665  4249    1.06
3         Alaska  324  636   60    92  1112    0.28
4        Alberta 2185   69   55    48  2357    0.59
5 American Samoa    8   11    1    40    60    0.01
6        Arizona  586 2799  437   174  3996    1.00
7       Arkansas  154  347 1442    80  2023    0.50

Having read in survey data for preferred beverage nomenclature, we make a quick plot in

ggplot(dat, mapping = aes(x = Percent, y = Region)) + geom_point()
A busy dotplot showing the percent of beverage survey respondents within the given geographic region with regions from the US and Canada. Percents range from 0 to 6 and geographic regions ordered alphabetically.
Figure 4: A hectic dotplot showing the percent of beverage survey respondents within the given geographic region.
ggplot(data = subset(dat, Percent > 1), mapping = aes(x = Percent, y = Region)) + geom_point()
A busy dotplot showing the percent of beverage survey respondents within the given geographic region with regions from the US and Canada. Percents range from 0 to 6 and geographic regions ordered alphabetically.
Figure 5: A dotplot showing the percent of beverage survey respondents within the given geographic region, only data in excess of one percent are shown.

Finally, in Figure 6 the region labels are ordere from highest to lowest in terms of percent. While it’s harder to infer geographic relationships, it is easier to identify regions with similar values.

ggplot(data = subset(dat, Percent > 1), mapping = aes(x = Percent, y = reorder(Region, Percent))) + geom_point()
A busy dotplot showing the percent of beverage survey respondents within the given geographic region with regions from the US and Canada. Percents range from 0 to 6 and geographic regions ordered alphabetically.
Figure 6: A hectic dotplot showing the percent of beverage survey respondents within the given geographic region.

While the idea above was that not all data with a spatial interpretation need be shown spatially, we will now combine that value data with map data to generate informative color schemes. First we load the map data using the ggplot2 function map_data().

usStates <- map_data("state")
head(usStates, n = 2)
       long      lat group order  region subregion
1 -87.46201 30.38968     1     1 alabama      <NA>
2 -87.48493 30.37249     1     2 alabama      <NA>
head(usStates)
       long      lat group order  region subregion
1 -87.46201 30.38968     1     1 alabama      <NA>
2 -87.48493 30.37249     1     2 alabama      <NA>
3 -87.52503 30.37249     1     3 alabama      <NA>
4 -87.53076 30.33239     1     4 alabama      <NA>
5 -87.57087 30.32665     1     5 alabama      <NA>
6 -87.58806 30.32665     1     6 alabama      <NA>
head(usStates[usStates$region == "michigan", ])
          long      lat group order   region subregion
6193 -90.41273 46.55855    23  6215 michigan     north
6194 -90.37836 46.56428    23  6216 michigan     north
6195 -90.31534 46.59293    23  6217 michigan     north
6196 -90.28096 46.61584    23  6218 michigan     north
6197 -90.20075 46.63303    23  6219 michigan     north
6198 -90.13772 46.64450    23  6220 michigan     north

The data stored in usStates contains latitude, longitude, group, order, region (i.e., ‘state name’), and subregion. All points are plotted in Figure 7. A neat opportunity would be to add col = group or col = order.

plot(lat ~ long, usStates, pch = 19, cex = 0.1)
Points are along state boundaries and plot out a pointilist map of the Lower 48 United States.
Figure 7: A scatterplot of points that happen to lay along state boundaries..

At this point we make the transition to ggplot2 graphics. In this case the last line guides(fill = "none") supresses the bringing of the state-by-state color legend. Later, when color becomes more meaningful we will have to remember to remove that. Removing the referenced line above from the code prints a surprising legend with a finely-resolved color palette.

ggplot(data = usStates, 
       mapping = aes(x = long, y = lat,  
       group = group, fill = region)) + 
       geom_polygon(color = "gray90") + 
       guides(fill = "none")
A basic ggplot map of the Lower 48 United States with brightly-colored states.
Figure 8: A basic ggplot map of the Lower 48 United States with brightly-colored states.
ggplot(data = usStates, 
  mapping = aes(x = long, y = lat,  
  group = group, fill = region)) + 
  geom_polygon(color = "gray90") + 
  coord_map(projection = "albers", lat0 = 39, lat1 = 45) + 
  guides(fill = FALSE)
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
of ggplot2 3.3.4.
A basic ggplot map of the Lower 48 United States with brightly-colored states, but made using the Albers projection which is described as standard for the region..
Figure 9: A basic ggplot map of the Lower 48 United States with brightly-colored states made with the Albers projection.

Things start to get more interesting with a rationale for coloring states (or relevant regions) with colors indicative of another variable as in Figure 10. This can certainly be done with tidyverse syntax, perhaps even more easily, but I will use base R commands for this because that is way easier for me. We first convert the state names in the survey data to lowercase (note the corresponding variable name Region remains uppercase). Then with merge we tack the survey results on, state-by-state, repeating rows as necessary. From this, in Figure 10, we can derive color schemes from values of the numerical variables.

dat$Region <- tolower(dat$Region)
usStates <- merge(usStates, dat, by.x = "region", by.y = "Region")
ggplot(data = usStates, 
  mapping = aes(x = long, y = lat,  
  group = group, fill = Percent)) + 
  geom_polygon(color = "gray90", linewidth = 0.1, show.legend = TRUE) + 
  coord_map(projection = "albers", lat0 = 39, lat1 = 45)
A basic ggplot map of the Lower 48 United States with states colored indicating the value of the variable Percent. States with the highest percents (around 6) include California, Texas, and most of the Great Lakes states.
Figure 10: A basic ggplot map of the Lower 48 United States with states colored indicating the value of the variable Percent.

There are a lot of colors in the interior states which are only slightly different shades. We can bin values using the cut() function and color by these binned values. With fewer values and shades to resolve visually, the bigger differences should be clearer, as demonstrated in Figure 11. It would be worth reducing the complexity of the cutpoints to c(0, 2, 4, 6, 100). With our earlier work we could certainly improve or remove some of the labels or change the color scheme.

usStates$bin <- as.numeric(cut(usStates$Percent, c(0, 1, 2, 3, 4, 5, 100)))
ggplot(data = usStates, 
  mapping = aes(x = long, y = lat,  
  group = group, fill = bin)) + 
  geom_polygon(color = "gray90", linewidth = 0.1, show.legend = TRUE) + 
  coord_map(projection = "albers", lat0 = 39, lat1 = 45)
A basic ggplot map of the Lower 48 United States with states colored indicating the value of the variable Percent. States with the highest percents (around 6) include California, Texas, and most of the Great Lakes states.
Figure 11: A basic ggplot map of the Lower 48 United States with states colored indicating the value of the variable Percent.

Footnotes

  1. I guess really “base” in terms of older graphics platform.↩︎