6

I am working on highlighting groups of counties within Ohio and while I am happy with where I have ended up overall, I have one aesthetic issue I cannot seem to solve.

Goal: Map of Ohio with grouped counties filled white with red outer border around group, and ungrouped counties in gray.

Here's a clip of what the issue looks like on a corner of my full map.
Issue

I am trying to get rid of that extra border that is outside of the border of Ohio on groups of counties. The red line inside the groups of counties is great and it doesn't have the outside border problem with the grayed-out counties because I put the "nogroup" filled counties after the others in the code. Based on that, I initially thought it was an order of operations issue in the ggplot2 code, but I tried multiple permutations to no avail.

I'll give a smaller dataset below to replicate the issue, but it's worth noting that I actually have 25 groups of counties and therefore filling the groups with different colors doesn't work because you wouldn't be able to get enough colors that can easily be differentiated (Unless someone has code that allocates colors to polygons based on contrast with adjacent colors? Then you could use a more limited palette and repeat colors across the geographic area...).

# access data 
if (!exists(hasRun)) {
  OHstate = tigris::states(cb=TRUE, class='sf') |> subset(NAME=='Ohio')
  OHcounties = tigris::counties(state='OH', cb=TRUE, class ='sf')
  hasRun=TRUE
}

# make groups
group1 = OHcounties |> subset(GEOID %in% c(39055, 39085, 39007))
group2 = OHcounties |> subset(GEOID %in% c(39103, 39153))
nogroup = OHcounties |> subset(GEOID %in% c(39129, 39073, 39141, 39047, 39065, 
                                            39091, 39139, 39029, 39151, 39035, 
                                            39093, 39033, 39175, 39161, 39075, 
                                            39009, 39087, 39001, 39135))

and plot

library(ggplot2)
map <- ggplot() +
  geom_sf(data = st_union(group1), color = "#C12637", linewidth = 3, fill = "white") +
  geom_sf(data = st_union(group2), color = "#C12637", linewidth = 3, fill = "white") +
  geom_sf(data = OHcounties, color = "black", fill = NA) + 
  geom_sf(data = nogroup) +
  geom_sf(data = OHstate, color = "black", linewidth = 1, fill = NA) +
  scale_fill_identity() +
  theme_void()

map 

2 Answers 2

2

One way to do this is creating new polygons of grouped counties that are slightly smaller than their originals, then plotting them alongside all the other polygons. There may be another aesthetics plugin that can solve this, but I sadly could not find anything in my search.

In your reproducible code chunk, your red buffer does also bleed into grey counties. Creating an inset buffer will also mitigate this issue.

Using st_buffer, we make a slightly smaller version of your grouped counties (I used dist=-0.02, which in my opinion, produced the most visually appealing result. After loading relevant packages and creating groups, do the following:

# Load relevant packages
library(pacman)
pacman::p_load(tidyverse, ggplot2, tigris, maps, mapdata, sf)

# Pull state and county data
OHstate <- states(cb = TRUE, class = "sf") %>% filter(NAME == "Ohio")
OHcounties <- counties(state="OH", cb=TRUE, class ="sf")

# Make groups
group1 <- OHcounties %>% filter(GEOID %in% c(39055, 39085, 39007))
group2 <- OHcounties %>% filter(GEOID %in% c(39103, 39153))
nogroup <-  OHcounties %>% filter(GEOID %in% c(39129, 39073, 39141, 39047, 39065, 39091, 39139, 39029, 39151, 39035, 39093, 39033, 39175, 39161, 39075, 39009, 39087, 39001, 39135))

#An inset buffer within the grouped counties
smallgroup1 <- st_buffer(st_union(group1), dist=-0.02)
smallgroup2 <- st_buffer(st_union(group2), dist=-0.02)

map <- ggplot() +
  geom_sf(data = nogroup) +
  geom_sf(data = st_union(group1), color = "black", fill = "white") +
  geom_sf(data = st_union(group2), color = "black", fill = "white") +
  geom_sf(data = OHcounties, color = "black", fill = NA) +

# Making a separate geometry for these inset buffers, I altered the line width from 3 to 1.5 to prevent bleeding past the county boundary

  geom_sf(data = smallgroup1, color = "#C12637", linewidth=1.5, fill="NA") +
  geom_sf(data = smallgroup2, color = "#C12637", linewidth=1.5, fill="NA") +

#I also made the state boundary thinner, so that the inset red borders would still show.

  geom_sf(data = OHstate, color = "black", linewidth = 1, fill = NA) +
  scale_fill_identity() +
  theme_void()

map 
Sign up to request clarification or add additional context in comments.

Comments

0

Another approach is to create a polygon that has the exact shape of your "background" and make it white.

First, get the data.

library(sf)
#> Linking to GEOS 3.13.0, GDAL 3.8.5, PROJ 9.5.1; sf_use_s2() is TRUE
library(ggplot2)
library(tigris)
#> To enable caching of data, set `options(tigris_use_cache = TRUE)`
#> in your R script or .Rprofile.

  OHstate = tigris::states(cb = TRUE, class = 'sf') |> subset(NAME == 'Ohio')
#> Retrieving data for the year 2024
  OHcounties = tigris::counties(state = 'OH', cb = TRUE, class = 'sf')
#> Retrieving data for the year 2024

group1 = OHcounties |> subset(GEOID %in% c(39055, 39085, 39007))
group2 = OHcounties |> subset(GEOID %in% c(39103, 39153))
nogroup = OHcounties |> subset(GEOID %in% c(39129, 39073, 39141, 39047, 39065, 
                                          39091, 39139, 39029, 39151, 39035, 
                                          39093, 39033, 39175, 39161, 39075, 
                                          39009, 39087, 39001, 39135))

Then, create a big square that contains all the states and make a polygon that is the difference between this "big box" and the boundary of the states. The result is the red polygon below.

# Group all counties to get the outer boundary
grouped_counties <- OHcounties |>
  dplyr::summarise(geometry = st_union(geometry))

# Create a bounding box around the counties
bbox <- st_bbox(grouped_counties)
# Extended a little further just to make sure
bbox <- bbox + c(-0.1, -0.1, 0.1, 0.1)
bbox <- st_as_sfc(bbox)
# Define the polygon as difference between the bbox and the counties
panel_bg <- st_difference(bbox, grouped_counties)

# Just to get a feel for what's going on
ggplot() +
  geom_sf(data = grouped_counties) +
  # The panel_bg shape is everything outside the counties
  geom_sf(data = panel_bg, fill = "red", color = NA)

enter image description here

Finally, add this polygon (panel_bg) to your plot and make the same color as your background.

# Then, just make it all white (or same color as your background)
ggplot() +
  geom_sf(
    data = st_union(group1),
    color = "#C12637",
    linewidth = 3,
    fill = "white"
  ) +
  geom_sf(
    data = st_union(group2),
    color = "#C12637",
    linewidth = 3,
    fill = "white"
  ) +
  geom_sf(data = OHcounties, color = "black", fill = NA) +
  geom_sf(data = nogroup) +
  geom_sf(data = OHstate, color = "black", linewidth = 1, fill = NA) +
  geom_sf(data = panel_bg, fill = "white", color = NA) +
  scale_fill_identity() +
  theme_void()

enter image description here

Created on 2025-12-04 with reprex v2.1.1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.