简体   繁体   中英

How can I use geom_sf to plot a polygon with multiple holes from coordinates?

I have objects with multiple holes. I can plot them successfully with geom_polypath and some geometry identification courtesy of Z.Lin . See geom_polypath(aes(x = x, y = y, group = section)) below.

I with to use geom_sf to plot these same objects. It is not clear to me how to maintain this geometry information when using sf objects.

The documentation for st_cast states :

ids integer vector, denoting how geometries should be grouped (default: no grouping)

Is that a clue? It's still not clear to me.

The xml file for the letter 'g' is here .

library(tidyverse)
library(transformr)
library(ggpolypath)
library(sf)

get_letter <- function(){
  letter_xml <- readRDS("tmp/letter_g")

  # Extract coordinates from Picture object
  x <- letter_xml@paths$text@letters[1]$path@x
  y <- letter_xml@paths$text@letters[1]$path@y

  one_letter <- tibble(
    x, 
    y,
    x.n = names(x),
    id = 1
  )

  one_letter <- one_letter %>%
    mutate(is.move = x.n == "move") %>%
    mutate(section = cumsum(is.move)) %>%

    group_by(section) %>%
    mutate(section.length = n()) %>%
    ungroup() %>%
    filter(section.length >= 3)

  one_letter <- select(one_letter, x, y, id, section)
}

letter_to_sf <- function(one_letter){
  one_letter_sf <- one_letter %>%
    st_as_sf(coords = c("x", "y")) %>%
    summarise(geometry = st_combine(geometry)) %>%
    st_cast(to = "POLYGON") %>% 
    st_normalize()

  df2 <- data.frame(
    id = 1,
    one_letter_sf$geometry
  )
}
one_letter <- get_letter()
one_letter_sf <- letter_to_sf(one_letter)

# Incorrect using xy df
ggplot(one_letter) +
  geom_polygon(aes( x = x, y = y))

# Correct using xy df
ggplot(one_letter) +
  geom_polypath(aes(x = x, y = y, group = section))

# Incorrect using sf
ggplot(one_letter_sf) +
  geom_sf(aes(geometry = geometry))

This is a different approach that I feel is less clunky. We have to abandon using the st_as_sf(coords = ) built in method, but given that we have raw points I think this is more warranted. We use the fact that st_polygon takes a list of matrices of points, where subsequent matrices after the first are the holes. So we can simply split the original dataframe by section (you could use base::split too), iterate over the groups and coerce to matrix, putting the results to a list, and directly pass the result to st_polygon . You can then wrap this POLYGON geometry ( sfg ) inside an sfc and then an sf object to get back the dataframe style object that we can then ggplot .

library(tidyverse)
library(sf)
#> Linking to GEOS 3.7.1, GDAL 2.4.0, PROJ 5.2.0

one_letter <- tibble::tibble(
  x = c(317.422, 315.605, 310.16, 304.367, 300.527, 299.141, 299.141, 299.141, 299.797, 301.297, 301.719, 300.805, 298.199, 295.684, 294.172, 293.672, 293.672, 293.672, 294.172, 295.684, 298.199, 300.805, 301.719, 300.684, 297.75, 294.93, 293.246, 292.688, 292.688, 292.688, 294.367, 299.203, 306.891, 314.566, 317.125, 319.695, 327.41, 335.234, 340.207, 341.953, 341.953, 341.953, 340.152, 334.797, 325.941, 316.715, 313.641, 312.145, 307.656, 303.695, 301.5, 300.828, 300.828, 300.828, 301.121, 301.906, 303.047, 304.066, 304.406, 305.078, 306.82, 307.094, 308.059, 312.82, 317.008, 318.406, 320.199, 325.586, 331.316, 335.109, 336.484, 336.484, 336.484, 336.016, 334.609, 332.25, 329.828, 328.938, 328.953, 329.332, 330.355, 332.008, 333.723, 334.297, 334.863, 336.563, 338.102, 338.375, 338.004, 336.723, 336.188, 336.188, 336.188, 336.516, 337.395, 338.664, 339.793, 340.172, 340.664, 342.148, 343.793, 344.91, 345.328, 345.328, 345.328, 344.5, 342.234, 338.832, 335.664, 334.609, 333.41, 329.813, 326.332, 324.152, 323.328, 323.281, 322.734, 318.75, 317.422, 317.422, 317.719, 318.82, 322.137, 325.664, 327.996, 328.844, 328.844, 328.844, 328.023, 325.723, 322.172, 318.75, 317.609, 316.52, 313.258, 309.871, 307.672, 306.891, 306.891, 306.891, 307.727, 310.031, 313.469, 316.656, 317.719, 317.719, 317.813, 319.559, 324.809, 330.023, 333.281, 334.406, 334.406, 334.406, 333.215, 329.797, 324.387, 319.008, 317.219, 315.516, 310.41, 305.215, 301.898, 300.734, 300.734, 300.734, 301.906, 305.289, 310.66, 316.023, 317.813, 317.813),
  y = c(8101.36, 8101.36, 8100.18, 8096.9, 8091.93, 8087.22, 8085.66, 8084.56, 8081.29, 8078.09, 8077.52, 8077.23, 8075.97, 8073.87, 8071.21, 8068.79, 8067.98, 8067.23, 8064.98, 8062.39, 8060.21, 8058.79, 8058.44, 8058.05, 8056.45, 8053.89, 8050.75, 8047.95, 8047.02, 8045.46, 8040.79, 8036.11, 8033.15, 8032.13, 8032.13, 8032.13, 8033.22, 8036.35, 8041.29, 8046.18, 8047.81, 8049.44, 8054.32, 8059.05, 8061.93, 8062.91, 8062.91, 8062.91, 8063.15, 8063.99, 8065.55, 8067.38, 8067.98, 8068.39, 8069.6, 8070.93, 8071.82, 8072.16, 8072.16, 8072.16, 8071.58, 8071.45, 8071.03, 8069.53, 8068.88, 8068.88, 8068.88, 8070.09, 8073.45, 8078.52, 8083.29, 8084.88, 8085.88, 8088.91, 8092.53, 8095.71, 8097.88, 8098.47, 8099.19, 8101.34, 8103.39, 8104.62, 8105.03, 8105.03, 8105.03, 8104.52, 8103.37, 8103.05, 8102.77, 8101.41, 8100.18, 8099.77, 8099.41, 8098.35, 8097.18, 8096.39, 8096.09, 8096.09, 8096.09, 8096.54, 8097.78, 8099.61, 8101.3, 8101.86, 8102.7, 8105.23, 8107.9, 8109.66, 8110.3, 8110.3, 8110.3, 8109.68, 8107.85, 8104.8, 8101.63, 8100.56, 8100.69, 8101.36, 8101.36, 8101.36, 8094.8, 8094.8, 8094.05, 8092, 8088.89, 8085.95, 8084.97, 8084.01, 8081.13, 8078.12, 8076.14, 8075.44, 8075.44, 8075.44, 8076.13, 8078.1, 8081.17, 8084.17, 8085.17, 8086.12, 8088.97, 8092.03, 8094.06, 8094.8, 8094.8, 8094.8, 8056.27, 8056.27, 8055.66, 8053.93, 8051.15, 8048.35, 8047.42, 8046.52, 8043.83, 8041.07, 8039.3, 8038.67, 8038.67, 8038.67, 8039.27, 8041, 8043.75, 8046.5, 8047.42, 8048.34, 8051.1, 8053.89, 8055.65, 8056.27, 8056.27, 8056.27),
  id = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
  section = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L)
)

one_letter %>%
  group_by(section) %>%
  group_split() %>%
  map(~ select(.x, x, y) %>% as.matrix) %>%
  st_polygon() %>%
  st_sfc() %>%
  st_sf() %>%
  ggplot() +
  geom_sf()

Created on 2019-03-04 by the reprex package (v0.2.1)

Here's my first approach, though it feels a bit clumsy. I downloaded letter_g and made a reprex version of one_letter , so you can work with that. The first thing to do is to get a polygon for the boundary and each hole by grouping by section, but then it is a bit convoluted to erase the holes from the boundary polygon. I do it here by using st_intersection and keeping only the resulting polygon that originates entirely from the boundary. I'm also assuming that the section variable is numbered properly so that the boundary is the first set of points; if we don't know which is supposed to be the boundary I don't know how you can get the right holes.

I am not sure if this method is better than manually converting the points into the format for the st_polygon constructor.

library(tidyverse)
library(sf)
#> Linking to GEOS 3.7.1, GDAL 2.4.0, PROJ 5.2.0

one_letter <- tibble::tibble(
  x = c(317.422, 315.605, 310.16, 304.367, 300.527, 299.141, 299.141, 299.141, 299.797, 301.297, 301.719, 300.805, 298.199, 295.684, 294.172, 293.672, 293.672, 293.672, 294.172, 295.684, 298.199, 300.805, 301.719, 300.684, 297.75, 294.93, 293.246, 292.688, 292.688, 292.688, 294.367, 299.203, 306.891, 314.566, 317.125, 319.695, 327.41, 335.234, 340.207, 341.953, 341.953, 341.953, 340.152, 334.797, 325.941, 316.715, 313.641, 312.145, 307.656, 303.695, 301.5, 300.828, 300.828, 300.828, 301.121, 301.906, 303.047, 304.066, 304.406, 305.078, 306.82, 307.094, 308.059, 312.82, 317.008, 318.406, 320.199, 325.586, 331.316, 335.109, 336.484, 336.484, 336.484, 336.016, 334.609, 332.25, 329.828, 328.938, 328.953, 329.332, 330.355, 332.008, 333.723, 334.297, 334.863, 336.563, 338.102, 338.375, 338.004, 336.723, 336.188, 336.188, 336.188, 336.516, 337.395, 338.664, 339.793, 340.172, 340.664, 342.148, 343.793, 344.91, 345.328, 345.328, 345.328, 344.5, 342.234, 338.832, 335.664, 334.609, 333.41, 329.813, 326.332, 324.152, 323.328, 323.281, 322.734, 318.75, 317.422, 317.422, 317.719, 318.82, 322.137, 325.664, 327.996, 328.844, 328.844, 328.844, 328.023, 325.723, 322.172, 318.75, 317.609, 316.52, 313.258, 309.871, 307.672, 306.891, 306.891, 306.891, 307.727, 310.031, 313.469, 316.656, 317.719, 317.719, 317.813, 319.559, 324.809, 330.023, 333.281, 334.406, 334.406, 334.406, 333.215, 329.797, 324.387, 319.008, 317.219, 315.516, 310.41, 305.215, 301.898, 300.734, 300.734, 300.734, 301.906, 305.289, 310.66, 316.023, 317.813, 317.813),
  y = c(8101.36, 8101.36, 8100.18, 8096.9, 8091.93, 8087.22, 8085.66, 8084.56, 8081.29, 8078.09, 8077.52, 8077.23, 8075.97, 8073.87, 8071.21, 8068.79, 8067.98, 8067.23, 8064.98, 8062.39, 8060.21, 8058.79, 8058.44, 8058.05, 8056.45, 8053.89, 8050.75, 8047.95, 8047.02, 8045.46, 8040.79, 8036.11, 8033.15, 8032.13, 8032.13, 8032.13, 8033.22, 8036.35, 8041.29, 8046.18, 8047.81, 8049.44, 8054.32, 8059.05, 8061.93, 8062.91, 8062.91, 8062.91, 8063.15, 8063.99, 8065.55, 8067.38, 8067.98, 8068.39, 8069.6, 8070.93, 8071.82, 8072.16, 8072.16, 8072.16, 8071.58, 8071.45, 8071.03, 8069.53, 8068.88, 8068.88, 8068.88, 8070.09, 8073.45, 8078.52, 8083.29, 8084.88, 8085.88, 8088.91, 8092.53, 8095.71, 8097.88, 8098.47, 8099.19, 8101.34, 8103.39, 8104.62, 8105.03, 8105.03, 8105.03, 8104.52, 8103.37, 8103.05, 8102.77, 8101.41, 8100.18, 8099.77, 8099.41, 8098.35, 8097.18, 8096.39, 8096.09, 8096.09, 8096.09, 8096.54, 8097.78, 8099.61, 8101.3, 8101.86, 8102.7, 8105.23, 8107.9, 8109.66, 8110.3, 8110.3, 8110.3, 8109.68, 8107.85, 8104.8, 8101.63, 8100.56, 8100.69, 8101.36, 8101.36, 8101.36, 8094.8, 8094.8, 8094.05, 8092, 8088.89, 8085.95, 8084.97, 8084.01, 8081.13, 8078.12, 8076.14, 8075.44, 8075.44, 8075.44, 8076.13, 8078.1, 8081.17, 8084.17, 8085.17, 8086.12, 8088.97, 8092.03, 8094.06, 8094.8, 8094.8, 8094.8, 8056.27, 8056.27, 8055.66, 8053.93, 8051.15, 8048.35, 8047.42, 8046.52, 8043.83, 8041.07, 8039.3, 8038.67, 8038.67, 8038.67, 8039.27, 8041, 8043.75, 8046.5, 8047.42, 8048.34, 8051.1, 8053.89, 8055.65, 8056.27, 8056.27, 8056.27),
  id = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
  section = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L)
)

one_letter %>%
  st_as_sf(coords = c("x", "y")) %>%
  group_by(section) %>%
  summarise(do_union = FALSE) %>%
  st_cast("POLYGON") %>%
  mutate(hole = section > 1) %>%
  group_by(hole) %>%
  summarise(do_union = FALSE) %>%
  st_intersection() %>%
  filter(origins == "1") %>%
  ggplot() +
  geom_sf()

Created on 2019-03-04 by the reprex package (v0.2.1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM