Combine multiple maps in R using ggplot2 - r

This is a reproductible example of an issue I am facing. I am trying to create maps with ggplot2 in multiple stages. Here is the issue I face.
Consider the data borderwith the polygons of states in US at the Mexican border, and border.countywith the polygons of the counties in these states. The following code allows you to get the data:
library(maps)
library(ggmap)
library(ggplot2)
USA <- get_googlemap(center = 'usa', zoom = 4,
style = 'administrative|element:labels|visibility:off')
us.df <- map_data("state")
border <- subset(us.df,
region %in% c("california","arizona","new mexico","texas"))
counties <- map_data("county")
border.county <- subset(counties,
region %in% c("california","arizona","new mexico","texas"))
Now I wan to create a map, with the background of a map from Google Maps, with the state polygons and the county borders. If I do the following, it works neatly:
Allmap <- ggmap(USA) +
geom_polygon(aes(x = long, y = lat, fill = region, group = group),
data=border, color = "white") +
geom_polygon(aes(x = long, y = lat, group = group),
data=border.county, fill=NA, color="red")
Now if I wanted to create this map in multiple stages, I hit problems. I just want the county boundaries for background information (as sort of 'recurrent theme'), and I will create multiple maps with changing information at the state level. So I create the 'background map' with counties, which works fine:
Countmap <- ggmap(USA) +
geom_polygon(aes(x = long, y = lat, group = group),
data=border.county, fill=NA, color="red")
And now I try to combine it with the state maps:
Statmap <- ggmap(USA) +
geom_polygon(aes(x = long, y = lat, fill = region, group = group),
data=border, color = "white") +
Countmap
That gives me the error:
Error: Don't know how to add o to a plot
How can I solve this? I can combine the maps in the other way (as in: Statmap <- Countmap + geom_polygon(aes(x = long, y = lat, fill = region, group = group), data=border, color = "white")); however, that puts the counties under the state boundaries.
I also know this specific problem has the easy solution of just drawing a map with the states first, and combine it with the counties in a second stage. However, in my real scenario, that is not an option because the recurrent theme of the map is something that needs to be drawn in second place : cities and geographic borders (like my county boundaries here).
This is the map I want to create:

If I understand your description correctly, you don't want to combine maps. You want to combine layers, specifically, to overlay the county outlines on changing state-level maps.
Try this:
# define county outlines as a geom_polygon layer
county.layer <- geom_polygon(aes(x = long, y = lat, group = group),
data = border.county, fill = NA, color = "red")
# add county.layer as the last layer to your state-level map
Statmap <- ggmap(USA) +
geom_polygon(aes(x = long, y = lat, fill = region, group = group),
data=border, color = "white") +
county.layer
Statmap
Edit in response to comment
If you have multiple county layers to plot, place them in a list:
border.county2 <- subset(counties, region %in% c("montana"))
layer2 <- list(geom_polygon(aes(x = long, y = lat, group = group),
data = border.county2, fill = NA, color = "blue"),
geom_polygon(aes(x = long, y = lat, group = group),
data = border.county, fill = NA, color = "red"))
Statmap <- ggmap(USA) +
geom_polygon(aes(x = long, y = lat, fill = region, group = group),
data=border, color = "white") +
layer2

Related

Can I make a map in R which shows two different categorical variables (e.g., income and region)?

I am trying to make a map of the U.S. which shows two categorical variables, for example the income group of the state and the region the state belongs in. The idea is to use the "fill" aesthetic to show the income level of each state, and then the "color" aesthetic to show the outlines of each region. The information that I am trying to communicate is that lower-income and higher-income states are clustered in certain regions.
An alternative would be to somehow show the regional boundaries with a bolder or thicker boundary than the state boundaries, but I am also unsure how to do this. Other ideas which communicate the same information would also be welcome.
Ideally, it would be some combination of the following two plots:
## Create map data
state_map_data <- map_data("state")
state_regions <- tibble(state_name = tolower(state.name), state.region,
as_tibble(state.x77)) %>%
mutate(income_cat = cut(Income, breaks = 3,
labels = c("low", "medium", "high")))
state_map_data <- state_map_data %>%
left_join(state_regions,
by = c("region" = "state_name"))
## Map with just income
p1 <- ggplot() +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
fill = income_cat))
print(p1)
This generates the following map with income
## Map with just regions
p2 <- ggplot() +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
color = state.region))
print(p2)
This generates the following map with regions
## Map with both
p <- ggplot() +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
fill = income_cat)) +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
color = state.region))
print(p)
This does not produce the expected results of a map with both a color outline by region and filled states by income as seen here
The way you have your code you are drawing two sets of polygons, with state.region polygons on top of the income_cat polygons. Instead, you want to draw one set of polygons with the correct outline color and fill color:
ggplot() +
geom_polygon(data = state_map_data,
aes(x = long, y = lat, group = group,
fill = income_cat, color = state.region)
)

Is there a polygon of world countries from -360 to 360 degrees in R?

I am kind of stuck on how to plot world maps that do not adjust to the map_data provide by the ggplot2 package, which is "world" (from -180 to 180 degrees) and "world2" (from 0 to 360 degrees). For example, if I want to plot from 100°E to 20°E, no polygon provide by ggplot2 is useful. Here is the code of the example:
library(ggplot2)
map1 <- map_data('world')
map2 <- map_data('world2')
ggplot() +
theme_bw() +
geom_polygon(data = map1,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
coord_fixed(1.4, xlim = c(100,380))
ggplot() +
theme_bw() +
geom_polygon(data = map2,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
coord_fixed(1.4, xlim = c(100,380))
So, are there world maps polygons like those of the example but with a greater range?
This is scrappy, but why not? You could show a duplicated world so that every region can be shown without going over the edge.
library(dplyr)
map1 <- map_data('world')
map2 <- map_data('world') %>% mutate(long = long+360)
map3 <- map_data('world') %>% mutate(long = long+720)
ggplot() +
theme_bw() +
geom_polygon(data = map1,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
geom_polygon(data = map2,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
geom_polygon(data = map3,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
coord_fixed(1.4, xlim = c(100,480))
Rather than finding those polygons, you might just clip the longitude boundaries with a coord_quickmap() or coord_sf(). Note that coord_map() will incorrectly group polygon's points unless clipped before the ggplot() call.
library(tidyverse)
ggplot() +
theme_bw() +
geom_polygon(data = map2,
mapping = aes(x = long,
y = lat,
group = group),
col = 'gray') +
coord_sf(xlim = c(20,100))
#coord_quickmap(xlim = c(100,20)) is also an alternative to coord_sf()
"world" and "world2" are two versions of the world map that are implemented in the "maps" package. But that package actually allows you to define a world map with any boundaries, e.g. c(20,380) or c(100,460).
The simplest way is just to use the "wrap" option (which is described in the man page of maps::map). This option is passed to maps::map() (so look in the maps package for full help page)
map3 <- map_data('world', wrap=c(20,380))
This should give you a nice map for any meridian that you may choose. Note that in this case "wrap" must be be a vector and that the left and right boundaries must be exactly 360 apart.

Mapping state borders with ggplot

I am attempting to create a ggplot map with both county and state boundaries. I am able to produce the map with county boundaries, but when adding the following line to get state borders as well, I run into an issue. The code is reproduced below.
ggplot2::geom_polygon(aes(x= long, y = lat, group = group), fill = NA, color = "black",
data = filter(us_map(), abbr %in% states_of_interest))
When I run this code, I get the error:
"Error in FUN(X[[i]], ...)
object 'long' not found". I am relatively unfamiliar with the us_map package, but I was told using long at lat for x and y would function correctly.
I have tried adding the command
"inherit.aes = FALSE"
upon other stack overflow post recommendations, but it did not solve the error.
The us_map function returns an object with x and y for lat/long. You can store the counties and states separately, then add them both to ggplot as polygons.
library(usmap)
states_of_interest <- c("CA","OR", "WA")
counties <- us_map(regions = "counties", include = states_of_interest)
states <- us_map(include = states_of_interest)
ggplot() +
geom_polygon(data = counties, aes(x = x, y = y, group = group), fill = NA, color = "black") +
geom_polygon(data = states, aes(x = x, y = y, group = group), fill = NA, color = "red") +
coord_equal()

How to fill a map from a shapefile using its own attributes table in R?

I see some people using R to fill maps from shapefiles, e.g. here. With my R knowledge, I only read the shapefile and plot it.
With QGIS I added an extra column to the attribute table called "OCCURENCE". This column is a year when a specific event occurred. I'd like to fill each country according to the year of occurence using a color scale, leaving the countries with no data without fill. The folder with the shapefiles is here. As example, I added some years to a few countries, and I like to obtain something like:
and a legend .
library(rgdal)
library(ggplot2)
World <- readOGR(dsn = "mundo", layer = "mundo")
class(World)
World2 <- fortify(World)
class(World2)
ggplot() +
geom_polygon(data = World2, aes(x = long, y = lat, group = group),
colour = "black", size = 0.5, fill = "white")
Any help??
The simple way, as #jazzurro said, is to work with sf package. But, you can achieve this with the method proposed by you adding a couple of steps.
You need to add an id field in SpatialPolygonsDataFrame and join these attributes with fortify() product. With this, you can fill polygons with any field:
library(rgdal)
library(ggplot2)
World <- readOGR(dsn = "mundo", layer = "mundo")
class(World)
World2 <- fortify(World)
class(World2)
World#data$id <- 0:(dim(World#data)[1]-1) # add id field
World2_join = plyr::join(x = World2,y = World#data, by="id") # join by id
ggplot() +
geom_polygon(data = World2_join, aes(x = long, y = lat, group = group, fill = OCCURENCE), # fill by OCCURENCE
colour = "black", size = 0.5)

How do you combine a map with complex display of points in ggplot2?

I'm trying to plot points from study sites with a background map of Africa. I can create the two independently, but I am having a hard time overlaying them on top of eachother.
The map of Africa I am using is an Esri shapefile from maplibrary.org. It is available from my dropbox at https://www.dropbox.com/s/etqdw3nky52czv4/Africa%20map.zip. I have the points in a text file, also available from my drop box. https://www.dropbox.com/s/scvymytjsr5pvaf/SPM-437-22Nov12.txt. They refer to studies on molecular drug resistance of malaria parasites. I would like to plot them so that the color is the proportion of parasites with the drug resistant genetic marker and the size is the number of parasites tested.
Plotting the points independently:
qplot(Longitude, Latitude, data = d.spm.437, colour = Frc437, size = Tot437)
Plotting the map of Africa:
library(maptools)
africa = readShapePoly("Africa.shp")
africa.map = fortify(africa, region="COUNTRY")
qplot(long, lat, data = africa.map, geom="path", group=group)
Any help on putting these two together while preserving the display of the points would be appreciated.
Try something like this. Seems to work for me. I think some of your lat-long coordinates are wrong though. The fill colour for geom_point is currently set to Tot437 so you might want to change that.
library(ggplot2)
library(rgdal)
africa <- readOGR("c:/test", layer = "Africa")
africa.map = fortify(africa, region="COUNTRY")
africa.points = read.table("c:/test/SPM-437-22Nov12.txt", header = TRUE, sep = ",")
names(africa.points)[which(names(africa.points) == 'Longitude')] <- 'long' # rename lat and long for consistency with shp file
names(africa.points)[which(names(africa.points) == 'Latitude')] <- 'lat'
ggplot(africa.map, aes(x = long, y = lat, group = group)) +
geom_polygon(colour = "black", size = 1, fill = "white", aes(group = group)) +
geom_point(data = africa.points, aes(x = long, y = lat, fill = Tot437, group = NULL), size = 4, shape = 21, colour = "black", size = 3)
Incidentally, looking at your map you may have difficulty getting a good detailed view of individual areas, so one way to tackle that would be by subsetting, in this case with the data frames. You could do this:
africa.map <- africa.map[africa.map$id == 'Madagascar', ]
africa.points <- africa.points[africa.points$Country == 'Madagascar', ]
ggplot(africa.map, aes(x = long, y = lat, group = group)) +
geom_polygon(colour = "black", size = 1, fill = "white", aes(group = group)) +
geom_point(data = africa.points, aes(x = long, y = lat, fill = Tot437, group = NULL), size = 2, shape = 21, colour = "black", size = 2)
...which should get you something similar to this:

Resources