Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw SpatialPolygons with multiple subpolygons and holes using ggplot2

I would like to draw SpatialPolygons from library sp with holes in ggplot2. Thanks to others questions on stackoverflow, I know that this is allowed while dealing with clockwise written polygons:
http://stackoverflow.com/questions/12047643/geom-polygon-with-multiple-hole/12051278#12051278
Indeed, when transforming a SpatialPolygons using broom::tidy (replacing ggplot2::fortify), holes polygons are saved in clockwise direction to be drawn as holes.
In ggplot2, the way polygons with holes are drawn force to draw them once using fill, and another time using colour, otherwise you may see lines crossing polygons. When dealing with multiple subpolygons, some with holes, this is more tricky, the order of points features as defined by broom::tidy may not allow for filling polygons (see image below).
Do any of you have a solution to get rid of this filling problem behaviour ?

Here is a reproducible example:

library(sp)
library(ggplot2)

# Create two polygons: second would be a hole inside the first
xy = cbind(
  x = c(13.4, 13.4, 13.6, 13.6, 13.4),
  y = c(48.9, 49, 49, 48.9, 48.9)
    )
hole.xy <- cbind(
  x = c(13.5, 13.5, 13.45, 13.45, 13.5),
  y = c(48.98, 48.92, 48.92, 48.98, 48.98)
  )

# Transform as SpatialPolygons with holes
xy.sp <- SpatialPolygons(list(
  Polygons(list(Polygon(xy),
                Polygon(hole.xy, hole = TRUE)), "1"),
  Polygons(list(Polygon(xy + 0.2),
                Polygon(xy + 0.35),
                Polygon(hole.xy + 0.2, hole = TRUE)), "2")
  ))

# Transform SpatialObject to be used by ggplot2
xy.sp.l <- broom::tidy(xy.sp)

ggplot(xy.sp.l) +
  geom_polygon(aes(x = long, y = lat, group = id, fill = id))

ggplot fill problem with SpatialPolygons with holes
(source: statnmap.com)

like image 257
Sébastien Rochette Avatar asked May 23 '17 16:05

Sébastien Rochette


3 Answers

Could be a good time to "go over" to the sf package. Working with sf object is in fact much easier in ggplot, thanks to the geom_sf geometry:

library("sf")
library("rgeos")
sf_poly <- as(xy.sp, "sf")
sf::st_crs(sf_poly) <- 4326
sf_poly$id <- c(1,2)
ggplot(sf_poly) +
  geom_sf(aes(fill = as.factor(id)))

enter image description here

like image 66
lbusett Avatar answered Oct 23 '22 03:10

lbusett


Adding lines shows the source of the problem. The blue "polygon" is being drawn, lower -> upper -> hole.

enter image description here

This code (which is not very elegant, sorry) makes the path go back to the starting point of the first piece before proceeding to 3rd.

 library(dplyr)
    extra <- xy.sp.l %>%
        filter(piece != 1) %>%
        group_by(id, group) %>%
        summarise(last_pt = max(order))


for (n in 1:nrow(extra)) {
    id_ex <- as.character(extra[n,"id"])
    x <- subset(xy.sp.l, id == id_ex & piece == 1 & order == 1)
    x$order <- as.numeric(extra[n,"last_pt"]) + 0.5
    xy.sp.l <- rbind(xy.sp.l,x)
}

xy.sp.l <- xy.sp.l[order(xy.sp.l$id, xy.sp.l$order),] 

enter image description here

like image 22
Jeremy Voisey Avatar answered Oct 23 '22 03:10

Jeremy Voisey


This post is a good question and already received great answers. I also believe people should learn how to work with sf objects as it is the next generation of the spatial data type in R. But I want to share that in this case geom_spatial from the ggspatial package could be an option to plot the SpatialPolygons.

library(sp)
library(ggplot2)
library(ggspatial)

ggplot() +
  geom_spatial(xy.sp, aes(fill = id))
# Ignoring argument 'mapping' in geom_spatial.SpatialPolygons
# Autodetect projection: assuming lat/lon (epsg 4326)

enter image description here

like image 1
www Avatar answered Oct 23 '22 02:10

www