3232subplot <- function (... , nrows = 1 , widths = NULL , heights = NULL , margin = 0.02 ,
3333 shareX = FALSE , shareY = FALSE , titleX = shareX ,
3434 titleY = shareY , which_layout = " merge" ) {
35- # are the dots a list of plotly objects?
3635 dotz <- list (... )
37- if (length(dotz ) == 1 && is.list(dotz [[1 ]]) && ! is.plotly(dotz [[1 ]])) {
38- dotz <- dotz [[1 ]]
39- }
40- # build each plot
41- plotz <- lapply(dotz , function (d ) plotly_build(d )$ x )
42- # ensure "axis-reference" trace attributes are properly formatted
43- # TODO: should this go inside plotly_build()?
44- plotz <- lapply(plotz , function (p ) {
45- p $ data <- lapply(p $ data , function (tr ) {
46- if (length(tr [[" geo" ]])) {
47- tr [[" geo" ]] <- sub(" ^geo1$" , " geo" , tr [[" geo" ]][1 ]) %|| % NULL
48- tr [[" xaxis" ]] <- NULL
49- tr [[" yaxis" ]] <- NULL
50- } else {
51- tr [[" geo" ]] <- NULL
52- tr [[" xaxis" ]] <- sub(" ^x1$" , " x" , tr [[" xaxis" ]][1 ] %|| % " x" )
53- tr [[" yaxis" ]] <- sub(" ^y1$" , " y" , tr [[" yaxis" ]][1 ] %|| % " y" )
54- }
55- tr
56- })
57- p
58- })
59- # Are any traces referencing "axis-like" layout attributes that are missing?
60- # If so, move those traces to a "new plot", and inherit layout attributes,
61- # which makes this sort of thing possible:
62- # https://plot.ly/r/map-subplots-and-small-multiples/
63- plots <- list ()
64- for (i in seq_along(plotz )) {
65- p <- plots [[i ]] <- plotz [[i ]]
66- layoutAttrs <- c(names(p $ layout ), c(" geo" , " xaxis" , " yaxis" ))
67- xTraceAttrs <- sub(" ^x" , " xaxis" , sapply(p $ data , function (tr ) tr [[" geo" ]] %|| % tr [[" xaxis" ]]))
68- yTraceAttrs <- sub(" ^y" , " yaxis" , sapply(p $ data , function (tr ) tr [[" geo" ]] %|| % tr [[" yaxis" ]]))
69- missingAttrs <- setdiff(c(xTraceAttrs , yTraceAttrs ), layoutAttrs )
70- # move to next iteration if trace references are complete
71- if (! length(missingAttrs )) next
72- # remove each "missing" trace from this plot
73- missingTraces <- xTraceAttrs %in% missingAttrs | yTraceAttrs %in% missingAttrs
74- plots [[i ]]$ data [missingTraces ] <- NULL
75- # move traces with "similar missingness" to a new plot
76- for (j in missingAttrs ) {
77- newPlot <- list (
78- data = p $ data [xTraceAttrs %in% j | yTraceAttrs %in% j ],
79- layout = p $ layout
36+ if (tibble :: is_tibble(dotz )) {
37+ # if dots is a tibble, search for one column with a list of plotly objects
38+ idx <- which(vapply(dotz , function (x ) is.plotly(x [[1 ]]), logical (1 )))
39+ if (length(idx ) != 1 ) {
40+ stop(
41+ " If you supply a tibble to subplot(), \n " ,
42+ " it must have _one_ column with a list of plotly objects" ,
43+ call. = FALSE
8044 )
81- # reset the anchors
82- newPlot $ data <- lapply(newPlot $ data , function (tr ) {
83- for (k in c(" geo" , " xaxis" , " yaxis" )) {
84- tr [[k ]] <- sub(" [0-9]+" , " " , tr [[k ]]) %|| % NULL
85- }
86- tr
87- })
88- plots <- c(plots , list (newPlot ))
8945 }
46+ dotz <- dotz [[idx ]]
47+ } else if (length(dotz ) == 1 && is.list(dotz [[1 ]]) && ! is.plotly(dotz [[1 ]])) {
48+ # if ... is a list of plotly objects, list(...) is a (length 1) list
49+ # containing a list of plotly objects
50+ dotz <- dotz [[1 ]]
9051 }
91- # main plot objects
52+ # build each plot
53+ plots <- lapply(dotz , function (d ) plotly_build(d )[[" x" ]])
54+
55+ # grab main plot objects
9256 traces <- lapply(plots , " [[" , " data" )
9357 layouts <- lapply(plots , " [[" , " layout" )
9458 shapes <- lapply(layouts , " [[" , " shapes" )
9559 annotations <- lapply(layouts , function (x ) {
96- # keep non axis title annotations
60+ # keep non axis title annotations (for rescaling)
9761 axes <- vapply(x $ annotations , function (a ) identical(a $ annotationType , " axis" ), logical (1 ))
9862 x $ annotations [! axes ]
9963 })
100- # collect axis objects (note a _single_ geo object counts a both an x and y)
101- geoDomainDefault <- list (x = c(0 , 1 ), y = c(0 , 1 ))
64+ # collect axis objects (note a _single_ geo/mapbox object counts a both an x and y)
10265 xAxes <- lapply(layouts , function (lay ) {
103- keys <- grep(" ^geo|^xaxis" , names(lay ), value = TRUE ) %|| % " xaxis"
66+ keys <- grep(" ^geo|^mapbox|^ xaxis" , names(lay ), value = TRUE ) %|| % " xaxis"
10467 for (k in keys ) {
105- lay [[k ]]$ domain <- lay [[k ]]$ domain %|| % if (grepl(" ^geo" , k )) geoDomainDefault else c(0 , 1 )
68+ dom <- lay [[k ]]$ domain %|| % c(0 , 1 )
69+ if (" x" %in% names(dom )) dom <- dom [[" x" ]]
10670 }
10771 lay [keys ]
10872 })
10973 yAxes <- lapply(layouts , function (lay ) {
110- keys <- grep(" ^geo|^yaxis" , names(lay ), value = TRUE ) %|| % " yaxis"
74+ keys <- grep(" ^geo|^mapbox|^ yaxis" , names(lay ), value = TRUE ) %|| % " yaxis"
11175 for (k in keys ) {
112- lay [[k ]]$ domain <- lay [[k ]]$ domain %|| % if (grepl(" ^geo" , k )) geoDomainDefault else c(0 , 1 )
76+ dom <- lay [[k ]]$ domain %|| % c(0 , 1 )
77+ if (" y" %in% names(dom )) dom <- dom [[" y" ]]
11378 }
11479 lay [keys ]
11580 })
@@ -168,17 +133,17 @@ subplot <- function(..., nrows = 1, widths = NULL, heights = NULL, margin = 0.02
168133 yAxes [[i ]] <- setNames(yAxes [[i ]], names(yMap ))
169134 # for cartesian, bump corresponding axis anchor
170135 for (j in seq_along(xAxes [[i ]])) {
171- if (grepl(" ^geo" , names(xAxes [[i ]][j ]))) next
136+ if (grepl(" ^geo|^mapbox " , names(xAxes [[i ]][j ]))) next
172137 map <- yMap [yMap %in% sub(" y" , " yaxis" , xAxes [[i ]][[j ]]$ anchor %|| % " y" )]
173138 xAxes [[i ]][[j ]]$ anchor <- sub(" axis" , " " , names(map ))
174139 }
175140 for (j in seq_along(yAxes [[i ]])) {
176- if (grepl(" ^geo" , names(yAxes [[i ]][j ]))) next
141+ if (grepl(" ^geo|^mapbox " , names(yAxes [[i ]][j ]))) next
177142 map <- xMap [xMap %in% sub(" x" , " xaxis" , yAxes [[i ]][[j ]]$ anchor %|| % " x" )]
178143 yAxes [[i ]][[j ]]$ anchor <- sub(" axis" , " " , names(map ))
179144 }
180145 # map trace xaxis/yaxis/geo attributes
181- for (key in c(" geo" , " xaxis" , " yaxis" )) {
146+ for (key in c(" geo" , " subplot " , " xaxis" , " yaxis" )) {
182147 oldAnchors <- unlist(lapply(traces [[i ]], " [[" , key ))
183148 if (! length(oldAnchors )) next
184149 axisMap <- if (key == " yaxis" ) yMap else xMap
@@ -226,7 +191,9 @@ subplot <- function(..., nrows = 1, widths = NULL, heights = NULL, margin = 0.02
226191 p $ layout $ annotations <- Reduce(c , annotations )
227192 p $ layout $ shapes <- Reduce(c , shapes )
228193 # merge non-axis layout stuff
229- layouts <- lapply(layouts , function (x ) x [! grepl(" ^[x-y]axis|^geo" , names(x ))] %|| % list ())
194+ layouts <- lapply(layouts , function (x ) {
195+ x [! grepl(" ^[x-y]axis|^geo|^mapbox|annotations|shapes" , names(x ))] %|| % list ()
196+ })
230197 if (which_layout != " merge" ) {
231198 if (! is.numeric(which_layout )) warning(" which_layout must be numeric" )
232199 if (! all(idx <- which_layout %in% seq_along(plots ))) {
@@ -240,7 +207,9 @@ subplot <- function(..., nrows = 1, widths = NULL, heights = NULL, margin = 0.02
240207 if (length(sources ) > 1 ) {
241208 stop(" Can have multiple source values in a single subplot" )
242209 }
210+ p $ config <- Reduce(modify_list , lapply(plots , " [[" , " config" )) %|| % NULL
243211 p $ source <- sources [1 ]
212+ p $ subplot <- TRUE
244213 as_widget(p )
245214}
246215
0 commit comments