Skip to content

Fixing Legend Position #241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ before_install:
- chmod 755 ./travis-tool.sh
- ./travis-tool.sh bootstrap
# password is encrypted below
- echo "Sys.setenv('plotly_username' = 'dfvd')" > ~/.Rprofile
- echo "Sys.setenv('plotly_username' = 'dsvgb')" > ~/.Rprofile

install:
- ./travis-tool.sh install_deps
Expand All @@ -26,7 +26,7 @@ after_success:
env:
global:
# plotly_api_key (for posting to plot.ly)
- secure: "eHO4OUmgLusU9i4xSvr3daZxnsMZw3x4FH8BYVEC8Ja+Ey6kkAFFSh2iAC/CqewEYJ7I/M8aIJYqlyTMGRYUgy36WU7iWHAGgaZOU8fIB5dWzMwHbIvS4Naq2sdU7lRT7sxkS+40K1+rplpWDoLF2yt8vSRWo9rjNzp+yc8PjXM="
- secure: "GTrq7AhGuufvuP6EfiI/tiFCcmCCkNDMQPV7Ux/djC6pdmhbbmmuB6AuRnYe9Z/pknPfWrjeyN3E/AFjzZtbRVQeVQSjlFILDRWPQhXdsXQ4P0XySTeRu4uRayS9NPFzNaNh1Kbrf/lq1+jkxKhlef1ZlUQqhuOch80vbXRFEyY="
# GITHUB_PAT (for pushing to plotly-test-table)
- secure: "LHJONgWOo+98vNeFLI7LSJU3RtbMVszlI79GB8CcXmc2mlgM/UtZ5b6RnkNlhmg3Gj1/uObfm/rIybVTwuS1yNpeKv73+gsZOYhobVXiUGVxdRFG/mg5mbqwyWkkuofjPGFlMZCEMgHim37eZzgjSibwVH1LClRDsCoFMCgvgV0="
# plotlyjs_full (link to the full offline bundle)
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create interactive web-based graphs via plotly's API
Version: 1.0.1
Version: 1.0.3
Authors@R: c(person("Chris", "Parmer", role = c("aut", "cph"),
email = "[email protected]"),
person("Scott", "Chamberlain", role = "aut",
Expand Down Expand Up @@ -38,6 +38,8 @@ Suggests:
knitr,
devtools,
shiny,
htmltools,
curl,
rmarkdown,
RColorBrewer
LazyData: true
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ import(ggplot2)
import(httr)
import(jsonlite)
importFrom(magrittr,"%>%")
importFrom(viridis,viridis)
11 changes: 11 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
1.0.3 -- 7 Aug 2015

Improved legend positioning. See #241

1.0.2 -- 2 Aug 2015

* last_plot() will now look for the last plotly object; if not found, it will try to find the last ggplot object.
* Officially added the filename, fileopt, and world_readable arguments to plot_ly() and ggplotly().
* If plotly offline is not available, the shiny.launch.browser option is changed to open a web brower. See #245.
* Various namespace/documentation improvements for R CMD check.

1.0.1 -- 2 Aug 2015

Removed the stream() function as it wasn't ready to be included.
Expand Down
33 changes: 24 additions & 9 deletions R/ggplotly.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @param filename character string describing the name of the plot in your plotly account.
#' Use / to specify directories. If a directory path does not exist it will be created.
#' If this argument is not specified and the title of the plot exists,
#' that will be used for the filename.
#' @param fileopt character string describing whether to create a "new" plotly, "overwrite" an existing plotly,
#' "append" data to existing plotly, or "extend" it.
#' @param world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All @@ -23,8 +32,13 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot()) {
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
hash_plot(p$data, l)
}

Expand Down Expand Up @@ -354,7 +368,7 @@ gg2list <- function(p) {
# x axis scale instead of on the grid 0-1 scale). This allows
# transformations to be used out of the box, with no additional d3
# coding.
theme.pars <- ggplot2:::plot_theme(p)
theme.pars <- getFromNamespace("plot_theme", "ggplot2")(p)

# Flip labels if coords are flipped - transform does not take care
# of this. Do this BEFORE checking if it is blank or not, so that
Expand Down Expand Up @@ -662,9 +676,11 @@ gg2list <- function(p) {
if (exists("increase_margin_r")) {
layout$margin$r <- 60
}
layout$legend <- list(bordercolor="transparent",
x=1.05, y=1/2,
xanchor="center", yanchor="top")
layout$legend <- list(bordercolor = "transparent",
x = 1.01,
y = 0.075 * 0.5* length(trace.list) + 0.45,
xref="paper", yref="paper",
xanchor = "left", yanchor = "top")

## Legend hiding when guides(fill="none").
legends.present <- unique(unlist(layer.legends))
Expand Down Expand Up @@ -709,15 +725,14 @@ gg2list <- function(p) {
nann <- 1
}
annotations[[nann]] <- list(text=legend.title,
x=layout$legend$x,
y=layout$legend$y * 1.04,
x = layout$legend$x * 1.0154,
y = 0.075 * 0.5* length(trace.list) + 0.55,
showarrow=FALSE,
xref="paper", yref="paper",
xanchor="center",
xanchor="left", yanchor = "top",
textangle=0)
layout$annotations <- annotations
}

# Family font for text
if (!is.null(theme.pars$text$family)) {
layout$titlefont$family <- theme.pars$text$family
Expand Down
22 changes: 19 additions & 3 deletions R/plotly.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,21 @@
#' @param color Either a variable name or a vector to use for color mapping.
#' @param colors Either a colorbrewer2.org palette name (e.g. "YlOrRd" or "Blues"),
#' or a vector of colors to interpolate in hexadecimal "#RRGGBB" format,
#' or a color interpolation function like \link{grDevices::colorRamp}.
#' or a color interpolation function like \code{colorRamp()}.
#' @param symbol Either a variable name or a (discrete) vector to use for symbol encoding.
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
#' @param size A variable name or numeric vector to encode the size of markers.
#' @param inherit should future traces inherit properties from this initial trace?
#' @param filename character string describing the name of the plot in your plotly account.
#' Use / to specify directories. If a directory path does not exist it will be created.
#' If this argument is not specified and the title of the plot exists,
#' that will be used for the filename.
#' @param fileopt character string describing whether to create a "new" plotly, "overwrite" an existing plotly,
#' "append" data to existing plotly, or "extend" it.
#' @param world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @references \url{https://plot.ly/r/reference/}
Expand All @@ -45,6 +54,7 @@
#'
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
filename, fileopt, world_readable = TRUE,
inherit = TRUE, evaluate = FALSE) {
# "native" plotly arguments
argz <- substitute(list(...))
Expand All @@ -69,6 +79,11 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL
)
# tack on special keyword arguments
if (!missing(filename)) p$filename <- filename
if (!missing(fileopt)) p$fileopt <- fileopt
p$world_readable <- world_readable

if (evaluate) p <- plotly_build(p)
hash_plot(data, p)
}
Expand All @@ -83,7 +98,7 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
#' @param color Either a variable name or a vector to use for color mapping.
#' @param colors Either a colorbrewer2.org palette name (e.g. "YlOrRd" or "Blues"),
#' or a vector of colors to interpolate in hexadecimal "#RRGGBB" format,
#' or a color interpolation function like \link{grDevices::colorRamp}.
#' or a color interpolation function like \code{colorRamp}.
#' @param symbol Either a variable name or a (discrete) vector to use for symbol encoding.
#' @param symbols A character vector of symbol types. Possible values:
#' 'dot', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-left', 'triangle-right', 'triangle-up'
Expand Down Expand Up @@ -182,6 +197,7 @@ style <- function(p = last_plot(), ..., traces = 1, evaluate = FALSE) {
#' list.
#'
#' @param l a ggplot object, or a plotly object, or a list.
#' @importFrom viridis viridis
#' @export
plotly_build <- function(l = last_plot()) {
# ggplot objects don't need any special type of handling
Expand Down
17 changes: 11 additions & 6 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ is.offline <- function(x) inherits(x, "offline")
# set a default for the offline bundle directory
if (Sys.getenv("plotly_offline") == "") {
Sys.setenv("plotly_offline" = "~/.plotly/plotlyjs")
# maybe rely a message if bundle is (or isn't) found?
# iframes won't work in RStudio viewer, so we override
# shiny's browser launch method
if (!has_offline())
options("shiny.launch.browser" = function(url) { browseURL(url) })
}
invisible(NULL)
}
Expand Down Expand Up @@ -61,13 +64,16 @@ get_plot <- function(data = NULL, last = FALSE) {
}
}

#' Retrive last plotly to be modified or created
#' Retrive and create the last plotly (or ggplot).
#'
#' @seealso \link{plotly_build}
#' @param data (optional) a data frame with a class of plotly (and a plotly_hash attribute).
#' @export
#'
last_plot <- function(...) {
p <- get_plot(..., last = TRUE)
last_plot <- function(data = NULL) {
p <- try(get_plot(data, last = TRUE), silent = TRUE)
if (inherits(p, "try-error")) p <- try(ggplotly(), silent = TRUE)
if (inherits(p, "try-error")) stop("The last plot doesn't exist")
structure(
p,
class = unique(c("plotly", class(p)))
Expand Down Expand Up @@ -143,8 +149,7 @@ get_domain <- function(type = "main") {

# plotly's special keyword arguments in POST body
get_kwargs <- function() {
c("filename", "fileopt", "style", "traces", "layout",
"world_readable", "kwarg_example")
c("filename", "fileopt", "style", "traces", "layout", "world_readable")
}

# POST header fields
Expand Down
2 changes: 1 addition & 1 deletion man/add_trace.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ a different trace will be created for each unique value.}

\item{colors}{Either a colorbrewer2.org palette name (e.g. "YlOrRd" or "Blues"),
or a vector of colors to interpolate in hexadecimal "#RRGGBB" format,
or a color interpolation function like \link{grDevices::colorRamp}.}
or a color interpolation function like \code{colorRamp}.}

\item{symbol}{Either a variable name or a (discrete) vector to use for symbol encoding.}

Expand Down
14 changes: 13 additions & 1 deletion man/ggplotly.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,22 @@
\alias{ggplotly}
\title{Create plotly graphs using ggplot2 syntax}
\usage{
ggplotly(p = ggplot2::last_plot())
ggplotly(p = ggplot2::last_plot(), filename, fileopt, world_readable = TRUE)
}
\arguments{
\item{p}{a ggplot object.}

\item{filename}{character string describing the name of the plot in your plotly account.
Use / to specify directories. If a directory path does not exist it will be created.
If this argument is not specified and the title of the plot exists,
that will be used for the filename.}

\item{fileopt}{character string describing whether to create a "new" plotly, "overwrite" an existing plotly,
"append" data to existing plotly, or "extend" it.}

\item{world_readable}{logical. If \code{TRUE}, the graph is viewable
by anyone who has the link and in the owner's plotly account.
If \code{FALSE}, graph is only viewable in the owner's plotly account.}
}
\description{
See up-to-date documentation and examples at
Expand Down
9 changes: 6 additions & 3 deletions man/last_plot.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
% Please edit documentation in R/utils.R
\name{last_plot}
\alias{last_plot}
\title{Retrive last plotly to be modified or created}
\title{Retrive and create the last plotly (or ggplot).}
\usage{
last_plot(...)
last_plot(data = NULL)
}
\arguments{
\item{data}{(optional) a data frame with a class of plotly (and a plotly_hash attribute).}
}
\description{
Retrive last plotly to be modified or created
Retrive and create the last plotly (or ggplot).
}
\seealso{
\link{plotly_build}
Expand Down
19 changes: 16 additions & 3 deletions man/plot_ly.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
\title{Initiate a plotly visualization}
\usage{
plot_ly(data = data.frame(), ..., type = "scatter", group, color, colors,
symbol, symbols, size, inherit = TRUE, evaluate = FALSE)
symbol, symbols, size, filename, fileopt, world_readable = TRUE,
inherit = TRUE, evaluate = FALSE)
}
\arguments{
\item{data}{A data frame (optional).}
Expand All @@ -22,7 +23,7 @@ a different trace will be created for each unique value.}

\item{colors}{Either a colorbrewer2.org palette name (e.g. "YlOrRd" or "Blues"),
or a vector of colors to interpolate in hexadecimal "#RRGGBB" format,
or a color interpolation function like \link{grDevices::colorRamp}.}
or a color interpolation function like \code{colorRamp()}.}

\item{symbol}{Either a variable name or a (discrete) vector to use for symbol encoding.}

Expand All @@ -31,7 +32,19 @@ or a color interpolation function like \link{grDevices::colorRamp}.}

\item{size}{A variable name or numeric vector to encode the size of markers.}

\item{inherit}{should future traces inherit properties from this initial trace?}
\item{filename}{character string describing the name of the plot in your plotly account.
Use / to specify directories. If a directory path does not exist it will be created.
If this argument is not specified and the title of the plot exists,
that will be used for the filename.}

\item{fileopt}{character string describing whether to create a "new" plotly, "overwrite" an existing plotly,
"append" data to existing plotly, or "extend" it.}

\item{world_readable}{logical. If \code{TRUE}, the graph is viewable
by anyone who has the link and in the owner's plotly account.
If \code{FALSE}, graph is only viewable in the owner's plotly account.}

\item{inherit}{logical. Should future traces inherit properties from this initial trace?}

\item{evaluate}{logical. Evaluate arguments when this function is called?}
}
Expand Down
34 changes: 34 additions & 0 deletions tests/testthat/test-ggplot-legend.R
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,37 @@ test_that("0 breaks -> 3 traces with showlegend=FALSE", {
computed.showlegend <- sapply(info$traces, "[[", "showlegend")
expect_identical(as.logical(computed.showlegend), expected.showlegend)
})

# test of legend position
test_that("very long legend items", {
long_items <- data.frame(cat1 = sample(x = LETTERS[1:10],
size = 100, replace = TRUE),
cat2 = sample(x = c("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCC"),
size = 100, replace = TRUE))
p_long_items <- ggplot(long_items, aes(cat1, fill=cat2)) +
geom_bar(position="dodge")
info <- expect_traces(p_long_items, 3, "very long legend items")
expect_equal(length(info$layout$annotations), 1)
expected.names <- levels(long_items$cat2)
expect_identical(info$layout$annotations[[1]]$y -
info$layout$legend$y > 0, TRUE)
})

# test of legend position
test_that("many legend items", {
many_items <- data.frame(cat1 = sample(x = paste0("Group ", LETTERS[1:12]),
size = 100, replace = TRUE),
cat2 = sample(x = c("foo", "bar", "baz"),
size = 100, replace = TRUE))
p_many_items <- ggplot(many_items, aes(cat2, fill=cat1)) +
geom_bar(position="dodge")
info <- expect_traces(p_many_items, 12, "many legend items")
expect_equal(length(info$layout$annotations), 1)
expected.names <- levels(many_items$cat2)
expect_identical(info$layout$annotations[[1]]$y > 0.5, TRUE)
expect_identical(info$layout$annotations[[1]]$y -
info$layout$legend$y > 0, TRUE)
})

6 changes: 2 additions & 4 deletions tests/testthat/test-plotly-filename.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ context("Filename")

test_that("filepath with directories is returned as passed", {
p <- print(plot_ly(mtcars, x = wt, y = vs, filename = "directory/awesome"))
usr <- sub("https://plot.ly/~(.*)/[0-9]+", "\\1", p$url)
id <- sub("https://plot.ly/~.*/([0-9]+)", "\\1", p$url)
fig <- plotly_build(get_figure(usr, id))
expect_identical(fig$data[[1]]$filename, "directory/awesome")
# why is the directory name replicated in the response?
expect_identical(p$filename, "directorydirectory/awesome")
})