Нарисуйте SpatialPolygons с несколькими подполигонами и отверстиями, используя ggplot2

Я хотел бы нарисовать SpatialPolygons из библиотеки sp с отверстиями в ggplot2. Благодаря другим вопросам о stackoverflow, я знаю, что это разрешено при работе с написанными по часовой стрелке полигонами:
qaru.site/info/724353/...
Действительно, при преобразовании SpatialPolygons с использованием broom::tidy (заменив ggplot2::fortify), многоangularьники отверстий сохраняются в направлении по часовой стрелке, чтобы быть нарисованными как отверстия.
В ggplot2 способ рисования полигонов с отверстиями заставляет их рисовать один раз, используя fill, а другой раз, используя colour, в противном случае вы можете увидеть линии, пересекающие многоangularьники. При работе с несколькими подполигонами, некоторые с отверстиями, это более сложно, порядок точек, определенный в broom::tidy, может не допускать заполнения полигонов (см. изображение ниже).
У кого-нибудь из вас есть решение, чтобы избавиться от этой проблемы с заполнением?

Вот воспроизводимый пример:

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
(источник: statnmap.com)

Ответ 1

Хорошее время для "перехода" в пакет sf. Работа с объектом sf на самом деле намного проще в ggplot, благодаря геометрии geom_sf:

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)))

введите описание изображения здесь

Ответ 2

Добавление строк показывает источник проблемы. Рисуется синий "многоугольник", нижний → верхний → отверстие.

введите описание изображения здесь

Этот код (который не очень изящный, извините) возвращает путь к начальной точке первой части, прежде чем перейти к третьей.

 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),] 

введите описание изображения здесь

Ответ 3

Этот пост является хорошим вопросом и уже получил отличные ответы. Я также считаю, что люди должны научиться работать с объектами sf, поскольку это следующее поколение типа пространственных данных в R. Но я хочу поделиться тем, что в этом случае geom_spatial из пакета ggspatial может быть вариантом для построения 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)

введите описание изображения здесь