6

I have a simple map like this:

library(maps)
library(ggplot2)    
rs=map("world", col="gray80", 
          xlim=c(32.002755, 44.283487),
          ylim=c(12.075434, 30.211327),
          resolution=0, bg="white", lty=1, lwd=2, fill=T)
redsea= map_data(rs)

Then I plot it with ggplot2:

ggplot()+
   geom_polygon(data=redsea, 
          aes(long,lat, group=group, label=redsea$region), color="black", fill="gray80")+
   coord_fixed(xlim=c(32.002755, 44.283487),ylim=c(12.075434, 30.211327), ratio=1.3)+
   theme_bw()

The map looks perfect for me but I need to add scale bar. I used the following code to do so:

ggplot(....)+scalebar(39.392287,27.903255, dist=1000, location="topright", st.size=2)

This did not work and gives me a warning message and sometimes complains that the function does not exist. Any advise how to add a scale bar and north arrow in this map?

Mike Wise
  • 22,131
  • 8
  • 81
  • 104
user5707371
  • 61
  • 1
  • 2
  • 2
    Have a look at this blog post: http://editerna.free.fr/wp/?p=76 – erc Dec 22 '15 at 15:59
  • I already used your code to add scale bar as scalebar(39.392287,27.903255, dist=1000, location="topright", st.size=2) ,but it gives me error message that the function is not exist, however I loaded all required packages (ggplot2,maps, maptools, grid). – user5707371 Dec 23 '15 at 17:49
  • Did you run the code from the blog post to create the functions? – aosmith Dec 23 '15 at 19:45
  • Possible duplicate of [Adding scale bar to ggplot map](https://stackoverflow.com/questions/17151024/adding-scale-bar-to-ggplot-map) – rafa.pereira Jul 01 '17 at 09:03

3 Answers3

2

When I add all the functions, and all the required packages, i.e. install.packages("ggplot2", "grid", "maptools", "maps", dependencies = TRUE) from the linked blog post it works for me (see code below)

scale bar in ggplot map

ggplot(…) + scaleBar(lon = 38.5, lat = 28, distanceLon = 200, distanceLat = 100, distanceLegend = 200, dist.unit = "km", orientation = FALSE)

Should I all all the code form the blog post to my response?

Eric Fail
  • 8,191
  • 8
  • 72
  • 128
  • @ Eric Fail, I installed all required packages and updated R version into the newest release (3.2.3) and still gives me error. even when search for help ??scaleBar, R doesn't give me help for this function. Error: could not find function "scaleBar" Any advise for that ?? – user5707371 Dec 30 '15 at 19:30
  • @user5707371 You have to copy the code for the `scaleBar` function from the blog post and run it in your R session. Only once you do this will you be able to use the function, as it's not part of any of the packages you are using. – aosmith Jan 05 '16 at 03:06
  • @user5707371, exactly was Mr. aosmith says! – Eric Fail Jan 05 '16 at 04:24
1

I also used the approach presented by @Eric Fail after this blog entry. Yet, I added some additional gimmicks like a box around the scale and the possibility to change the Color of the north arrow. This might be useful for plotting on a ggmap(get_map()) base map:

library(maps)
library(maptools)
library(ggplot2)
library(grid)

#
# Result #
#--------#
# Return a list whose elements are :
#   - rectangle : a data.frame containing the coordinates to draw the first rectangle ;
#   - rectangle2 : a data.frame containing the coordinates to draw the second rectangle ;
#   - legend : a data.frame containing the coordinates of the legend texts, and the texts as well.
#
# Arguments : #
#-------------#
# lon, lat : longitude and latitude of the bottom left point of the first rectangle to draw ;
# distanceLon : length of each rectangle ;
# distanceLat : width of each rectangle ;
# distanceLegend : distance between rectangles and legend texts ;
# dist.units : units of distance "km" (kilometers) (default), "nm" (nautical miles), "mi" (statute miles).
createScaleBar <- function(lon,lat,distanceLon,distanceLat,distanceLegend, dist.units = "km"){
  # First rectangle
  bottomRight <- gcDestination(lon = lon, lat = lat, bearing = 90, dist = distanceLon, dist.units = dist.units, model = "WGS84")

  topLeft <- gcDestination(lon = lon, lat = lat, bearing = 0, dist = distanceLat, dist.units = dist.units, model = "WGS84")
  rectangle <- cbind(lon=c(lon, lon, bottomRight[1,"long"], bottomRight[1,"long"], lon),
                     lat = c(lat, topLeft[1,"lat"], topLeft[1,"lat"],lat, lat))
  rectangle <- data.frame(rectangle, stringsAsFactors = FALSE)

  # Second rectangle t right of the first rectangle
  bottomRight2 <- gcDestination(lon = lon, lat = lat, bearing = 90, dist = distanceLon*2, dist.units = dist.units, model = "WGS84")
  rectangle2 <- cbind(lon = c(bottomRight[1,"long"], bottomRight[1,"long"], bottomRight2[1,"long"], bottomRight2[1,"long"], bottomRight[1,"long"]),
                      lat=c(lat, topLeft[1,"lat"], topLeft[1,"lat"], lat, lat))
  rectangle2 <- data.frame(rectangle2, stringsAsFactors = FALSE)

  # Now let's deal with the text
  onTop <- gcDestination(lon = lon, lat = lat, bearing = 0, dist = distanceLegend, dist.units = dist.units, model = "WGS84")
  onTop2 <- onTop3 <- onTop
  onTop2[1,"long"] <- bottomRight[1,"long"]
  onTop3[1,"long"] <- bottomRight2[1,"long"]

  legend <- rbind(onTop, onTop2, onTop3)
  legend <- data.frame(cbind(legend, text = c(0, distanceLon, distanceLon*2)), stringsAsFactors = FALSE, row.names = NULL)
  return(list(rectangle = rectangle, rectangle2 = rectangle2, legend = legend))
}


#
# Result #
#--------#
# Returns a list containing :
#   - res : coordinates to draw an arrow ;
#   - coordinates of the middle of the arrow (where the "N" will be plotted).
#
# Arguments : #
#-------------#
# scaleBar : result of createScaleBar() ;
# length : desired length of the arrow ;
# distance : distance between legend rectangles and the bottom of the arrow ;
# dist.units : units of distance "km" (kilometers) (default), "nm" (nautical miles), "mi" (statute miles).
createOrientationArrow <- function(scaleBar, length, distance = 1, dist.units = "km"){
  lon <- scaleBar$rectangle2[1,1]
  lat <- scaleBar$rectangle2[1,2]

  # Bottom point of the arrow
  begPoint <- gcDestination(lon = lon, lat = lat, bearing = 0, dist = distance, dist.units = dist.units, model = "WGS84")
  lon <- begPoint[1,"long"]
  lat <- begPoint[1,"lat"]

  # Let us create the endpoint
  onTop <- gcDestination(lon = lon, lat = lat, bearing = 0, dist = length, dist.units = dist.units, model = "WGS84")

  leftArrow <- gcDestination(lon = onTop[1,"long"], lat = onTop[1,"lat"], bearing = 225, dist = length/5, dist.units = dist.units, model = "WGS84")

  rightArrow <- gcDestination(lon = onTop[1,"long"], lat = onTop[1,"lat"], bearing = 135, dist = length/5, dist.units = dist.units, model = "WGS84")

  res <- rbind(
    cbind(x = lon, y = lat, xend = onTop[1,"long"], yend = onTop[1,"lat"]),
    cbind(x = leftArrow[1,"long"], y = leftArrow[1,"lat"], xend = onTop[1,"long"], yend = onTop[1,"lat"]),
    cbind(x = rightArrow[1,"long"], y = rightArrow[1,"lat"], xend = onTop[1,"long"], yend = onTop[1,"lat"]))

  res <- as.data.frame(res, stringsAsFactors = FALSE)

  # Coordinates from which "N" will be plotted
  coordsN <- cbind(x = lon, y = (lat + onTop[1,"lat"])/2)

  return(list(res = res, coordsN = coordsN))
}
#
# Result #
#--------#
# This function enables to draw a scale bar on a ggplot object, and optionally an orientation arrow #
# Arguments : #
#-------------#
# lon, lat : longitude and latitude of the bottom left point of the first rectangle to draw ;
# distanceLon : length of each rectangle ;
# distanceLat : width of each rectangle ;
# distanceLegend : distance between rectangles and legend texts ;
# dist.units : units of distance "km" (kilometers) (by default), "nm" (nautical miles), "mi" (statute miles) ;
# rec.fill, rec2.fill : filling colour of the rectangles (default to white, and black, resp.);
# rec.colour, rec2.colour : colour of the rectangles (default to black for both);
# legend.colour : legend colour (default to black);
# legend.size : legend size (default to 3);
# orientation : (boolean) if TRUE (default), adds an orientation arrow to the plot ;
# arrow.length : length of the arrow (default to 500 km) ;
# arrow.distance : distance between the scale bar and the bottom of the arrow (default to 300 km) ;
# arrow.North.size : size of the "N" letter (default to 6).
res <- c()
scaleBar <- function(lon, lat, distanceLon, distanceLat, distanceLegend, dist.unit = "km", rec.fill = "white", rec.colour = "black", rec2.fill = "black", rec2.colour = "black", legend.colour = "black", legend.size = 3, orientation = TRUE, arrow.length = 500, arrow.distance = 300, arrow.North.size = 6, arrow.color = "black", box = TRUE, box.line.color = "black", box.fill.color = "white", box.offset = 1){
  if (box){# Add a background box for better visualization on top of a base map
    topLeft <- gcDestination(lon = lon, lat = lat, bearing = 0, dist = distanceLat, dist.units = dist.unit, model = "WGS84")
    bottomRight <- gcDestination(lon = lon, lat = lat, bearing = 90, dist = distanceLon*2, dist.units = dist.unit, model = "WGS84")

    boxTopLeft <- gcDestination(lon = topLeft[1,"long"], lat = topLeft[1,"lat"], bearing = 315, dist = box.offset, dist.units = dist.unit, model = "WGS84")
    boxTopRight <- gcDestination(lon = bottomRight[1,"long"], lat = topLeft[1,"lat"], bearing = 45, dist = box.offset, dist.units = dist.unit, model = "WGS84")
    boxBottomRight <- gcDestination(lon = bottomRight[1,"long"], lat = bottomRight[1,"lat"], bearing = 135, dist = box.offset, dist.units = dist.unit, model = "WGS84")
    boxBottomLeft <- gcDestination(lon = topLeft[1,"long"], lat = bottomRight[1,"lat"], bearing = 225, dist = box.offset, dist.units = dist.unit, model = "WGS84")
    bg <- cbind(lon = c(boxTopLeft[1,"long"], boxTopRight[1,"long"], boxBottomRight[1,"long"], boxBottomLeft[1,"long"], boxTopLeft[1,"long"]),
                lat = c(boxTopLeft[1, "lat"], boxTopRight[1, "lat"], boxBottomRight[1, "lat"], boxBottomLeft[1, "lat"], boxTopLeft[1, "lat"]))
    bgdf <- data.frame(bg, stringsAsFactors = FALSE)
    bg <- geom_polygon(data = bgdf, aes(x = lon, y = lat), fill = box.fill.color, colour = box.line.color, size = 0.2)
    res <- c(res, bg)
  }

  laScaleBar <- createScaleBar(lon = lon, lat = lat, distanceLon = distanceLon, distanceLat = distanceLat, distanceLegend = distanceLegend, dist.unit = dist.unit)
  # First rectangle
  rectangle1 <- geom_polygon(data = laScaleBar$rectangle, aes(x = lon, y = lat), fill = rec.fill, colour = rec.colour)

  # Second rectangle
  rectangle2 <- geom_polygon(data = laScaleBar$rectangle2, aes(x = lon, y = lat), fill = rec2.fill, colour = rec2.colour)

  # Legend
  scaleBarLegend <- annotate("text", label = paste(laScaleBar$legend[,"text"], dist.unit, sep=""), x = laScaleBar$legend[,"long"], y = laScaleBar$legend[,"lat"], size = legend.size, colour = legend.colour)

  res <- c(res, list(rectangle1, rectangle2, scaleBarLegend))

  if(orientation){# Add an arrow pointing North
    coordsArrow <- createOrientationArrow(scaleBar = laScaleBar, length = arrow.length, distance = arrow.distance, dist.unit = dist.unit)
    arrow <- list(geom_segment(data = coordsArrow$res, aes(x = x, y = y, xend = xend, yend = yend), color = arrow.color), annotate("text", label = "N", x = coordsArrow$coordsN[1,"x"], y = coordsArrow$coordsN[1,"y"], size = arrow.North.size, colour = arrow.color))
    res <- c(res, arrow)
  }

  return(res)
}

All the distances you insert into scaleBar() are in the "dist.units" like "km". Lat and Long define the location and have to be in mapUnits (e.g. decimal degrees, when in WGS84). location = "topright" will not work in this function.

loki
  • 9,816
  • 7
  • 56
  • 82
0

You should also look at the ggsn package

library(ggsn); library(sf)
dsn <- system.file('extdata', package = 'ggsn')

# Map in geographic coordinates
map <- st_read(dsn, 'sp', quiet = TRUE)

 ggm1 <- ggplot(map, aes(fill = nots)) +
    geom_sf() +
    scale_fill_brewer(name = 'Animal abuse\nnotifications', palette = 8)

ggm1 +
        blank() +
        north(map) +
        scalebar(map, dist = 5, dd2km = TRUE, model = 'WGS84')
mmann1123
  • 5,031
  • 7
  • 41
  • 49