Skip to content

Adding a scale bar and north arrow to a ggplot

8 messages · Paul Hiemstra, Roger Bivand, Hadley Wickham +2 more

#
Hi people,

I posted a similar question to the ggplot2 mailing list and with their 
help and a lot of tinkering I got a well working function to add a 
scalebar to a ggplot plot. I could add the function to automap, but is 
there another package which would be more appropraite, e.g. sp (Roger?)?

cheers,
Paul

ps: new version of code here:

makeNiceNumber = function(num, num.pretty = 1) {
   # Rounding provided by code from Maarten Plieger
   return((round(num/10^(round(log10(num))-1))*(10^(round(log10(num))-1))))
}

createBoxPolygon = function(llcorner, width, height) {
   relativeCoords = data.frame(c(0, 0, width, width, 0), c(0, height, 
height, 0, 0))
   names(relativeCoords) = names(llcorner)
   return(t(apply(relativeCoords, 1, function(x) llcorner + x)))
}

addScaleBar = function(ggplot_obj, spatial_obj, attribute, addParams = 
list()) {
   addParamsDefaults = list(noBins = 5, xname = "x", yname = "y", unit = 
"m", placement = "bottomright",
                            sbLengthPct = 0.3, sbHeightvsWidth = 1/14)
   addParams = modifyList(addParamsDefaults, addParams)

   range_x = max(spatial_obj[[addParams[["xname"]]]]) - 
min(spatial_obj[[addParams[["xname"]]]])
   range_y = max(spatial_obj[[addParams[["yname"]]]]) - 
min(spatial_obj[[addParams[["yname"]]]])
   lengthScalebar = addParams[["sbLengthPct"]] * range_x
   ## OPTION: use pretty() instead
   widthBin = makeNiceNumber(lengthScalebar / addParams[["noBins"]])
   heightBin = lengthScalebar * addParams[["sbHeightvsWidth"]]
   lowerLeftCornerScaleBar = c(x = 
max(spatial_obj[[addParams[["xname"]]]]) - (widthBin * 
addParams[["noBins"]]),
                               y = min(spatial_obj[[addParams[["yname"]]]]))

   scaleBarPolygon = do.call("rbind", lapply(0:(addParams[["noBins"]] - 
1), function(n) {
     dum = data.frame(createBoxPolygon(lowerLeftCornerScaleBar + c((n * 
widthBin), 0), widthBin, heightBin))
     if(!(n + 1) %% 2 == 0) dum$cat = "odd" else dum$cat = "even"
     return(dum)
   }))
   scaleBarPolygon[[attribute]] = min(spatial_obj[[attribute]])
   textScaleBar = data.frame(x = 
lowerLeftCornerScaleBar[[addParams[["xname"]]]] + 
(c(0:(addParams[["noBins"]])) * widthBin),
                             y = 
lowerLeftCornerScaleBar[[addParams[["yname"]]]],
                             label = 
as.character(0:(addParams[["noBins"]]) * widthBin))
   textScaleBar[[attribute]] = min(spatial_obj[[attribute]])

   return(ggplot_obj +
     geom_polygon(data = subset(scaleBarPolygon, cat == "odd"), fill = 
"black", color = "black", legend = FALSE) +
     geom_polygon(data = subset(scaleBarPolygon, cat == "even"), fill = 
"white", color = "black", legend = FALSE) +
     geom_text(aes(label = label), color = "black", size = 6, data = 
textScaleBar, hjust = 0.5, vjust = 1.2, legend = FALSE))
}

library(ggplot2)
library(sp)

data(meuse)
data(meuse.grid)
ggobj = ggplot(aes(x = x, y = y, color = zinc), data = meuse) + geom_point()
# Make sure to increase the graphic device a bit
addScaleBar(ggobj, meuse, "zinc", addParams = list(noBins = 5))
On 11/18/2010 09:12 PM, Paul Hiemstra wrote:

  
    
#
On Wed, 15 Dec 2010, Paul Hiemstra wrote:

            
Paul,

If it was added to sp, sp would depend on ggplot2 and its dependencies, 
which are quite extensive, and include a circularity, because ggplot2 
suggests maptools, which in turn depends on sp. Consequently, sp is not a 
good idea. It might even make sense to split sp into sp with just classes 
and methods, and spViz for vizualisation methods, but changing things now 
is a bit late!

It will be cleaner to try to establish the mapping functionality that uses 
ggplot2 and sp as a separate package. Maybe Hadley would see this as a 
sensible development. There are already two supplements to ggplot2 on 
R-forge, but both moribund, I think, which suggests that this needs 
thinking through.

Roger

  
    
#
On 12/15/2010 09:53 AM, Roger Bivand wrote:
Or make a new geom_scalebar :). An alternative could be an sp-contrib 
package where any additional code relevant to sp can be put.

Paul

  
    
#
I have a student who is interested in working on more spatial features
for ggplot2 this summer. That work is likely to involve separating out
all spatial features from ggplot2 into their own package and that
would be a good place for this.

Hadley
On Wednesday, December 15, 2010, Paul Hiemstra <p.hiemstra at geo.uu.nl> wrote:

  
    
#
I think both packages have good reasons to not want to depend on
eachother, as integrating this in one of them would imply. Your solution
seems a reasonable one to me.
On 12/15/2010 02:07 PM, Hadley Wickham wrote:

  
    
#
I'd love to see a separate, dedicated package bringing spatial
functionalities to ggplot2 - this is such a great tool. However, it
seems to me that a significant problem when using ggplot2 is the lack
of geom dedicated to raster data.

My experience is you can't really deal with big rasters using
geom_tile, it takes too much memory. I guess the development of a
geom_raster is necessary to fully mimic spplot() functionalities - no
idea how demanding an effort would that be.

Pierre

2010/12/15 Roger Bivand <Roger.Bivand at nhh.no>:
#
This is something that I hope the student would explore over summer.

Hadley

On Wed, Dec 15, 2010 at 2:27 PM, Pierre Roudier
<pierre.roudier at gmail.com> wrote:

  
    
#
Hi Hadley,

That would great if rasters could be printed more efficiently. Could you 
send me an e-mail, or post on the r-sig-geo list when you are ready to 
receive any contributed code?

Paul
On 12/15/2010 10:20 PM, Hadley Wickham wrote: