Parsimonious Way to Add North Arrow and Scale Bar to Ggmap

Parsimonious way to add north arrow and scale bar to ggmap

It looks like map.scale and north.arrow are designed to work with base graphics, but ggplot uses grid graphics. I'm not that familiar with graphing spatial data, but as a quick hack for the North arrow, the code below includes two different options:

ggmap(map, extent= "device") +
geom_segment(arrow=arrow(length=unit(3,"mm")), aes(x=33.5,xend=33.5,y=-2.9,yend=-2.6),
colour="yellow") +
annotate(x=33.5, y=-3, label="N", colour="yellow", geom="text", size=4) +
geom_segment(arrow=arrow(length=unit(4,"mm"), type="closed", angle=40),
aes(x=33.7,xend=33.7,y=-2.7,yend=-2.6), colour=hcl(240,50,80)) +
geom_label(aes(x=33.7, y=-2.75, label="N"),
size=3, label.padding=unit(1,"mm"), label.r=unit(0.4,"lines"))

Sample Image

Adding a legend, scalebar and north arrow to my ggmap

as already pointed out in the comment, you should not remove the guide(fill), thats your legend.

As for adding a scale bar and a north arrow I stumbled over the nice ggsn package! I added some lines to your code. You should play around with the positioning.

library(RgoogleMaps)
library(ggmap)
library(ggsn)

Finalmap<- get_map(location = c(lon = -23.17, lat = 15.2), zoom = 11,
maptype = "hybrid", scale = 2)

ggmap(Finalmap) +
geom_point(data = Total_Surveys,
aes(x = Longitude,
y = Latitude,
fill = Survey_Type,
alpha = 0.8),
size = 5,
shape = 21) +
coord_equal() + # needed for ggsn
guides(alpha=FALSE, size=FALSE) +
ggsn::north(x.min = -23.3, x.max = -23.28,
y.min = 15.36, y.max = 15.41, scale = 1.5) +
ggsn::scalebar(x.min = -23.3, x.max = -23.25,
y.min = 15.33, y.max = 15.35,
dist = 5, dd2km = TRUE,
model = "WGS84", height = 0.5,
st.dist = 0.5
)

Sample Image

scale bar and north arrow on map-ggplot2

A few years back I produced some code that could draw a scalebar (see also this post on r-sig-geo), this is the code I wrote back then. You could give it a go:

First some support functions:

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)))
}

And the real function:

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))
}

And some example code:

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))

Add easily readable scale bar to ggmap (using ggsn package?)

The development version of scalebar at Github has new arguments, st.color and box.fill, which allow better customisation of the bar.

Install that version using:

devtools::install_github('oswaldosantos/ggsn')

And then use e.g.:

ggmap(Map) + 
my_scalebar(x.min = 144.5, x.max = 147.5,
y.min = 13.5, y.max = 16.5,
dist = 50, dd2km = TRUE, model = 'WGS84',
box.fill = c("yellow", "white), st.color = "white")

Sample Image

OLDER ANSWER

I wrote this answer on how to modify the function before discovering the newer version.

I think the best approach here is to modify the scalebar function.

First, you need to load the ggsn package and edit the function:

library(ggsn)
my_scalebar <- edit(scalebar)

This should pop up an editor. Modify the top of the function so that it looks like this:

function (data = NULL, location = "bottomright", dist, height = 0.02, 
st.dist = 0.02, st.bottom = TRUE, st.size = 5, dd2km = NULL,
model, x.min, x.max, y.min, y.max, anchor = NULL, facet.var = NULL,
facet.lev = NULL, box2_fill = "yellow", legend_color = "white")
{
require(maptools)

We added 3 things:

  • an argument box2_fill with default value = "yellow"
  • an argument legend_color with default value = "white"
  • require maptools because the function uses gcDestination() from that package

Next, look for the line that begins gg.box2 and alter it to use the value for box2_fill:

  gg.box2 <- geom_polygon(data = box2, aes(x, y), fill = box2_fill, 
color = "black")

Then, edit the section near the bottom of the function to use the value for legend_color:

else {
gg.legend <- annotate("text", label = paste0(legend[,
"text"], "km"), x = x.st.pos, y = st.dist, size = st.size,
color = legend_color)
}

Saving will close the editor and save the new function my_scalebar to your work space.

Now you can use the new function. You can either supply values to the new arguments box2_fill = and legend_color =, or just try the defaults:

ggmap(Map) + 
my_scalebar(x.min = 144.5, x.max = 147.5,
y.min = 13.5, y.max = 16.5,
dist = 50, dd2km = TRUE, model = 'WGS84')

And if you think your edits are useful, you could suggest them to the package developer using a pull request at Github. NOTE: it looks like the developer has started to address this issue, since the Github version of scalebar has new box.fill and st.color arguments.

Adding scale bar to map made with ggRGB

You could use package ggspatial

ggRGB(img = lsat,
r = 3,
g = 2,
b = 1,
stretch = 'hist') +
theme_void() +
ggspatial::annotation_scale(location = "tr", width_hint =0.5, pad_x = unit(0.7, "cm"))

Sample Image

If you want more fine control over the appearance, a slightly more verbose but highly customizable method is also given in this answer https://stackoverflow.com/a/39069955/2761575



Related Topics



Leave a reply



Submit