@@ -97,7 +97,7 @@ subplot <- function(..., nrows = 1, widths = NULL, heights = NULL, margin = 0.02
97
97
axes <- vapply(x $ annotations , function (a ) identical(a $ annotationType , " axis" ), logical (1 ))
98
98
x $ annotations [! axes ]
99
99
})
100
- # collect axis objects
100
+ # collect axis objects (note a _single_ geo object counts a both an x and y)
101
101
xAxes <- lapply(layouts , function (lay ) {
102
102
lay [grepl(" ^xaxis|^geo" , names(lay ))] %|| % list (xaxis = list (domain = c(0 , 1 )))
103
103
})
@@ -115,15 +115,21 @@ subplot <- function(..., nrows = 1, widths = NULL, heights = NULL, margin = 0.02
115
115
yAxisN <- vapply(yAxes , length , numeric (1 ))
116
116
# old -> new axis name dictionary
117
117
ncols <- ceiling(length(plots ) / nrows )
118
- xAxisID <- if (shareX ) {
119
- rep(rep(1 : ncols , length.out = length(plots )), xAxisN )
120
- } else {
121
- seq_len(sum(xAxisN ))
118
+ xAxisID <- seq_len(sum(xAxisN ))
119
+ if (shareX ) {
120
+ if (length(unique(xAxisN )) > 1 ) {
121
+ warning(" Must have a consistent number of axes per 'subplot' to share them." )
122
+ } else {
123
+ xAxisID <- rep(rep(seq_len(ncols * unique(xAxisN )), length.out = length(plots )), unique(xAxisN ))
124
+ }
122
125
}
123
- yAxisID <- if (shareY ) {
124
- rep(rep(1 : nrows , each = ncols , length.out = length(plots )), yAxisN )
125
- } else {
126
- seq_len(sum(yAxisN ))
126
+ yAxisID <- seq_len(sum(yAxisN ))
127
+ if (shareY ) {
128
+ if (length(unique(yAxisN )) > 1 ) {
129
+ warning(" Must have a consistent number of axes per 'subplot' to share them." )
130
+ } else {
131
+ yAxisID <- rep(rep(seq_len(nrows * unique(xAxisN )), each = ncols , length.out = length(plots )), unique(yAxisN ))
132
+ }
127
133
}
128
134
# current "axis" names
129
135
xCurrentNames <- unlist(lapply(xAxes , names ))
@@ -145,82 +151,71 @@ subplot <- function(..., nrows = 1, widths = NULL, heights = NULL, margin = 0.02
145
151
domainInfo <- get_domains(
146
152
length(plots ), nrows , margin , widths = widths , heights = heights
147
153
)
148
- # reposition shapes and annotations
149
- annotations <- Map(reposition , annotations , split(domainInfo , seq_along(plots )))
150
- shapes <- Map(reposition , shapes , split(domainInfo , seq_along(plots )))
151
- # rename axis objects, anchors, and scale their domains
152
154
for (i in seq_along(plots )) {
155
+ # map axis object names
153
156
xMap <- xAxisMap [[i ]]
154
157
yMap <- yAxisMap [[i ]]
158
+ xAxes [[i ]] <- setNames(xAxes [[i ]], names(xMap ))
159
+ yAxes [[i ]] <- setNames(yAxes [[i ]], names(yMap ))
160
+ # for cartesian, bump corresponding axis anchor
161
+ for (j in seq_along(xAxes [[i ]])) {
162
+ if (grepl(" ^geo" , names(xAxes [[i ]][j ]))) next
163
+ map <- yMap [yMap %in% sub(" y" , " yaxis" , xAxes [[i ]][[j ]]$ anchor %|| % " y" )]
164
+ xAxes [[i ]][[j ]]$ anchor <- sub(" axis" , " " , names(map ))
165
+ }
166
+ for (j in seq_along(yAxes [[i ]])) {
167
+ if (grepl(" ^geo" , names(yAxes [[i ]][j ]))) next
168
+ map <- xMap [xMap %in% sub(" x" , " xaxis" , yAxes [[i ]][[j ]]$ anchor %|| % " x" )]
169
+ yAxes [[i ]][[j ]]$ anchor <- sub(" axis" , " " , names(map ))
170
+ }
171
+ # map trace xaxis/yaxis/geo attributes
172
+ for (key in c(" geo" , " xaxis" , " yaxis" )) {
173
+ oldAnchors <- unlist(lapply(traces [[i ]], " [[" , key ))
174
+ if (! length(oldAnchors )) next
175
+ axisMap <- if (key == " yaxis" ) yMap else xMap
176
+ axisMap <- setNames(sub(" axis" , " " , axisMap ), sub(" axis" , " " , names(axisMap )))
177
+ newAnchors <- names(axisMap )[match(oldAnchors , axisMap )]
178
+ traces [[i ]] <- Map(function (tr , a ) { tr [[key ]] <- a ; tr }, traces [[i ]], newAnchors )
179
+ }
180
+ # rescale domains according to the tabular layout
155
181
xDom <- as.numeric(domainInfo [i , c(" xstart" , " xend" )])
156
182
yDom <- as.numeric(domainInfo [i , c(" yend" , " ystart" )])
157
- for (j in seq_along(xAxes [[i ]])) {
158
- # TODO: support ternary as well!
159
- isGeo <- grepl(" ^geo" , xMap [[j ]])
160
- anchorKey <- if (isGeo ) " geo" else " xaxis"
161
- traces [[i ]] <- lapply(traces [[i ]], function (tr ) {
162
- tr [[anchorKey ]] <- tr [[anchorKey ]] %|| % sub(" axis" , " " , anchorKey )
163
- # bump trace anchors, where appropriate
164
- if (sub(" axis" , " " , xMap [[j ]]) %in% tr [[anchorKey ]]) {
165
- tr [[anchorKey ]] <- sub(" axis" , " " , names(xMap [j ]))
166
- }
167
- tr
168
- })
169
- if (isGeo ) {
170
- xAxes [[i ]][[j ]]$ domain $ x <- sort(scales :: rescale(
171
- xAxes [[i ]][[j ]]$ domain $ x %|| % c(0 , 1 ), xDom , from = c(0 , 1 )
172
- ))
173
- xAxes [[i ]][[j ]]$ domain $ y <- sort(scales :: rescale(
174
- xAxes [[i ]][[j ]]$ domain $ y %|| % c(0 , 1 ), yDom , from = c(0 , 1 )
175
- ))
183
+ reScale <- function (old , new ) {
184
+ sort(scales :: rescale(
185
+ old %|| % c(0 , 1 ), new , from = c(0 , 1 )
186
+ ))
187
+ }
188
+ xAxes [[i ]] <- lapply(xAxes [[i ]], function (ax ) {
189
+ if (all(c(" x" , " y" ) %in% names(ax $ domain ))) {
190
+ # geo domains are different from cartesian
191
+ ax $ domain $ x <- reScale(ax $ domain $ x , xDom )
192
+ ax $ domain $ y <- reScale(ax $ domain $ y , yDom )
176
193
} else {
177
- xAxes [[i ]][[j ]]$ domain <- sort(scales :: rescale(
178
- xAxes [[i ]][[j ]]$ domain %|| % c(0 , 1 ), xDom , from = c(0 , 1 )
179
- ))
180
- # for cartesian, bump corresponding axis
181
- map <- yMap [yMap %in% sub(" y" , " yaxis" , xAxes [[i ]][[j ]]$ anchor %|| % " y" )]
182
- xAxes [[i ]][[j ]]$ anchor <- sub(" axis" , " " , names(map ))
194
+ ax $ domain <- reScale(ax $ domain , xDom )
183
195
}
184
- }
185
- for (j in seq_along(yAxes [[i ]])) {
186
- # TODO: support ternary as well!
187
- isGeo <- grepl(" ^geo" , yMap [[j ]])
188
- anchorKey <- if (isGeo ) " geo" else " yaxis"
189
- traces [[i ]] <- lapply(traces [[i ]], function (tr ) {
190
- tr [[anchorKey ]] <- tr [[anchorKey ]] %|| % sub(" axis" , " " , anchorKey )
191
- # bump trace anchors, where appropriate
192
- if (sub(" axis" , " " , yMap [[j ]]) %in% tr [[anchorKey ]]) {
193
- tr [[anchorKey ]] <- sub(" axis" , " " , names(yMap [j ]))
194
- }
195
- tr
196
- })
197
- if (isGeo ) {
198
- yAxes [[i ]][[j ]]$ domain $ x <- sort(scales :: rescale(
199
- yAxes [[i ]][[j ]]$ domain $ x %|| % c(0 , 1 ), xDom , from = c(0 , 1 )
200
- ))
201
- yAxes [[i ]][[j ]]$ domain $ y <- sort(scales :: rescale(
202
- yAxes [[i ]][[j ]]$ domain $ y %|| % c(0 , 1 ), yDom , from = c(0 , 1 )
203
- ))
196
+ ax
197
+ })
198
+ yAxes [[i ]] <- lapply(yAxes [[i ]], function (ax ) {
199
+ if (all(c(" x" , " y" ) %in% names(ax $ domain ))) {
200
+ # geo domains are different from cartesian
201
+ ax $ domain $ x <- reScale(ax $ domain $ x , xDom )
202
+ ax $ domain $ y <- reScale(ax $ domain $ y , yDom )
204
203
} else {
205
- yAxes [[i ]][[j ]]$ domain <- sort(scales :: rescale(
206
- yAxes [[i ]][[j ]]$ domain %|| % c(0 , 1 ), yDom , from = c(0 , 1 )
207
- ))
208
- # for cartesian, bump corresponding axis
209
- map <- xMap [xMap %in% sub(" x" , " xaxis" , yAxes [[i ]][[j ]]$ anchor %|| % " x" )]
210
- yAxes [[i ]][[j ]]$ anchor <- sub(" axis" , " " , names(map ))
204
+ ax $ domain <- reScale(ax $ domain , yDom )
211
205
}
212
- }
213
- xAxes [[i ]] <- setNames(xAxes [[i ]], names(xMap ))
214
- yAxes [[i ]] <- setNames(yAxes [[i ]], names(yMap ))
206
+ ax
207
+ })
215
208
}
216
209
# start merging the plots into a single subplot
217
210
p <- list (
218
211
data = Reduce(c , traces ),
219
212
layout = Reduce(modifyList , c(xAxes , rev(yAxes )))
220
213
)
214
+ # reposition shapes and annotations
215
+ annotations <- Map(reposition , annotations , split(domainInfo , seq_along(plots )))
216
+ shapes <- Map(reposition , shapes , split(domainInfo , seq_along(plots )))
221
217
p $ layout $ annotations <- Reduce(c , annotations )
222
218
p $ layout $ shapes <- Reduce(c , shapes )
223
-
224
219
# merge non-axis layout stuff
225
220
layouts <- lapply(layouts , function (x ) x [! grepl(" ^[x-y]axis|^geo" , names(x ))] %|| % list ())
226
221
if (which_layout != " merge" ) {
0 commit comments