diff --git a/DESCRIPTION b/DESCRIPTION index 94415fe..7ae54d0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,14 +1,15 @@ -Package: ggmap -Version: 2.7.900 +Package: ggmapAwhere +Version: 2.7.900.1 Title: Spatial Visualization with ggplot2 Description: A collection of functions to visualize spatial data and models on top of static maps from various online sources (e.g Google Maps and Stamen Maps). It includes tools common to those tasks, including functions for geolocation and routing. -URL: https://github.com/dkahle/ggmap -BugReports: https://github.com/dkahle/ggmap/issues -Authors@R: c(person("David", "Kahle", email = "david.kahle@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-9999-1558")), - person("Hadley", "Wickham", email = "h.wickham@gmail.com", role = "aut", comment = c(ORCID = "0000-0003-4757-117X"))) +URL: https://github.com/aWhereAPI/ggmap +BugReports: https://github.com/aWhereAPI/ggmap/issues +Authors@R: c(person("Technical", "Support", ,"technicalsupport@awhere.com", role = c("ctb")) + person("David", "Kahle", email = "david.kahle@gmail.com", role = c("aut", "cre")) + person("Hadley", "Wickham", email = "h.wickham@gmail.com", role = "aut")) Depends: R (>= 3.1.0), ggplot2 (>= 2.2.0) diff --git a/NAMESPACE b/NAMESPACE index bd142a7..0eb9aed 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,7 @@ S3method(print,ggmap) S3method(print,ggmap_credentials) +export(GetMapTiles) export(LonLat2XY) export(OSM_scale_lookup) export(XY2LonLat) diff --git a/R/GetMapTiles.R b/R/GetMapTiles.R new file mode 100644 index 0000000..363a1fb --- /dev/null +++ b/R/GetMapTiles.R @@ -0,0 +1,141 @@ +#' Download Map Tiles to local directory +#' +#' \code{GetMapTiles} is a smart wrapper that queries the Google Maps, +#' OpenStreetMap, Stamen Maps or Naver Map servers for a map. This download map +#' tiles from specified map tile servers such as openstreetmap or Google +#' Query the server for map tiles, defined uniquely by their X and Y ID and zoom. +#' For offline usage, these map tiles are stored in a local directory +#' THIS CODE WAS MODIFIED FROM THE FUNCTION IN THE RgoogleMaps Package +#' +#' @param bbox Extent over which to pull tiles +#' @param zoom map zoom, an integer from 3 (continent) to 21 +#' (building), default value 10 (city). openstreetmaps limits a +#' zoom of 18, and the limit on stamen maps depends on the +#' maptype. "auto" automatically determines the zoom for bounding +#' box specifications, and is defaulted to 10 with center/zoom +#' specifications. maps of the whole world currently not +#' supported. +#' @param urlBase path of tile server to query +#' @param CheckExistingFiles will check if current tiles are already downloaded and skip if are +#' @param TotalSleep ##<< overall time (in seconds) that one is willing to add in +#' between downloads. This is intended to lower the risk of a server denial. +#' If NULL no call to \link{Sys.sleep} is executed +#' @param tileDir Where are local tiles stored. Only relevant if doOffline == TRUE +#' @param tileExt file extension of files from tile server +#' @param verbose level of verbosity +#' @return a ggmap object (a classed raster object with a bounding +#' box attribute) +#' @author David Kahle \email{david.kahle@@gmail.com} +#' @seealso \code{\link{ggmap}}, \code{\link{GetMap}} in package +#' RgoogleMaps +#' @export +#' @examples GetMapTiles(zoom=9 +#' ,bbox = sbbox +#' ,verbose=1 +#' ,urlBase = "http://tile.stamen.com/terrain/" +#' ,tileDir= "./mapTiles/stamen/terrain/") + +GetMapTiles <- function(bbox = c(left = -95.80204 + ,bottom = 29.38048 + ,right = -94.92313 + ,top = 30.14344) + ,zoom =10 + ,urlBase = "http://tile.stamen.com/terrain/" + ,CheckExistingFiles = TRUE + ,TotalSleep = NULL + ,tileExt = ".png" + ,tileDir= "~/mapTiles/stamen/" + ,verbose=0){ + + + # tile2long = function(x,z){ return (x/(2^z)*360-180); } + # tile2lat= function(y,z) { n=pi-2*pi*y/(2^z); + # return (180/pi*atan(0.5*(exp(n)-exp(-n)))); } + + lonR <- c(bbox['left'],bbox['right']) + latR <- c(bbox['bottom'],bbox['top']) + + nTiles = c(0,0) + + if (!missing(lonR) & !missing(latR)) { + XYmin = RgoogleMaps::LatLon2XY(lat=latR[1], lon=lonR[1],zoom=zoom) + XYmax = RgoogleMaps::LatLon2XY(lat=latR[2], lon=lonR[2],zoom=zoom) + nTiles[1] = abs(XYmax$Tile[1,1]-XYmin$Tile[1,1])+1 + nTiles[2] = abs(XYmax$Tile[1,2]-XYmin$Tile[1,2])+1 + + center = c(lat=mean(latR),lon=mean(lonR)) + + if (verbose){ + cat("nTiles=",nTiles,", center=", round(center,3), "\n") + } + } + + XY = RgoogleMaps::LatLon2XY(lat=center["lat"], lon=center["lon"],zoom=zoom) + tileXY = XY$Tile + as.numeric(XY$Coords > 256) + + if (nTiles[1] %% 2 == 0) {#even + X = (tileXY[1,1]-nTiles[1]/2):(tileXY[1,1]+nTiles[1]/2-1); + } else { + X = (tileXY[1,1]-(nTiles[1]-1)/2):(tileXY[1,1]+(nTiles[1]-1)/2); + } + if (nTiles[2] %% 2 == 0) {#even + Y = (tileXY[1,2]-nTiles[2]/2):(tileXY[1,2]+nTiles[2]/2-1); + } else { + Y = (tileXY[1,2]-(nTiles[2]-1)/2):(tileXY[1,2]+(nTiles[2]-1)/2); + } + + if (!dir.exists(tileDir)) { + if (verbose) cat("trying to create dir",tileDir, "\n") + dir.create(tileDir, recursive = TRUE) + } + if (CheckExistingFiles) ExistingFiles=list.files(path=tileDir) + + NumTiles = length(X)*length(Y) + if (verbose) cat (NumTiles, "tiles to download \n") + + DOWNLOAD=TRUE + k=1 + tiles=list() + y = 0 + + for (x_count in 1:length(X)){ + + cat(paste0('Currently on tile ', ((x_count-1) * length(Y))+1,' of ',NumTiles,'\n')) + + x <- X[x_count] + + for (y_count in 1:length(Y)){ + + y <- Y[y_count] + + if (grepl("openstreetmap",urlBase) | grepl("stamen",urlBase)){ + url <- paste0(urlBase, zoom, "/",x , "/", y, ".png") + } else if (grepl("google",urlBase)){ + url <- paste0(urlBase, "&x=", x, "&y=", y, "&z=", zoom) + } + + f=paste(zoom, x, y, sep="_") + if (CheckExistingFiles) + if (paste0(f,tileExt) %in% ExistingFiles) { + if (verbose) cat(" NOT downloading existing file ",f, tileExt, "\n",sep="" ) + DOWNLOAD=FALSE + } else { + if (verbose) cat(" downloading file ",f, tileExt, "\n",sep="" ) + DOWNLOAD=TRUE + } + destfile = file.path(tileDir, f) + mapFile=paste0(destfile,tileExt) + + if (DOWNLOAD){ + if (!is.null(TotalSleep)){ + Sys.sleep(round(runif(1,max=2*TotalSleep/NumTiles),1)) + } + try(download.file(url, mapFile, mode="wb", quiet = TRUE)); + } + k=k+1 + } + } + mt = list(X=X,Y=Y,zoom=zoom,tileDir=tileDir,tileExt=tileExt,tiles=tiles) + class(mt) = "mapTiles" + invisible(mt) +} \ No newline at end of file diff --git a/R/get_map.R b/R/get_map.R index 1b02fd2..3c2fc50 100644 --- a/R/get_map.R +++ b/R/get_map.R @@ -32,6 +32,9 @@ #' @param color color ("color") or black-and-white ("bw") #' @param language language for google maps #' @param api_key an api key for cloudmade maps +#' @param doOffline Should map tiles be loaded from local directory. +#' Must be downloaded first +#' @param tileDir Where are local tiles stored. Only relevant if doOffline == TRUE #' @return a ggmap object (a classed raster object with a bounding #' box attribute) #' @author David Kahle \email{david.kahle@@gmail.com} @@ -57,18 +60,39 @@ #' ggmap(map, fullpage = TRUE) #' #' } -get_map <- function( - location = c(lon = -95.3632715, lat = 29.7632836), zoom = "auto", scale = "auto", - maptype = c("terrain", "terrain-background", "satellite", "roadmap", - "hybrid", "toner", "watercolor", "terrain-labels", - "terrain-lines", "toner-2010", "toner-2011", "toner-background", - "toner-hybrid", "toner-labels", "toner-lines", "toner-lite"), - source = c("google","osm","stamen","cloudmade"), - force = ifelse(source == "google", TRUE, TRUE), messaging = FALSE, urlonly = FALSE, - filename = NULL, - crop = TRUE, color = c("color","bw"), language = "en-EN", - api_key -){ +get_map <- function(location = c(lon = -95.3632715, lat = 29.7632836) + ,zoom = "auto" + ,scale = "auto" + ,maptype = c("terrain" + ,"terrain-background" + ,"satellite", "roadmap" + ,"hybrid", + ,"toner" + ,"watercolor" + ,"terrain-labels" + ,"terrain-lines" + ,"toner-2010" + ,"toner-2011" + ,"toner-background" + ,"toner-hybrid" + ,"toner-labels" + ,"toner-lines" + ,"toner-lite") + ,source = c("google","osm","stamen","cloudmade") + ,force = ifelse(source == "google", TRUE, TRUE) + ,messaging = FALSE + ,urlonly = FALSE + ,filename = NULL + ,crop = TRUE + ,color = c("color","bw") + ,language = "en-EN" + ,api_key + ,doOffline = FALSE + ,tileDir = './mapTiles'){ + + if (doOffline == TRUE & !(maptype %in% c('terrain','toner')) & source == 'stamen') { + stop('Offline Mode only works for Stamen Terrain and Toner maps currently') + } # deprecated syntaxes args <- as.list(match.call(expand.dots = TRUE)[-1]) @@ -184,6 +208,13 @@ get_map <- function( zoom = 10 } + maxZoomDownloaded <- 13 + + if (doOffline == TRUE & zoom > maxZoomDownloaded) { + zoom <- maxZoomDownloaded + } + + # compute scale when scale = "auto" (only for google/osm) if(scale == "auto"){ @@ -273,9 +304,17 @@ get_map <- function( # get map/return return( - get_stamenmap(bbox = location, zoom = zoom, maptype = maptype, crop = crop, - messaging = messaging, urlonly = urlonly, filename = filename, force = force, - color = color) + get_stamenmap(bbox = location + ,zoom = zoom + ,maptype = maptype + ,crop = crop + ,messaging = messaging + ,urlonly = urlonly + ,filename = filename + ,force = force + ,color = color + ,doOffline = doOffline + ,tileDir = tileDir) ) } diff --git a/R/get_stamenmap.R b/R/get_stamenmap.R index d109317..0903b17 100644 --- a/R/get_stamenmap.R +++ b/R/get_stamenmap.R @@ -21,6 +21,9 @@ #' up? #' @param where where should the file drawer be located (without #' terminating "/") +#' @param doOffline Should map tiles be loaded from local directory. +#' Must be downloaded first +#' @param tileDir Where are local tiles stored. Only relevant if doOffline == TRUE #' @param ... ... #' @return a ggmap object (a classed raster object with a bounding #' box attribute) @@ -210,14 +213,33 @@ #' #' } # end dontrun #' -get_stamenmap <- function( - bbox = c(left = -95.80204, bottom = 29.38048, right = -94.92313, top = 30.14344), - zoom = 10, maptype = c("terrain","terrain-background","terrain-labels", - "terrain-lines", "toner", "toner-2010", "toner-2011", "toner-background", - "toner-hybrid", "toner-labels", "toner-lines", "toner-lite", "watercolor"), - crop = TRUE, messaging = FALSE, urlonly = FALSE, color = c("color","bw"), force = FALSE, - where = tempdir(), ... -){ +get_stamenmap <- function(bbox = c(left = -95.80204 + ,bottom = 29.38048 + ,right = -94.92313 + ,top = 30.14344) + ,zoom = 10 + ,maptype = c("terrain" + ,"terrain-background" + ,"terrain-labels" + ,"terrain-lines" + ,"toner" + ,"toner-2010" + ,"toner-2011" + ,"toner-background" + ,"toner-hybrid" + ,"toner-labels" + ,"toner-lines" + ,"toner-lite" + ,"watercolor") + ,crop = TRUE + ,messaging = FALSE + ,urlonly = FALSE + ,color = c("color","bw") + ,force = FALSE + ,where = tempdir() + ,doOffline = FALSE + ,tileDir = './mapTiles' + , ...){ # enumerate argument checking (added in lieu of checkargs function) args <- as.list(match.call(expand.dots = TRUE)[-1]) @@ -294,15 +316,36 @@ get_stamenmap <- function( if(urlonly) return(urls) - # make list of tiles - listOfTiles <- lapply(split(tilesNeeded, 1:nrow(tilesNeeded)), function(v){ - v <- as.numeric(v) - get_stamenmap_tile(maptype, zoom, v[1], v[2], color, force = force, messaging = messaging) - }) + if (doOffline == FALSE) { + # make list of tiles + listOfTiles <- lapply(split(tilesNeeded, 1:nrow(tilesNeeded)), function(v){ + v <- as.numeric(v) + ggmap:::get_stamenmap_tile(maptype + ,zoom + ,v[1] + ,v[2] + ,color + ,force = force + ,messaging = messaging) + }) + } else { + listOfTiles <- lapply(split(tilesNeeded, 1:nrow(tilesNeeded)), function(v){ + v <- as.numeric(v) + get_stamenmap_tile_offline(maptype + ,zoom + ,v[1] + ,v[2] + ,color + ,force = force + ,messaging = messaging + ,tileDir = tileDir) + }) + } + # stitch tiles together - map <- stitch(listOfTiles) + map <- ggmap:::stitch(listOfTiles) # format map and return if not cropping @@ -367,21 +410,6 @@ get_stamenmap <- function( - - - - - - - - - - - - - - - get_stamenmap_checkargs <- function(args){ eargs <- lapply(args, eval) argsgiven <- names(args) @@ -425,17 +453,6 @@ get_stamenmap_checkargs <- function(args){ - - - - - - - - - - - get_stamenmap_tile <- function(maptype, zoom, x, y, color, force = FALSE, messaging = TRUE, where = tempdir()){ # check arguments @@ -539,7 +556,97 @@ get_stamenmap_tile <- function(maptype, zoom, x, y, color, force = FALSE, messag tile } +get_stamenmap_tile_offline <- function(maptype + ,zoom + ,x + ,y + ,color + ,force = FALSE + ,messaging = TRUE + ,where = tempdir() + ,tileDir = './mapTiles'){ + + # check arguments + is.wholenumber <- function (x, tol = .Machine$double.eps^0.5) abs(x - round(x)) < tol + + stopifnot(is.wholenumber(zoom) || !(zoom %in% 1:20)) + stopifnot(is.wholenumber(x) || !(0 <= x && x < 2^zoom)) + stopifnot(is.wholenumber(y) || !(0 <= y && y < 2^zoom)) + + # format url http://tile.stamen.com/[maptype]/[zoom]/[x]/[y].jpg + if(maptype %in% c("watercolor")){ + filetype <- "jpg" + } else { + filetype <- "png" + } + #url <- sprintf("http://tile.stamen.com/%s/%i/%i/%i.%s", maptype, zoom, x, y, filetype) + url <- paste0(tileDir,'/stamen/',maptype,'/',zoom,'_',x,'_',y,'.',filetype) + + # read in + if (file.exists(url) == TRUE) { + fileExists <- TRUE + if(filetype == "jpg"){ + tile <- try(readJPEG(url),silent = TRUE) + } else { + tile <- try(png::readPNG(url),silent = TRUE) + } + } else { + fileExists <- FALSE + cat(paste0('Tile ',zoom,'/',x,'/',y,' not present. Tile needs to be downloaded before used in offline mode. Contact aWhere support\n')) + } + + + if (class(tile) =="try-error" | fileExists == FALSE) { + tile <- array(NA, dim = c(256L, 256L)) + } else { + # convert to colors + # toner-lines treated differently for alpha + if(maptype %in% c("toner-hybrid", "toner-labels", "toner-lines", + "terrain-labels", "terrain-lines")){ + if(color == "color") { + tile <- t(apply(tile, 1:2, function(x) rgb(x[1], x[2], x[3], x[4]))) + } else { # color == "bw" (all these are black and white naturally) + tile <- t(apply(tile, 1:2, function(x) rgb(x[1], x[2], x[3], x[4]))) + } + } else { + if(color == "color") { + tile <- t(apply(tile, 2, rgb)) + } else { # color == "bw" + tiled <- dim(tile) + tile <- gray(.30 * tile[,,1] + .59 * tile[,,2] + .11 * tile[,,3]) + dim(tile) <- tiled[1:2] + tile <- t(tile) + } + } + } + + + # determine bbox of map. note : not the same as the argument bounding box - + # the map is only a covering of the bounding box extent the idea is to get + # the lower left tile and the upper right tile and compute their bounding boxes + # tiles are referenced by top left of tile, starting at 0,0 + # see http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames + lonlat_upperleft <- XY2LonLat(x, y, zoom) + lonlat_lowerright <- XY2LonLat(x, y, zoom, 255, 255) + bbox <- c( + left = lonlat_upperleft$lon, + bottom = lonlat_lowerright$lat, + right = lonlat_lowerright$lon, + top = lonlat_upperleft$lat + ) + bb <- data.frame( + ll.lat = unname(bbox["bottom"]), + ll.lon = unname(bbox["left"]), + ur.lat = unname(bbox["top"]), + ur.lon = unname(bbox["right"]) + ) + + # format + class(tile) <- c("ggmap", "raster") + attr(tile, "bb") <- bb + return(tile) +} diff --git a/man/GetMapTiles.Rd b/man/GetMapTiles.Rd new file mode 100644 index 0000000..9d4f8d4 --- /dev/null +++ b/man/GetMapTiles.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/GetMapTiles.R +\name{GetMapTiles} +\alias{GetMapTiles} +\title{Download Map Tiles to local directory} +\usage{ +GetMapTiles(bbox = c(left = -95.80204, bottom = 29.38048, right = -94.92313, + top = 30.14344), zoom = 10, urlBase = "http://tile.stamen.com/terrain/", + CheckExistingFiles = TRUE, TotalSleep = NULL, tileExt = ".png", + tileDir = "~/mapTiles/stamen/", verbose = 0) +} +\arguments{ +\item{bbox}{Extent over which to pull tiles} + +\item{zoom}{map zoom, an integer from 3 (continent) to 21 +(building), default value 10 (city). openstreetmaps limits a +zoom of 18, and the limit on stamen maps depends on the +maptype. "auto" automatically determines the zoom for bounding +box specifications, and is defaulted to 10 with center/zoom +specifications. maps of the whole world currently not +supported.} + +\item{urlBase}{path of tile server to query} + +\item{CheckExistingFiles}{will check if current tiles are already downloaded and skip if are} + +\item{TotalSleep}{##<< overall time (in seconds) that one is willing to add in +between downloads. This is intended to lower the risk of a server denial. +If NULL no call to \link{Sys.sleep} is executed} + +\item{tileExt}{file extension of files from tile server} + +\item{tileDir}{Where are local tiles stored. Only relevant if doOffline == TRUE} + +\item{verbose}{level of verbosity} +} +\value{ +a ggmap object (a classed raster object with a bounding + box attribute) +} +\description{ +\code{GetMapTiles} is a smart wrapper that queries the Google Maps, +OpenStreetMap, Stamen Maps or Naver Map servers for a map. This download map + tiles from specified map tile servers such as openstreetmap or Google + Query the server for map tiles, defined uniquely by their X and Y ID and zoom. + For offline usage, these map tiles are stored in a local directory + THIS CODE WAS MODIFIED FROM THE FUNCTION IN THE RgoogleMaps Package +} +\examples{ +GetMapTiles(zoom=9 + ,bbox = sbbox + ,verbose=1 + ,urlBase = "http://tile.stamen.com/terrain/" + ,tileDir= "./mapTiles/stamen/terrain/") +} +\seealso{ +\code{\link{ggmap}}, \code{\link{GetMap}} in package + RgoogleMaps +} +\author{ +David Kahle \email{david.kahle@gmail.com} +} diff --git a/man/geom_leg.Rd b/man/geom_leg.Rd index 44b38aa..79deb05 100644 --- a/man/geom_leg.Rd +++ b/man/geom_leg.Rd @@ -31,14 +31,12 @@ a warning. If \code{TRUE}, missing values are silently removed.} \item{show.legend}{logical. Should this layer be included in the legends? \code{NA}, the default, includes if any aesthetics are mapped. -\code{FALSE} never includes, and \code{TRUE} always includes. -It can also be a named logical vector to finely select the aesthetics to -display.} +\code{FALSE} never includes, and \code{TRUE} always includes.} \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, rather than combining with them. This is most useful for helper functions that define both data and aesthetics and shouldn't inherit behaviour from -the default plot specification, e.g. \code{\link[=borders]{borders()}}.} +the default plot specification, e.g. \code{\link{borders}}.} \item{...}{...} } diff --git a/man/get_map.Rd b/man/get_map.Rd index 41b5948..4ac124b 100644 --- a/man/get_map.Rd +++ b/man/get_map.Rd @@ -6,12 +6,13 @@ \usage{ get_map(location = c(lon = -95.3632715, lat = 29.7632836), zoom = "auto", scale = "auto", maptype = c("terrain", "terrain-background", "satellite", - "roadmap", "hybrid", "toner", "watercolor", "terrain-labels", "terrain-lines", - "toner-2010", "toner-2011", "toner-background", "toner-hybrid", - "toner-labels", "toner-lines", "toner-lite"), source = c("google", "osm", - "stamen", "cloudmade"), force = ifelse(source == "google", TRUE, TRUE), - messaging = FALSE, urlonly = FALSE, filename = NULL, crop = TRUE, - color = c("color", "bw"), language = "en-EN", api_key) + "roadmap", "hybrid", , "toner", "watercolor", "terrain-labels", + "terrain-lines", "toner-2010", "toner-2011", "toner-background", + "toner-hybrid", "toner-labels", "toner-lines", "toner-lite"), + source = c("google", "osm", "stamen", "cloudmade"), force = ifelse(source + == "google", TRUE, TRUE), messaging = FALSE, urlonly = FALSE, + filename = NULL, crop = TRUE, color = c("color", "bw"), + language = "en-EN", api_key, doOffline = FALSE, tileDir = "./mapTiles") } \arguments{ \item{location}{an address, longitude/latitude pair (in that @@ -55,6 +56,11 @@ box} \item{language}{language for google maps} \item{api_key}{an api key for cloudmade maps} + +\item{doOffline}{Should map tiles be loaded from local directory. +Must be downloaded first} + +\item{tileDir}{Where are local tiles stored. Only relevant if doOffline == TRUE} } \value{ a ggmap object (a classed raster object with a bounding diff --git a/man/get_stamenmap.Rd b/man/get_stamenmap.Rd index 5cc8c02..4021853 100644 --- a/man/get_stamenmap.Rd +++ b/man/get_stamenmap.Rd @@ -10,7 +10,7 @@ get_stamenmap(bbox = c(left = -95.80204, bottom = 29.38048, right = -94.92313, "toner-background", "toner-hybrid", "toner-labels", "toner-lines", "toner-lite", "watercolor"), crop = TRUE, messaging = FALSE, urlonly = FALSE, color = c("color", "bw"), force = FALSE, - where = tempdir(), ...) + where = tempdir(), doOffline = FALSE, tileDir = "./mapTiles", ...) } \arguments{ \item{bbox}{a bounding box in the format c(lowerleftlon, @@ -38,6 +38,11 @@ up?} \item{where}{where should the file drawer be located (without terminating "/")} +\item{doOffline}{Should map tiles be loaded from local directory. +Must be downloaded first} + +\item{tileDir}{Where are local tiles stored. Only relevant if doOffline == TRUE} + \item{...}{...} } \value{