6
6
# #' @export
7
7
# #' @return list of geom info.
8
8
# #' @author Toby Dylan Hocking
9
+
10
+ # # calc. the epoch
11
+ now <- Sys.time()
12
+ the.epoch <- now - as.numeric(now )
13
+
9
14
group2NA <- function (g , geom ){
10
15
poly.list <- split(g $ data , g $ data $ group )
11
16
is.group <- names(g $ data ) == " group"
@@ -47,12 +52,19 @@ pch2symbol <- c("0"="square",
47
52
aes2marker <- c(alpha = " opacity" ,
48
53
colour = " color" ,
49
54
size = " size" ,
55
+ sizeref = " sizeref" ,
56
+ sizemode = " sizemode" ,
50
57
shape = " symbol" )
51
58
52
- marker.defaults <- c(alpha = 1 ,
53
- shape = " o" ,
54
- size = 1 ,
55
- colour = " black" )
59
+ default.marker.sizeref = 1
60
+ marker.size.mult <- 10
61
+
62
+ marker.defaults <- list (alpha = 1 ,
63
+ shape = " o" ,
64
+ size = marker.size.mult ,
65
+ sizeref = default.marker.sizeref ,
66
+ sizemode = " area" ,
67
+ colour = " black" )
56
68
line.defaults <-
57
69
list (linetype = " solid" ,
58
70
colour = " black" ,
@@ -96,12 +108,19 @@ lty2dash <- c(numeric.lty, named.lty, coded.lty)
96
108
97
109
aesConverters <-
98
110
list (linetype = function (lty ){
99
- lty2dash [as.character(lty )]
100
- },colour = function (col ){
101
- toRGB(col )
102
- },size = identity ,alpha = identity ,shape = function (pch ){
103
- pch2symbol [as.character(pch )]
104
- }, direction = identity )
111
+ lty2dash [as.character(lty )]
112
+ },
113
+ colour = function (col ){
114
+ toRGB(col )
115
+ },
116
+ size = identity ,
117
+ sizeref = identity ,
118
+ sizemode = identity ,
119
+ alpha = identity ,
120
+ shape = function (pch ){
121
+ pch2symbol [as.character(pch )]
122
+ },
123
+ direction = identity )
105
124
106
125
toBasic <-
107
126
list (segment = function (g ){
@@ -144,6 +163,7 @@ toBasic <-
144
163
stop(" TODO" )
145
164
})
146
165
166
+
147
167
# ' Convert basic geoms to traces.
148
168
geom2trace <-
149
169
list (path = function (data , params ){
@@ -173,9 +193,10 @@ geom2trace <-
173
193
mode = " markers" ,
174
194
marker = paramORdefault(params , aes2marker , marker.defaults ))
175
195
if (" size" %in% names(data )){
176
- L $ marker $ sizeref <- min(data $ size )
177
- L $ marker $ sizemode <- " area"
178
- L $ marker $ size <- data $ size
196
+ L $ marker $ sizeref <- default.marker.sizeref
197
+ # # Make sure sizes are passed as a list even when there is only one element.
198
+ marker.size <- data $ size * marker.size.mult
199
+ L $ marker $ size <- if (length(marker.size ) > 1 ) marker.size else list (marker.size )
179
200
}
180
201
L
181
202
},
@@ -221,6 +242,8 @@ aes2line <- c(linetype="dash",
221
242
direction = " shape" )
222
243
223
244
markLegends <-
245
+ # # NOTE: Do we also want to split on size?
246
+ # # list(point=c("colour", "fill", "shape", "size"),
224
247
list (point = c(" colour" , " fill" , " shape" ),
225
248
path = c(" linetype" , " size" , " colour" ),
226
249
polygon = c(" colour" , " fill" , " linetype" , " size" , " group" ),
@@ -297,7 +320,6 @@ gg2list <- function(p){
297
320
trace.list $ plot <- built $ plot $ data
298
321
}
299
322
300
- ranges <- built $ panel $ ranges [[1 ]]
301
323
for (i in seq_along(built $ plot $ layers )){
302
324
# # This is the layer from the original ggplot object.
303
325
L <- p $ layers [[i ]]
@@ -341,7 +363,18 @@ gg2list <- function(p){
341
363
misc $ breaks [[sc $ aesthetics ]] <- ranks
342
364
}
343
365
}
366
+
367
+ # # get gglayout now because we need some of its info in layer2traces
368
+ gglayout <- built $ panel $ layout
369
+ # # invert rows so that plotly and ggplot2 show panels in the same order
370
+ gglayout $ plotly.row <- max(gglayout $ ROW ) - gglayout $ ROW + 1
344
371
372
+ # # Add ROW and COL to df: needed to link axes to traces; keep df's
373
+ # # original ordering while merging.
374
+ df $ order <- seq_len(nrow(df ))
375
+ df <- merge(df , gglayout [,c(" PANEL" ," plotly.row" ," COL" )])
376
+ df <- df [order(df $ order ),]
377
+ df $ order <- NULL
345
378
# # This extracts essential info for this geom/layer.
346
379
traces <- layer2traces(L , df , misc , trace.list $ plot )
347
380
@@ -350,11 +383,12 @@ gg2list <- function(p){
350
383
# # built$panel$ranges[[1]])
351
384
trace.list <- c(trace.list , traces )
352
385
}
353
- # Export axis specification as a combination of breaks and
354
- # labels, on the relevant axis scale (i.e. so that it can
355
- # be passed into d3 on the x axis scale instead of on the
356
- # grid 0-1 scale). This allows transformations to be used
357
- # out of the box, with no additional d3 coding.
386
+
387
+ # # Export axis specification as a combination of breaks and labels, on
388
+ # # the relevant axis scale (i.e. so that it can be passed into d3 on the
389
+ # # x axis scale instead of on the grid 0-1 scale). This allows
390
+ # # transformations to be used out of the box, with no additional d3
391
+ # # coding.
358
392
theme.pars <- ggplot2 ::: plot_theme(p )
359
393
360
394
# # Flip labels if coords are flipped - transform does not take care
@@ -430,6 +464,114 @@ gg2list <- function(p){
430
464
! is.blank(s(" axis.ticks.%s" ))
431
465
layout [[s(" %saxis" )]] <- ax.list
432
466
}
467
+
468
+ # # copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each
469
+ xaxis.title <- layout $ xaxis $ title
470
+ yaxis.title <- layout $ yaxis $ title
471
+ inner.margin <- 0.01 # # between facets
472
+ outer.margin <- 0.05 # # to put titles outside of the plots
473
+ orig.xaxis <- layout $ xaxis
474
+ orig.yaxis <- layout $ yaxis
475
+ if (nrow(gglayout ) > 1 )
476
+ {
477
+ row.size <- 1 . / max(gglayout $ ROW )
478
+ col.size <- 1 . / max(gglayout $ COL )
479
+ for (i in seq_len(nrow(gglayout )))
480
+ {
481
+ row <- gglayout [i , " plotly.row" ]
482
+ col <- gglayout [i , " COL" ]
483
+ x <- col * col.size
484
+ xmin <- x - col.size
485
+ xmax <- x - inner.margin
486
+ y <- row * row.size
487
+ ymin <- y - row.size
488
+ ymax <- y - inner.margin
489
+ yaxis.name <- if (row == 1 ) " yaxis" else paste0(" yaxis" , row )
490
+ xaxis.name <- if (col == 1 ) " xaxis" else paste0(" xaxis" , col )
491
+ layout [[xaxis.name ]] <- orig.xaxis
492
+ layout [[xaxis.name ]]$ domain <- c(xmin , xmax )
493
+ layout [[xaxis.name ]]$ anchor <- " y"
494
+ layout [[xaxis.name ]]$ title <- NULL
495
+ if (orig.xaxis $ type == " linear" && # range only makes sense for numeric data
496
+ (is.null(p $ facet $ scales ) || p $ facet $ scales == " fixed" || p $ facet $ scales == " free_y" ))
497
+ {
498
+ layout [[xaxis.name ]]$ range <- built $ panel $ ranges [[i ]]$ x.range
499
+ layout [[xaxis.name ]]$ autorange <- FALSE
500
+ }
501
+
502
+ layout [[yaxis.name ]] <- orig.yaxis
503
+ layout [[yaxis.name ]]$ domain <- c(ymin , ymax )
504
+ layout [[yaxis.name ]]$ anchor <- " x"
505
+ layout [[yaxis.name ]]$ title <- NULL
506
+ if (orig.yaxis $ type == " linear" && # range only makes sense for numeric data
507
+ (is.null(p $ facet $ scales ) || p $ facet $ scales == " fixed" || p $ facet $ scales == " free_x" ))
508
+ {
509
+ layout [[yaxis.name ]]$ range <- built $ panel $ ranges [[i ]]$ y.range
510
+ layout [[yaxis.name ]]$ autorange <- FALSE
511
+ }
512
+
513
+ }
514
+ # # add panel titles as annotations
515
+ annotations <- list ()
516
+ nann <- 1
517
+ make.label <- function (text , x , y )
518
+ list (text = text , showarrow = FALSE , x = x , y = y , ax = 0 , ay = 0 , xref = " paper" , yref = " paper" )
519
+
520
+ if (" grid" %in% class(p $ facet ))
521
+ {
522
+ frows <- names(p $ facet $ rows )
523
+ nann <- 1
524
+
525
+ for (i in seq_len(max(gglayout $ ROW )))
526
+ {
527
+ text <- paste(lapply(gglayout [gglayout $ ROW == i , frows , drop = FALSE ][1 ,],
528
+ as.character ),
529
+ collapse = " , " )
530
+ annotations [[nann ]] <- make.label(text , 1 + outer.margin , row.size * (max(gglayout $ ROW )- i + 0.5 ))
531
+ nann <- nann + 1
532
+ }
533
+
534
+ fcols <- names(p $ facet $ cols )
535
+ for (i in seq_len(max(gglayout $ COL )))
536
+ {
537
+ text <- paste(lapply(gglayout [gglayout $ COL == i , fcols , drop = FALSE ][1 ,],
538
+ as.character ),
539
+ collapse = " , " )
540
+ annotations [[nann ]] <- make.label(text , col.size * (i - 0.5 ) - inner.margin / 2 , 1 + outer.margin )
541
+ nann <- nann + 1
542
+ }
543
+
544
+ # # add empty traces everywhere so that the background shows even if there
545
+ # # is no data for a facet
546
+ for (r in seq_len(max(gglayout $ ROW )))
547
+ for (c in seq_len(max(gglayout $ COL )))
548
+ trace.list <- c(trace.list , list (list (xaxis = paste0(" x" , c ), yaxis = paste0(" y" , r ), showlegend = FALSE )))
549
+ }
550
+ else if (" wrap" %in% class(p $ facet ))
551
+ {
552
+ facets <- names(p $ facet $ facets )
553
+ for (i in seq_len(max(as.numeric(gglayout $ PANEL ))))
554
+ {
555
+ ix <- gglayout $ PANEL == i
556
+ row <- gglayout $ ROW [ix ]
557
+ col <- gglayout $ COL [ix ]
558
+ text <- paste(lapply(gglayout [ix , facets , drop = FALSE ][1 ,],
559
+ as.character ),
560
+ collapse = " , " )
561
+ annotations [[nann ]] <- make.label(text , col.size * (col - 0.5 ) - inner.margin / 2 ,
562
+ row.size * (max(gglayout $ ROW ) - row + 1 ))
563
+ nann <- nann + 1
564
+ }
565
+ }
566
+
567
+ # # axes titles
568
+ annotations [[nann ]] <- make.label(xaxis.title , 0.5 , - outer.margin )
569
+ nann <- nann + 1
570
+ annotations [[nann ]] <- make.label(yaxis.title , - outer.margin , 0.5 )
571
+ nann <- nann + 1
572
+
573
+ layout $ annotations <- annotations
574
+ }
433
575
434
576
# # Remove legend if theme has no legend position
435
577
if (theme.pars $ legend.position == " none" ) layout $ showlegend <- FALSE
@@ -465,18 +607,20 @@ layer2traces <- function(l, d, misc, plot=NULL){
465
607
plot = plot )
466
608
# # needed for when group, etc. is an expression.
467
609
g $ aes <- sapply(l $ mapping , function (k ) as.character(as.expression(k )))
468
-
610
+
469
611
# # For non-numeric data on the axes, we should take the values from
470
612
# # the original data.
471
- for (axis.name in c(" x" , " y" )){
472
- if (! misc $ is.continuous [[axis.name ]]){
613
+ for (axis.name in c(" x" , " y" )){
614
+ if (! misc $ is.continuous [[axis.name ]]){
473
615
aes.names <- paste0(axis.name , c(" " , " end" , " min" , " max" ))
474
616
aes.used <- aes.names [aes.names %in% names(g $ aes )]
475
617
for (a in aes.used ){
476
618
col.name <- g $ aes [aes.used ]
477
619
data.vec <- l $ data [[col.name ]]
478
- if (inherits(data.vec , " POSIXt" )){
479
- data.vec <- strftime(data.vec , " %Y-%m-%d %H:%M:%S" )
620
+ if (inherits(data.vec , " POSIXt" )) {
621
+ # # Re-create dates from nb seconds
622
+ data.vec <- strftime(as.POSIXlt(g $ data [[a ]], origin = the.epoch ),
623
+ " %Y-%m-%d %H:%M:%S" )
480
624
} else if (inherits(data.vec , " factor" )) {
481
625
# # Re-order data so that Plotly gets it right from ggplot2.
482
626
g $ data <- g $ data [order(g $ data [[a ]]),]
@@ -486,6 +630,7 @@ layer2traces <- function(l, d, misc, plot=NULL){
486
630
}
487
631
}
488
632
}
633
+
489
634
# # use un-named parameters so that they will not be exported
490
635
# # to JSON as a named object, since that causes problems with
491
636
# # e.g. colour.
@@ -525,7 +670,6 @@ layer2traces <- function(l, d, misc, plot=NULL){
525
670
}else {
526
671
convert(g )
527
672
}
528
-
529
673
# # Then split on visual characteristics that will get different
530
674
# # legend entries.
531
675
data.list <- if (basic $ geom %in% names(markLegends )){
@@ -540,29 +684,30 @@ layer2traces <- function(l, d, misc, plot=NULL){
540
684
# # mark.names <- mark.names[!mark.names %in% to.erase]
541
685
# # }
542
686
name.names <- sprintf(" %s.name" , mark.names )
543
- is.split <- names(basic $ data ) %in% name.names
687
+ # # split on 'PANEL' to support facets
688
+ is.split <- names(basic $ data ) %in% c(name.names , " PANEL" )
544
689
if (any(is.split )){
545
690
data.i <- which(is.split )
546
691
matched.names <- names(basic $ data )[data.i ]
547
- name.i <- which( name.names %in% matched.names )
692
+ name.i <- name.names %in% matched.names
548
693
invariable.names <- cbind(name.names , mark.names )[name.i ,]
549
694
other.names <- ! names(basic $ data ) %in% invariable.names
550
695
vec.list <- basic $ data [is.split ]
551
696
df.list <- split(basic $ data , vec.list , drop = TRUE )
552
697
lapply(df.list , function (df ){
553
698
params <- basic $ params
554
- params [invariable.names ] <- df [1 , invariable.names ]
699
+ params [invariable.names ] <- if (ncol( x <- df [1 , invariable.names ]) > 0 ) x else NULL
555
700
list (data = df [other.names ],
556
701
params = params )
557
702
})
558
703
}
559
704
}
705
+
560
706
# # case of no legend, if either of the two ifs above failed.
561
707
if (is.null(data.list )){
562
708
data.list <- structure(list (list (data = basic $ data , params = basic $ params )),
563
709
names = basic $ params $ name )
564
710
}
565
-
566
711
getTrace <- geom2trace [[basic $ geom ]]
567
712
if (is.null(getTrace )){
568
713
warning(" Conversion not implemented for geom_" ,
@@ -572,6 +717,7 @@ layer2traces <- function(l, d, misc, plot=NULL){
572
717
return (list ())
573
718
}
574
719
traces <- NULL
720
+ names.in.legend <- NULL
575
721
for (data.i in seq_along(data.list )){
576
722
data.params <- data.list [[data.i ]]
577
723
tr <- do.call(getTrace , data.params )
@@ -594,8 +740,21 @@ layer2traces <- function(l, d, misc, plot=NULL){
594
740
name.list <- data.params $ params [name.names ]
595
741
tr $ name <- paste(unlist(name.list ), collapse = " ." )
596
742
}
743
+
744
+ dpd <- data.params $ data
745
+ if (" PANEL" %in% names(dpd ) && nrow(dpd ) > 0 )
746
+ {
747
+ tr $ xaxis <- paste0(" x" , dpd [1 , " COL" ])
748
+ tr $ yaxis <- paste0(" y" , dpd [1 , " plotly.row" ])
749
+ }
750
+
751
+ if (is.null(tr $ name ) || tr $ name %in% names.in.legend )
752
+ tr $ showlegend <- FALSE
753
+ names.in.legend <- c(names.in.legend , tr $ name )
754
+
597
755
traces <- c(traces , list (tr ))
598
756
}
757
+
599
758
sort.val <- sapply(traces , function (tr ){
600
759
rank.val <- unlist(tr $ sort )
601
760
if (is.null(rank.val )){
@@ -606,6 +765,7 @@ layer2traces <- function(l, d, misc, plot=NULL){
606
765
0
607
766
}
608
767
})
768
+
609
769
ord <- order(sort.val )
610
770
no.sort <- traces [ord ]
611
771
for (tr.i in seq_along(no.sort )){
@@ -630,7 +790,6 @@ paramORdefault <- function(params, aesVec, defaults){
630
790
ggplot.value <- defaults [[ggplot.name ]]
631
791
}
632
792
if (is.null(ggplot.value )){
633
- print(defaults )
634
793
stop(" no ggplot default for " , ggplot.name )
635
794
}
636
795
convert <- aesConverters [[ggplot.name ]]
0 commit comments