简体   繁体   中英

Wrap horizontal legend across multiple rows

Suppose I have data like the following:

    lab <- "A really really long string!"
    dat <- data.frame(grp = paste(1:6,lab),x=1:6,y=runif(6))

When plotting a legend with strings this long, sometimes it can be a challenge to get the legend to fit nicely. If I have to I can always abbreviate the strings to shorten them, but I was wondering if it's possible (most likely using some grid magic) to 'wrap' a legend across multiple rows or columns. For instance, say I position the legend on the bottom, horizontally:

    ggplot(dat,aes(x=x,y=y,colour=grp)) + geom_point() + 
        opts(legend.position="bottom",legend.direction="horizontal")

Is it possible to get this legend to display as two rows of three, rather than one row of six?

To wrap long strings, use strwrap .

lipsum <- "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper tellus vitae libero placerat aliquet egestas tortor semper. Maecenas pellentesque euismod tristique. Donec semper interdum magna, commodo vehicula ante hendrerit vitae. Maecenas at diam sollicitudin magna mollis lobortis. In nibh elit, tincidunt eu lobortis ac, molestie a felis. Proin turpis leo, iaculis non commodo quis, venenatis at justo. Duis in magna vel erat fringilla gravida quis non nisl. Nunc lacus magna, varius eu luctus vel, luctus tristique sapien. Suspendisse mi dolor, vestibulum at facilisis elementum, lacinia vitae metus. Etiam ut nisl urna, vel tempus mi. In hac habitasse platea dictumst. Quisque pretium volutpat felis, nec tempor diam faucibus at. Praesent volutpat posuere sapien, eu vulputate risus molestie vitae. Proin iaculis quam non leo porttitor hendrerit."

strwrap(lipsum)
cat(strwrap(lipsum), sep = "\n")
# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper tellus
# vitae libero placerat aliquet egestas tortor semper. Maecenas pellentesque euismod
# tristique. Donec semper interdum magna, commodo vehicula ante hendrerit vitae. Maecenas
# at diam sollicitudin magna mollis lobortis. In nibh elit, tincidunt eu lobortis ac,
# molestie a felis. Proin turpis leo, iaculis non commodo quis, venenatis at justo. Duis
# in magna vel erat fringilla gravida quis non nisl. Nunc lacus magna, varius eu luctus
# vel, luctus tristique sapien. Suspendisse mi dolor, vestibulum at facilisis elementum,
# lacinia vitae metus. Etiam ut nisl urna, vel tempus mi. In hac habitasse platea
# dictumst. Quisque pretium volutpat felis, nec tempor diam faucibus at. Praesent
# volutpat posuere sapien, eu vulputate risus molestie vitae. Proin iaculis quam non leo
# porttitor hendrerit.

Try this. I wrote this for very long titles but it works for any long string. You still have to figure out the linelength for your instance.

# splits title of plot if to long
splittitle=function(title,linelength=40)
{
    spltitle<-strsplit(title,' ')
    splt<-as.data.frame(spltitle)
    title2<-NULL
    title3<-NULL
    titlelength<-round(nchar(title)/round(nchar(title)/linelength))
    dimsplt<-dim(splt)
    n=1
    doonce2=0
    for(m in 1:round(nchar(title)/linelength)){
  doonce=0
    doonce2=0
    for(l in n:dimsplt[1]){
        if(doonce==0){title2<-title3}
        title2=paste(title2,splt[l,],sep=' ')
        if(doonce2==0){if(nchar(title2)>=(titlelength*m)){title3=paste(title2,'\n',sep='')
        n<-(l+1)
        doonce2=1}
        }
        doonce=1
    }
    }
    title2
}

lab <- "A really really long string!A really really long string!A really really long string!A really really long string!A really really long string!A really really long string!A really really long string!A really really long string!"
lab2<-splittitle(lab)
cat(lab)
cat(lab2)


library('ggplot2')

1 original

dat <- data.frame(grp = paste(1:6,lab2),x=1:6,y=runif(6))

ggplot(dat,aes(x=x,y=y,colour=grp)) + geom_point() + 
    opts(legend.position="bottom",legend.direction="horizontal")

2 using splittitle

dat <- data.frame(grp = paste(1:6,lab2),x=1:6,y=runif(6))
ggplot(dat,aes(x=x,y=y,colour=grp)) + geom_point() + 
    opts(legend.position="bottom",legend.direction="horizontal")

The earlier mentioned splittitle almost works, but for example

> splittitle("abc defg hi jkl m", 6)
[1] " abc defg\\n hi\\n jkl m"

does not really give you what you want...

One trick is to use RGraphics::splitString which

"Splits a single string into multiple lines (by inserting line breaks) so that the output will fit within the current viewport."

Then you just change the viewport temporarily. The function below did the trick for me, but is still only a quick & dirty -solution. I used it to wrap a legend title.

library(RGraphics)
multiLines <- function(text, maxWidth=11) {
  textLen = nchar(text)
  maxHeight = ceiling(textLen/maxWidth)*1.5
  vp=viewport(width=maxWidth,height=maxHeight, default.units="char")
  pushViewport(vp) #activate the viewport
  text2 = splitString(text) #given vp, split the text
  popViewport() #get rid of it
  return(text2)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM