# Plotting islands in ggplot2

I'm trying to plot bodies of water on my map and struggling with islands in ggplot2. I understand the right/left-hand rule for exterior/interior rings but there is still a problem going from island to island. The question is how do I plot a polygon with lots of holes/islands in ggplot2? I believe the trick is order, but what order?

Here is the MWE I have built to try and understand and fix the problem:

``````library(ggplot2)

ids <- letters

# IDs and values to use for fill colour
values <- data.frame(
id = ids,
value = c(5)
)

# Example of good polygon and holes
good_positions <- data.frame(
id = rep(ids, each = 5),
#     shape        hole       hole       hole
x = c(1,10,10,1,1, 2,2,3,3,2, 7,7,8,8,7, 5,5,6,6,5 ),
y = c(1,1,10,10,1, 2,3,3,2,2, 7,8,8,7,7, 5,6,6,5,5)
)

# Example of good polygon and holes
bad_positions <- data.frame(
id = rep(ids, each = 5),
#     shape        hole       hole       hole
x = c(1,10,10,1,1, 2,2,3,3,2, 7,7,8,8,7, 5,5,6,6,5 ),
y = c(1,1,10,10,1, 2,3,3,2,2, 7,8,8,7,7, 3,4,4,3,3)
)

# Merge positions and values
good_datapoly <- merge(values, good_positions, by=c("id"))
bad_datapoly <- merge(values, bad_positions, by=c("id"))

# Plot polygons
good_plot <- ggplot(good_datapoly, aes(x=x, y=y)) +
geom_polygon(aes(group=id, fill=factor(value))) +
scale_fill_discrete("Key")

bad_plot <- ggplot(bad_datapoly, aes(x=x, y=y)) +
geom_polygon(aes(group=id, fill=factor(value))) +
scale_fill_discrete("Key")

good_plot
bad_plot
``````  ## 1 Answer

It looks like @rcs had the right idea, although generifying that into a function is a little more difficult. This blog post suggests that the 'easy way' is to use the non-CRAN package ggspatial (which looks like it has a `geom_spatial()` for use with `Spatial*` objects), but also contains the following non-package-ified function for use with `SpatialPolygons` objects (that you could probably apply to any example without worrying about coordinate order):

``````library(ggplot2)
library(dplyr)

fixfeature <- function(df) {
ringstarts <- which(!duplicated(df\$group))
if(length(ringstarts) < 2) {
return(df)
} else {
ringstarts <- c(ringstarts, nrow(df))
indicies <- c(1:(ringstarts-1), do.call(c, lapply(2:(length(ringstarts)-1), function(x) {
c(1, ringstarts[x]:(ringstarts[x+1]-1))
})), nrow(df))
return(df[indicies,])
}
}

custom_fortify <- function(x, ...) {
df <- fortify(x, ...)
df %>% group_by(id) %>% do(fixfeature(.))
}
``````

The example from the blog post uses data from the ggspatial package to illustrate this:

``````devtools::install_github("paleolimbot/ggspatial")
library(ggspatial)
data(longlake_waterdf)

spdf <- longlake_waterdf[is.na(longlake_waterdf\$label),]
ggplot(spdf, aes(x=long, y=lat)) + geom_polygon(aes(group=id)) # doesn't work
`````` ``````ggplot(custom_fortify(spdf), aes(x=long, y=lat)) +
geom_polygon(aes(group=id)) # does work
`````` ``````ggplot() + geom_spatial(spdf) # produces the same as above
`````` 