Skip to content

Commit 50e8141

Browse files
authored
Merge pull request #373 from jiwalker-usgs/master
Legend!
2 parents 5f0bf48 + ea72de6 commit 50e8141

20 files changed

+155
-157
lines changed

R/abline.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#' gsNew <- points(gs, y=1, x=2, col="blue", pch=18, legend.name="Points")
2222
#' gsNew <- lines(gsNew, c(3,4,3), c(2,4,6), legend.name="Lines")
2323
#' gsNew <- abline(gsNew, b=1, a=0, legend.name="1:1")
24-
#' gsNew <- legend(gsNew, "topleft",title="Awesome!")
24+
#' gsNew <- legend(gsNew, location="topleft",title="Awesome!")
2525
#' gsNew
2626
abline <- function(object, ...) {
2727
override("graphics", "abline", object, ...)

R/legend.R

Lines changed: 53 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ legend <- function(object, ...){
6161

6262

6363
legend.gsplot <- function(object, ..., location="topright", legend_offset=0.3) {
64-
6564
object <- modify_legend(object, location = location, legend_offset = legend_offset, draw = TRUE, ...)
66-
6765
return(object)
6866
}
6967

@@ -76,78 +74,78 @@ legend.gsplot <- function(object, ..., location="topright", legend_offset=0.3) {
7674

7775
draw_legend <- function(gsplot) {
7876

77+
default.args <- formals(graphics::legend)
7978

80-
draw <- gsplot[['legend']][['gs.config']][['draw']]
81-
legend_args_exist <- FALSE
82-
if (exists('legend', gsplot)){
83-
legend_args_exist <- exists('legend.args', gsplot[['legend']])
84-
}
85-
if (is.null(draw) || !draw || !legend_args_exist){ return() }
79+
if ("legend" %in% names(gsplot)){
8680

87-
oldXPD <- par()$xpd
88-
oldBg <- par('bg')
89-
90-
for(index in which(names(gsplot[['legend']]) %in% "gs.config")){
91-
92-
par(xpd=TRUE)
81+
# TODO rather than preserve individual pars we should par-scope this draw_legend call
82+
# and par scope each run of the for loop
83+
oldXPD <- par()$xpd
84+
oldBg <- par('bg')
9385

94-
if("legend.args" %in% names(gsplot[['legend']])) {
95-
96-
default.args <- formals(graphics::legend)
97-
legendParamsALL <- gsplot[['legend']][['legend.args']]
86+
for (legend.name in names(gsplot[['legend']])) {
9887

99-
overallLegendArgs <- appendLegendPositionConfiguration(gsplot[['legend']][['gs.config']])
100-
legendParamsALL <- append(legendParamsALL, overallLegendArgs)
101-
legendOrdered <- legendParamsALL[na.omit(match(names(default.args), names(legendParamsALL)))]
102-
103-
#for above/below, dynamically set the number of columns
104-
location <- gsplot[['legend']][['gs.config']][['location']]
105-
if(location == "below" || location == "above") {
106-
itemsPerCol <- 3 #TODO load this from config
107-
cols <- length(legendOrdered$legend) %/% 3;
108-
if(length(legendOrdered$legend) %% 3 > 0) {
109-
cols <- cols + 1
88+
par(xpd=TRUE)
89+
90+
legend <- gsplot[['legend']][[legend.name]]
91+
if (legend$draw) {
92+
legend <- appendLegendColumnInfo(legend)
93+
legend <- appendLegendPositionConfiguration(legend)
94+
# set required legend argument to NA if not exists
95+
if (!"legend" %in% names(legend)) {
96+
legend$legend <- NA
11097
}
111-
legendOrdered$ncol <- ifelse(is.null(legendOrdered$ncol), cols, legendOrdered$ncol)
112-
}
11398

114-
#set bg so that fill/border/etc args are correct, then evaluate any quoted list items
115-
if(any(names(overallLegendArgs) %in% c("bg"))) {
116-
par(bg=overallLegendArgs$bg)
99+
#set bg so that fill/border/etc args are correct, then evaluate any quoted list items
100+
if (any(names(legend) %in% c("bg"))) {
101+
par(bg=legend$bg)
102+
}
103+
legend <- lapply(legend, function(x) {unname(sapply(x, function(x) {eval(x)}))})
104+
# clean out arguments not allowed by legend
105+
legend <- legend[na.omit(match(names(default.args), names(legend)))]
106+
legend(legend)
107+
par(xpd=oldXPD)
108+
par(bg=oldBg)
117109
}
118-
legendComplete <- lapply(legendOrdered, function(x) {unname(sapply(x, function(x) {eval(x)}))})
119-
120-
} else {
121-
legendComplete <- appendLegendPositionConfiguration(gsplot[['legend']][[index]])
122110
}
123-
124-
legend(legendComplete)
125-
par(xpd=oldXPD)
126-
par(bg=oldBg)
127111
}
128-
129112
}
130113

131-
appendLegendPositionConfiguration <- function(gsConfig) {
114+
appendLegendPositionConfiguration <- function(legend) {
132115
#TODO support explicit x/y coords
133-
legend_offset <- gsConfig$legend_offset
134-
location <- gsConfig$location
135-
gsConfig$legend_offset <- NULL
136-
gsConfig$location <- NULL
116+
legend_offset <- legend$legend_offset
117+
location <- legend$location
118+
legend$legend_offset <- NULL
137119

138120
if(location == "below") {
139-
return(append(gsConfig, list(x = "bottom", y = NULL, inset=c(0, -legend_offset), bty="n")))
121+
return(append(legend, list(x = "bottom", y = NULL, inset=c(0, -legend_offset), bty="n")))
140122
} else if(location == "above") {
141-
return(append(gsConfig, list(x = "top", y = NULL, inset=c(0, -legend_offset), bty="n")))
123+
return(append(legend, list(x = "top", y = NULL, inset=c(0, -legend_offset), bty="n")))
142124
} else if(location == "toright") {
143-
return(append(gsConfig, list(x = "right", y = NULL, inset=c(-legend_offset, 0), bty="n")))
125+
return(append(legend, list(x = "right", y = NULL, inset=c(-legend_offset, 0), bty="n")))
144126
} else if(location == "toleft") {
145-
return(append(gsConfig, list(x = "left", y = NULL, inset=c(-legend_offset, 0), bty="n")))
146-
} else if("x" %in% names(gsConfig)){
147-
return(gsConfig)
127+
return(append(legend, list(x = "left", y = NULL, inset=c(-legend_offset, 0), bty="n")))
128+
} else if("x" %in% names(legend)){
129+
return(legend)
148130
} else {
149-
return(append(gsConfig, list(x = location)))
131+
return(append(legend, list(x = location)))
132+
}
133+
}
134+
135+
#' Based on location set legend columns
136+
#'
137+
#' @param legend to set columns on
138+
appendLegendColumnInfo <- function(legend) {
139+
location <- legend[['location']]
140+
if (location == "below" || location == "above") {
141+
itemsPerCol <- 3 # TODO load this from config
142+
cols <- length(legend$legend) %/% itemsPerCol;
143+
if(length(legend$legend) %% itemsPerCol > 0) {
144+
cols <- cols + 1
145+
}
146+
legend$ncol <- ifelse(is.null(legend$ncol), cols, legend$ncol)
150147
}
148+
return(legend)
151149
}
152150

153151

R/modify_legend.R

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,22 @@ add_to_legend <- function(object, fun.name, legend.name, call.args, option.args)
2323
if(is.null(legend.name)) {
2424
return(object)
2525
}
26-
27-
28-
# add legend$gs.config if it does not already exist
29-
legend.exists <- "legend" %in% names(object)
30-
legend.args.exist <- "legend.args" %in% names(object[['legend']])
31-
if(!legend.exists){object <- modify_legend(object)}
32-
33-
# add/add to legend$legend.args
26+
if(!exists("legend", object)) {object$legend <- list()}
27+
if(!exists("legend.auto", object$legend)) {object$legend$legend.auto <- create_empty_legend()}
28+
29+
# add/add to legend$legend.auto
3430
if(length(legend.name) > 1){
3531
call.args.df <- as.data.frame(call.args, stringsAsFactors = FALSE)
3632

3733
for(p in seq(nrow(call.args.df))) {
38-
if(p > 1){ legend.args.exist <- TRUE } #legend args will exist after the first loop
3934
call.args.list <- as.list(call.args.df[p,])
4035
fun.legend.args <- get_legend_args(fun.name, call.args.list, legend.name[p], option.args)
41-
object[['legend']][['legend.args']] <- combine_legend_args(object, fun.legend.args, legend.args.exist)
36+
object[['legend']][['legend.auto']] <- combine_legend_args(object, fun.legend.args)
4237
}
4338

4439
} else {
4540
fun.legend.args <- get_legend_args(fun.name, call.args, legend.name, option.args)
46-
object[['legend']][['legend.args']] <- combine_legend_args(object, fun.legend.args, legend.args.exist)
41+
object[['legend']][['legend.auto']] <- combine_legend_args(object, fun.legend.args)
4742
}
4843

4944
return(object)
@@ -85,7 +80,7 @@ get_legend_args <- function(fun.name, call.args, legend.name, option.args){
8580
call.args <- set_type_params(call.args, type.name, params.needed)
8681
if(type.name %in% c('p', 'lchsS')) {fun.name <- switch(type.name, p="points", lchsS="lines")}
8782
}
88-
83+
fun.specific <- list()
8984
if (fun.name == "points") {
9085
pt.names <- c("lwd","bg","cex")
9186
names(call.args) <- replace(names(call.args), which(names(call.args) %in% pt.names),
@@ -145,46 +140,61 @@ set_type_params <- function(list, type.name, params){
145140
#' @param legend.args.exist
146141
#' @param .dots lazy_dots
147142
#' @keywords internal
148-
combine_legend_args <- function(object, new.legend.args, legend.args.exist, ...){
143+
combine_legend_args <- function(object, new.legend.args, ...){
149144

150-
# look for existing list of legend args in the object
151-
if(!legend.args.exist){
152-
default.args <- formals(graphics::legend)
153-
overall.legend <- c("x", "y", "bty", "bg", "box.lty", "box.lwd", "box.col", "cex",
154-
"xjust", "yjust", "x.intersp", "y.intersp", "adj", "text.width",
155-
"merge", "trace", "plot", "ncol", "horiz", "title", "inset",
156-
"xpd", "title.col", "title.adj", "seg.len")
157-
not.overall <- default.args[which(!names(default.args) %in% overall.legend)]
158-
legend.args <- vector("list", length(not.overall))
159-
names(legend.args) <- names(not.overall)
160-
# // set up config legend stuff
161-
} else {
162-
legend.args <- object[['legend']][['legend.args']]
163-
}
145+
legend.args <- object[['legend']][['legend.auto']]
164146

165-
orderedParams <- new.legend.args[match(names(legend.args), names(new.legend.args))]
147+
orderedParams <- new.legend.args[match(names(legend.args), names(new.legend.args))]
166148
for (j in seq_along(legend.args)) {
167149
legend.args[[j]] <- c(legend.args[[j]], orderedParams[[j]])
168150
}
169151

170152
return(legend.args)
171153
}
172154

155+
#' Set up an empty legend
156+
#'
157+
create_empty_legend <- function() {
158+
default.args <- formals(graphics::legend)
159+
overall.legend <- c("x", "y", "bty", "bg", "box.lty", "box.lwd", "box.col", "cex",
160+
"xjust", "yjust", "x.intersp", "y.intersp", "adj", "text.width",
161+
"merge", "trace", "plot", "ncol", "horiz", "title", "inset",
162+
"xpd", "title.col", "title.adj", "seg.len")
163+
not.overall <- default.args[which(!names(default.args) %in% overall.legend)]
164+
legend <- vector("list", length(not.overall))
165+
names(legend) <- names(not.overall)
166+
167+
# add draw = FALSE as default
168+
legend$draw <- FALSE
169+
170+
return(legend)
171+
}
172+
173173
#' add legend configs
174174
#'
175175
#' @param object
176176
#' @keywords internal
177177
modify_legend <- function(object, location="topright", legend_offset=0.3, draw=FALSE, ...){
178178
# // this should be shared between add_to_legend and legend
179179
# // check if legend exists, if not add it (someone could call legend before any legend.names)
180-
gsConfig <- list(location = location, legend_offset = legend_offset, draw = draw, ...)
181-
180+
legend.config <- list(location = location, legend_offset = legend_offset, draw = draw, ...)
181+
182182
arguments <- list(...)
183-
if("x" %in% names(arguments)){
184-
gsConfig$location <- gsConfig$x
185-
gsConfig$x <- NULL
186-
}
183+
# auto is used when "legend" arg comes from "legend.name" in gsplot calls
184+
legend.index <- ifelse("legend" %in% names(legend.config),length(grep("legend.\\d+", names(object$legend))) + 1, "auto")
187185

188-
object[['legend']][['gs.config']] <- gsConfig
186+
if ("x" %in% names(arguments)){
187+
legend.config$location <- legend.config$x
188+
legend.config$x <- NULL
189+
}
190+
if (legend.index == 'auto') {
191+
# Merge new legend config into existing auto legend if it exists
192+
auto.legend <- object$legend$legend.auto
193+
# if draw is true, stay true
194+
legend.config$draw <- legend.config$draw || is.null(auto.legend) || auto.legend$draw
195+
auto.legend[names(legend.config)] <- legend.config
196+
legend.config <- auto.legend
197+
}
198+
object[['legend']][[paste0("legend.", legend.index)]] <- legend.config
189199
return(object)
190200
}

0 commit comments

Comments
 (0)