Skip to content

Commit e70e217

Browse files
committed
fortify sf objects in a similar manner to sf::st_as_grob
1 parent db377e0 commit e70e217

File tree

8 files changed

+98
-143
lines changed

8 files changed

+98
-143
lines changed

NAMESPACE

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,6 @@ S3method(geom2trace,GeomPolygon)
2020
S3method(geom2trace,GeomText)
2121
S3method(geom2trace,GeomTile)
2222
S3method(geom2trace,default)
23-
S3method(get_l,LINESTRING)
24-
S3method(get_l,MULTILINESTRING)
25-
S3method(get_l,MULTIPOINT)
26-
S3method(get_l,MULTIPOLYGON)
27-
S3method(get_l,POINT)
28-
S3method(get_l,POLYGON)
29-
S3method(get_x,LINESTRING)
30-
S3method(get_x,MULTILINESTRING)
31-
S3method(get_x,MULTIPOINT)
32-
S3method(get_x,MULTIPOLYGON)
33-
S3method(get_x,POINT)
34-
S3method(get_x,POLYGON)
35-
S3method(get_y,LINESTRING)
36-
S3method(get_y,MULTILINESTRING)
37-
S3method(get_y,MULTIPOINT)
38-
S3method(get_y,MULTIPOLYGON)
39-
S3method(get_y,POINT)
40-
S3method(get_y,POLYGON)
4123
S3method(ggplot,plotly)
4224
S3method(ggplotly,ggmatrix)
4325
S3method(ggplotly,ggplot)
@@ -58,6 +40,17 @@ S3method(print,api_plot)
5840
S3method(rename_,plotly)
5941
S3method(select_,plotly)
6042
S3method(slice_,plotly)
43+
S3method(st_as_plotly,CIRCULARSTRING)
44+
S3method(st_as_plotly,COMPOUNDCURVE)
45+
S3method(st_as_plotly,CURVEPOLYGON)
46+
S3method(st_as_plotly,GEOMETRYCOLLECTION)
47+
S3method(st_as_plotly,LINESTRING)
48+
S3method(st_as_plotly,MULTILINESTRING)
49+
S3method(st_as_plotly,MULTIPOINT)
50+
S3method(st_as_plotly,MULTIPOLYGON)
51+
S3method(st_as_plotly,MULTISURFACE)
52+
S3method(st_as_plotly,POINT)
53+
S3method(st_as_plotly,POLYGON)
6154
S3method(summarise_,plotly)
6255
S3method(to_basic,GeomAbline)
6356
S3method(to_basic,GeomAnnotationMap)
@@ -145,9 +138,6 @@ export(filter)
145138
export(filter_)
146139
export(geom2trace)
147140
export(get_figure)
148-
export(get_l)
149-
export(get_x)
150-
export(get_y)
151141
export(gg2list)
152142
export(ggplotly)
153143
export(group2NA)

R/ggplotly.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ gg2list <- function(p, width = NULL, height = NULL,
621621

622622
if (sum(isGrill) == 0) {
623623
# TODO: reduce the number of points (via coord_munch?)
624-
d <- expand(rng$graticule)
624+
d <- sf_fortify(rng$graticule)
625625
d$x <- scales::rescale(d$x, rng$x_range, from = c(0, 1))
626626
d$y <- scales::rescale(d$y, rng$y_range, from = c(0, 1))
627627
params <- list(

R/group2NA.R

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ group2NA <- function(data, groupNames = "group", nested = NULL, ordered = NULL,
4545

4646
if (NROW(data) == 0) return(data)
4747

48+
# for restoring class information on exit
49+
datClass <- oldClass(data)
50+
51+
# data.table doesn't play nice with list-columns
52+
if (inherits(data, "sf") || inherits(data, "GeomSf")) data <- sf_fortify(data)
53+
4854
# evaluate this lazy argument now (in case we change class of data)
4955
retrace <- force(retrace.first)
5056

@@ -53,9 +59,6 @@ group2NA <- function(data, groupNames = "group", nested = NULL, ordered = NULL,
5359
nested <- nested[nested %in% names(data)]
5460
ordered <- ordered[ordered %in% names(data)]
5561

56-
# for restoring class information on exit
57-
datClass <- oldClass(data)
58-
5962
dt <- data.table::as.data.table(data)
6063

6164
# if group doesn't exist, just order the rows and exit

R/layers2traces.R

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -270,31 +270,29 @@ to_basic.GeomRect <- function(data, prestats_data, layout, params, p, ...) {
270270
prefix_class(dat, c("GeomPolygon", "GeomRect"))
271271
}
272272

273-
#' @export
273+
#' @export
274274
to_basic.GeomSf <- function(data, prestats_data, layout, params, p, ...) {
275275

276-
browser()
277-
sf_data <- sf::st_as_sf(data)
278-
sf_d <- lapply(1:nrow(data), function(i) {
279-
sf_as_plotly(sf_data[i, , drop = FALSE])
280-
})
281-
282-
sf_as_plotly()
283-
284-
# logic based on GeomSf$draw_panel
285-
#p$coord$transform(data, layout$panel_params[[1]])
286-
287-
geomBasic <- switch(
288-
params$legend %||% "",
289-
point = "GeomPoint",
290-
line = "GeomPath",
291-
"GeomPolygon"
276+
# map sf geometry types to a suitable "basic geom"
277+
# TODO: support more of the esoteric geometry types, see sf::st_geometry_type
278+
# most important is probable geometry collection
279+
geom_type <- sf::st_geometry_type(sf::st_as_sf(data))
280+
basic_type <- dplyr::recode(
281+
as.character(geom_type),
282+
MULTIPOLYGON = "GeomPolygon",
283+
MULTILINESTRING = "GeomPath",
284+
MULTIPOINT = "GeomPoint",
285+
POLYGON = "GeomPolygon",
286+
LINESTRING = "GeomPath",
287+
POINT = "GeomPoint"
292288
)
293289

294-
295-
# determine the type of simple feature for each row
296-
# recode the simple feature with the type of geometry used to render it
297-
prefix_class(data, c("GeomSf", geomBasic))
290+
# return a list of data frames...one for every geometry (a la, GeomSmooth)
291+
d <- split(data, basic_type)
292+
for (i in seq_along(d)) {
293+
d[[i]] <- prefix_class(d[[i]], names(d)[[i]])
294+
}
295+
if (length(d) == 1) d[[1]] else d
298296
}
299297

300298
#' @export
@@ -724,7 +722,7 @@ geom2trace.GeomBar <- function(data, params, p) {
724722

725723
#' @export
726724
geom2trace.GeomPolygon <- function(data, params, p) {
727-
725+
728726
data <- group2NA(data)
729727

730728
L <- list(
@@ -913,7 +911,7 @@ split_on <- function(dat) {
913911

914912
# given a geom, are we hovering over points or fill?
915913
hover_on <- function(data) {
916-
if (inherits(data, c("GeomHex", "GeomRect", "GeomMap", "GeomMosaic", "GeomAnnotationMap")) ||
914+
if (inherits(data, c("GeomHex", "GeomRect", "GeomMap", "GeomMosaic", "GeomAnnotationMap", "GeomSf")) ||
917915
# is this a "basic" polygon?
918916
identical("GeomPolygon", grep("^Geom", class(data), value = T))) {
919917
"fills"
@@ -973,7 +971,7 @@ aes2plotly <- function(data, params, aes = "size") {
973971

974972
# Hack to support this geom_sf hack
975973
# https://github.com/tidyverse/ggplot2/blob/505e4bfb/R/sf.R#L179-L187
976-
defaults <- if (identical(geom, "GeomSf")) {
974+
defaults <- if (inherits(data, "GeomSf")) {
977975
type <- if (any(grepl("point", class(data)))) "point" else if (any(grepl("line", class(data)))) "line" else ""
978976
ggfun("default_aesthetics")(type)
979977
} else {

R/sf.R

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,90 @@
1-
# kind of like sf_as_plotly(), but maps to a plotly data structure, rather than a grob
2-
sf_as_plotly <- function(row, ...) {
1+
sf_fortify <- function(model, ...) {
2+
# TODO:
3+
# (1) avoid converting redundant features
4+
# (2) warn/error if data already contains x/y
5+
geoms <- sf::st_geometry(sf::st_as_sf(model))
6+
xy <- lapply(geoms, st_as_plotly)
7+
ids <- rep(seq_len(nrow(model)), sapply(xy, nrow))
8+
xy_all <- cbind(do.call(rbind, xy), ids)
9+
sf_key_name <- ".sf-group-id"
10+
xy_dat <- setNames(as.data.frame(xy_all), c("x", "y", sf_key_name))
11+
12+
d <- as.data.frame(model)
13+
d$geometry <- NULL
14+
d[[sf_key_name]] <- seq_len(nrow(d))
15+
xy_dat <- dplyr::left_join(xy_dat, d, by = sf_key_name)
16+
xy_dat[[sf_key_name]] <- NULL
17+
xy_dat
18+
}
19+
20+
# kind of like sf_as_grob(), but maps to a plotly data structure, rather than a grob
21+
st_as_plotly <- function(x, ...) {
322
UseMethod("st_as_plotly")
423
}
524

625
#' @export
7-
sf_as_plotly.POINT = function(row, ...) {
8-
x <- row[1]
9-
y <- row[2]
10-
x
26+
st_as_plotly.POINT = function(x, ...) {
27+
matrix(c(x[1], x[2]), ncol = 2)
1128
}
1229

1330
#' @export
14-
sf_as_plotly.MULTIPOINT = function(row, ...) {
15-
if (nrow(x) == 0)
16-
nullGrob()
17-
else
18-
pointsGrob(x[,1], x[,2], ..., default.units = default.units)
31+
st_as_plotly.MULTIPOINT = function(x, ...) {
32+
if (nrow(x) == 0) return(empty_xy())
33+
matrix(c(x[, 1], x[, 2]), ncol = 2)
1934
}
2035

2136
#' @export
22-
sf_as_plotly.LINESTRING = function(row, ...) {
23-
if (nrow(x) == 0)
24-
nullGrob()
25-
else
26-
linesGrob(x[,1], x[,2], ..., default.units = default.units)
37+
st_as_plotly.LINESTRING = function(x, ...) {
38+
if (nrow(x) == 0) return(empty_xy())
39+
matrix(c(x[, 1], x[, 2]), ncol = 2)
2740
}
2841

2942
#' @export
30-
sf_as_plotly.CIRCULARSTRING = function(x, y, ...) {
31-
sf_as_plotly(st_cast(x, "LINESTRING"), ...)
43+
st_as_plotly.CIRCULARSTRING = function(x, y, ...) {
44+
st_as_plotly(st_cast(x, "LINESTRING"), ...)
3245
}
3346

3447
#' @export
35-
sf_as_plotly.MULTILINESTRING = function(row, ...) {
36-
if (length(x) == 0)
37-
nullGrob()
38-
else {
39-
get_x = function(x) unlist(sapply(x, function(y) y[,1]))
40-
get_y = function(x) unlist(sapply(x, function(y) y[,2]))
41-
polylineGrob(get_x(x), get_y(x), id.lengths = vapply(x, nrow, 0L), ...,
42-
default.units = default.units)
43-
}
48+
st_as_plotly.MULTILINESTRING = function(x, ...) {
49+
if (length(x) == 0) return(empty_xy())
50+
xvals <- unlist(rbind(sapply(x, function(y) y[,1]), NA))
51+
yvals <- unlist(rbind(sapply(x, function(y) y[,2]), NA))
52+
matrix(c(xvals, yvals), ncol = 2)
4453
}
4554

4655
#' @export
47-
sf_as_plotly.POLYGON = function(row, ..., rule = "evenodd") {
48-
if (length(x) == 0)
49-
nullGrob()
50-
else {
51-
get_x = function(x) unlist(sapply(x, function(y) y[,1]))
52-
get_y = function(x) unlist(sapply(x, function(y) y[,2]))
53-
pathGrob(get_x(x), get_y(x), id.lengths = vapply(x, nrow, 0L), ..., default.units = default.units, rule = rule)
54-
}
56+
st_as_plotly.POLYGON = function(x, ..., rule = "evenodd") {
57+
if (length(x) == 0) return(empty_xy())
58+
xvals <- unlist(rbind(sapply(x, function(y) y[,1]), NA))
59+
yvals <- unlist(rbind(sapply(x, function(y) y[,2]), NA))
60+
matrix(c(xvals, yvals), ncol = 2)
5561
}
5662

5763
#' @export
58-
sf_as_plotly.MULTIPOLYGON = function(row, ..., rule = "evenodd") {
59-
if (length(x) == 0)
60-
nullGrob()
61-
else {
62-
get_x = function(x) unlist(sapply(x, function(y) sapply(y, function(z) z[,1])))
63-
get_y = function(x) unlist(sapply(x, function(y) sapply(y, function(z) z[,2])))
64-
get_l = function(x) unlist(sapply(x, function(y) vapply(y, nrow, 0L)))
65-
pathGrob(get_x(x), get_y(x), id.lengths = get_l(x), ..., default.units = default.units, rule = rule)
66-
}
64+
st_as_plotly.MULTIPOLYGON = function(x, ..., rule = "evenodd") {
65+
if (length(x) == 0) return(empty_xy())
66+
xvals <- unlist(sapply(x, function(y) rbind(sapply(y, function(z) z[,1]), NA)))
67+
yvals <- unlist(sapply(x, function(y) rbind(sapply(y, function(z) z[,2]), NA)))
68+
matrix(c(xvals, yvals), ncol = 2)
6769
}
6870

6971
#' @export
70-
sf_as_plotly.GEOMETRYCOLLECTION = function(row, ...) {
71-
if (length(x) == 0)
72-
nullGrob()
73-
else
74-
do.call(grid::grobTree, lapply(x, sf_as_plotly, ..., default.units = default.units))
72+
st_as_plotly.GEOMETRYCOLLECTION = function(x, ...) {
73+
if (length(x) == 0) return(empty_xy())
74+
lapply(x, st_as_plotly)
7575
}
7676

7777
#' @export
78-
sf_as_plotly.MULTISURFACE = sf_as_plotly.GEOMETRYCOLLECTION
78+
st_as_plotly.MULTISURFACE = st_as_plotly.GEOMETRYCOLLECTION
7979

8080
#' @export
81-
sf_as_plotly.CURVEPOLYGON = sf_as_plotly.GEOMETRYCOLLECTION
81+
st_as_plotly.CURVEPOLYGON = st_as_plotly.GEOMETRYCOLLECTION
8282

8383
#' @export
84-
sf_as_plotly.COMPOUNDCURVE = sf_as_plotly.GEOMETRYCOLLECTION
84+
st_as_plotly.COMPOUNDCURVE = st_as_plotly.GEOMETRYCOLLECTION
85+
86+
87+
88+
empty_xy <- function() {
89+
matrix(rep(NA, 2), ncol = 2)
90+
}

man/get_l.Rd

Lines changed: 0 additions & 14 deletions
This file was deleted.

man/get_x.Rd

Lines changed: 0 additions & 14 deletions
This file was deleted.

man/get_y.Rd

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)