Reputation: 2400
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))
Upvotes: 1
Views: 1136
Reputation: 15082
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)
Upvotes: 2
Reputation: 15082
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)
Upvotes: 0