11

So I am trying to create a Florida county-level map with borders based on a custom variable. I included an older version of the map that I am trying to create here

Essentially, the map shows a region breakdown of Florida counties, with media markets outlined with a bolded-black line border. I am able to plot the regions easily enough. What I am hoping to add is a bolder, black line border around outside of the regions defined by the media market variable "MMarket", similar to that of the map shown above. The fill variable would be Region and the media market border outline would be defined using MMarket. Here is how the data is read in and fortified:

#read in data
fl_data <- read_csv("Data for Mapping.csv")

#read in shapefiles
flcounties1 <- readOGR(dsn =".",layer = "Florida Counties")

#Fortify based on county name
counties.points <- fortify(flcounties1, region = "NAME")
counties.points$id <- toupper(counties.points$id)

#Merge plotting data and geospatial dataframe 
merged <- merge(counties.points, merged_data, by.x="id", by.y="County", all.x=TRUE)

The fl_data object contains the data to be mapped (including the media market variable) and the shapefile data is read into flcounties1. Here is a sample of the merged dataframe I'm using:

 head(merged %>% select(id:group, Region, MMarket))
       id      long      lat order  hole piece     group    Region     MMarket
1 ALACHUA -82.65855 29.83014     1 FALSE     1 Alachua.1 Panhandle Gainesville
2 ALACHUA -82.65551 29.82969     2 FALSE     1 Alachua.1 Panhandle Gainesville
3 ALACHUA -82.65456 29.82905     3 FALSE     1 Alachua.1 Panhandle Gainesville
4 ALACHUA -82.65367 29.82694     4 FALSE     1 Alachua.1 Panhandle Gainesville
5 ALACHUA -82.65211 29.82563     5 FALSE     1 Alachua.1 Panhandle Gainesville
6 ALACHUA -82.64915 29.82648     6 FALSE     1 Alachua.1 Panhandle Gainesville

I'm able to get a map of the region variable pretty easily using the following code: (here is a picture of the map)

ggplot() +
  # county polygons
  geom_polygon(data = merged, aes(fill = Region,
                                  x = long,
                                  y = lat,
                                  group = group)) +
  # county outline
  geom_path(data = merged, aes(x = long, y = lat, group = group), 
            color = "black", size = 1) +
  coord_equal() +
  # add the previously defined basic theme
  theme_map() +
  labs(x = NULL, y = NULL, 
       title = "Florida: Regions by County") +
  scale_fill_brewer(palette = "Set3",
                    direction = 1,
                    drop = FALSE,
                    guide = guide_legend(direction = "vertical",
                                         title.hjust = 0,
                                         title.vjust = 1,
                                         barheight = 30,
                                         label.position = "right",
                                         reverse = T,
                                         label.hjust = 0))
Louie Quicksell
  • 161
  • 1
  • 3
  • 10
  • does the data come in the format that you show to begin with or did you fortify it? If it first comes in sf or sp format (i.e. read from shape file or some other format) I think I could help using an approach that does not involve fortify – sebdalgarno Mar 27 '18 at 15:47
  • If you don't mind using `sf` and the github version of `ggplot2`, there's a `geom_sf` function for adding geographical shapes as a geom layer, and you could do this quite beautifully – camille Mar 27 '18 at 20:58
  • @sebdalgarno I edited the main post to show how the data was read in. `flcounties1` is in sp format and I fortified this initially using the county name as the id variable. When I added the media market variable to `flcounties1` and fortified using that variable as @FelipeAlvarenga outlined below, the map still contained the outlines of counties within the greater media markets. I'm trying to keep only the county outlines which create the outside border of the media markets, while removing all of the inner county outlines from within the media markets that contain them as in the first map. – Louie Quicksell Mar 27 '18 at 21:25
  • @Camille do you mind explaining in a little more detail how this would be done? I'm open to all suggestions! – Louie Quicksell Mar 27 '18 at 21:26
  • it's very hard to offer a solution without reproducible data unfortunately. – sebdalgarno Mar 28 '18 at 00:01

2 Answers2

11

Here's a quick example in case you want to get into sf with ggplot2::geom_sf. Since I don't have your shapefile, I'm just downloading the county subdivisions shapefile for Connecticut using tigris, and then convert it to a simple features object.

Update note: a few things seem to have changed with more recent versions of sf, such that you should now union the towns into counties with just summarise.

# download the shapefile I'll work with
library(dplyr)
library(ggplot2)
library(sf)

ct_sf <- tigris::county_subdivisions(state = "09", cb = T, class = "sf")

If I want to plot those towns as they are, I can use ggplot and geom_sf:

ggplot(ct_sf) +
  geom_sf(fill = "gray95", color = "gray50", size = 0.5) +
  # these 2 lines just clean up appearance
  theme_void() +
  coord_sf(ndiscr = F)

Grouping and calling summarise with no function gives you the union of several features. I'm going to unite towns based on their county FIPS code, which is the COUNTYFP column. sf functions fit into dplyr pipelines, which is awesome.

So this:

ct_sf %>% 
    group_by(COUNTYFP) %>% 
    summarise()

would give me a sf object where all the towns have been merged into their counties. I can combine those two to get a map of towns in the first geom_sf layer, and do the union for counties on the fly in the second layer:

ggplot(ct_sf) +
  geom_sf(fill = "gray95", color = "gray50", size = 0.5) +
  geom_sf(fill = "transparent", color = "gray20", size = 1, 
          data = . %>% group_by(COUNTYFP) %>% summarise()) +
  theme_void() +
  coord_sf(ndiscr = F)

No more fortify!

camille
  • 16,432
  • 18
  • 38
  • 60
  • Hello, I would like to do the same thing as you though when I try there is only the outline of the connecticut that appears ... On my personal map the same. Do you have an explanation to help me please? Thank you – thomas leon Jul 09 '19 at 16:59
  • @thomasleon it looks like some things in both `sf` and `tigris` have changed since then. Thanks for noticing, I'll update my answer shortly – camille Jul 09 '19 at 17:06
3

There are probably better ways of doing it, but my workaround is to fortify the data in all the dimensions you need to draw.

In your case I would create the fortified data sets of your counties and MMarkets and draw the map just like you did, but adding one more layer of geom_polygon without fillings, so to draw only the borders .

merged_counties   <- fortify(merged, region = "id")
merged_MMarket    <- fortify(merged, region = "MMarket")

and then

ggplot() +
  # county polygons
  geom_polygon(data = merged_counties, aes(fill = Region,
                                  x = long,
                                  y = lat,
                                  group = group)) +
# here comes the difference
geom_polygon(data = merged_MMarket, aes(x = long,
                                  y = lat,
                                  group = group),
                                  fill = NA, size = 0.2) +
  # county outline
  geom_path(data = merged, aes(x = long, y = lat, group = group), 
            color = "black", size = 1) +
  coord_equal() +
  # add the previously defined basic theme
  theme_map() +
  labs(x = NULL, y = NULL, 
       title = "Florida: Regions by County") +
  scale_fill_brewer(palette = "Set3",
                    direction = 1,
                    drop = FALSE,
                    guide = guide_legend(direction = "vertical",
                                         title.hjust = 0,
                                         title.vjust = 1,
                                         barheight = 30,
                                         label.position = "right",
                                         reverse = T,
                                         label.hjust = 0))

An example using a Brazilian shape file

brasil      <- readOGR(dsn = "path to shape file", layer = "the file")
brasilUF    <- fortify(brasil, region = "ID_UF")
brasilRG    <- fortify(brasil, region = "REGIAO")

ggplot() +
  geom_polygon(data = brasilUF, aes(x = long, y = lat, group = group), fill = NA, color = 'black')  +
  geom_polygon(data = brasilRG, aes(x = long, y = lat, group = group), fill = NA, color = 'black', size = 2) +
  theme(rect            = element_blank(), # drop everything and keep only maps and legend
        line            = element_blank(),
        axis.text.x     = element_blank(),
        axis.text.y     = element_blank(),
        axis.title.x    = element_blank()
  ) +
  labs(x = NULL, y = NULL) +
  coord_map()

enter image description here

Felipe Alvarenga
  • 2,572
  • 1
  • 17
  • 36
  • Just ran what you suggested, and it doesn't add the media market region borders, or really change the maps at all. What I am hoping to add is a bolder, black line border around outside of the regions defined by the media market variable "MMarket", similar to that shown in the first map included in my post. – Louie Quicksell Mar 26 '18 at 19:12
  • is your original data in `SpatialPolygon` format? because it works fine for me. I will add the steps to run it prior to fortifying the data. – Felipe Alvarenga Mar 26 '18 at 20:24
  • Can you provide the path to the shape file and the layer of the files in the Brazil example you provided? I ran what you edited to include, and the MMarket regions are not appearing, simply every county has a bold outline around it. I think the issue may be due to how MMarket and region variables are being added to the 'SpatialPolygon' dataframe, before it is fortified. – Louie Quicksell Mar 27 '18 at 04:09
  • Sure, just follow the above link to an example with the files https://drive.google.com/drive/folders/1RJaerMJqDzEtg8IejqSYG5VESxrKkEi5?usp=sharing – Felipe Alvarenga Mar 27 '18 at 13:15
  • I tried your example using the given shapefiles, and it worked as you said. Still can't seem to recreate it with my data though. Does the variable you pass to `fortify` have to be of a certain type? I am passing it a factor currently. – Louie Quicksell Mar 27 '18 at 14:23
  • It should not matter, in my example I did it with numeric variables, but the state variables were factors. Maybe your original data was already fortified? – Felipe Alvarenga Mar 27 '18 at 14:36