1`GetMap` <- structure(function# download a static map from the Google server
2### Query the Google server for a static map tile, defined primarily by its
3### center and zoom. Many additional arguments allow the user to customize
4### the map tile.
5### documentation at https://developers.google.com/maps/documentation/staticmaps/
6(
7  center=c(lat=42, lon=-76), ##<< optional center (lat first,lon second  )
8  size = c(640,640), ##<< desired size of the map tile image. defaults to maximum size returned by the Gogle server, which is 640x640 pixels
9  destfile = tempfile("staticMap",fileext = ".png"), ##<<  File to load the map image from or save to, depending on \code{NEWMAP}.
10  zoom =12, ##<< Google maps zoom level.
11  markers,  ##<< (optional) defines one or more markers to attach to the image at specified locations. This parameter takes a string of marker definitions separated by the pipe character (|)
12  path="",  ##<< (optional) defines a single path of two or more connected points to overlay on the image at specified locations. This parameter takes a string of point definitions separated by the pipe character (|)
13  span, ##<< (optional) defines a minimum viewport for the map image expressed as a latitude and longitude pair. The static map service takes this value and produces a map of the proper zoom level to include the entire provided span value from the map`s center point. Note that the resulting map may include larger bounds for either latitude or longitude depending on the rectangular dimensions of the map. If zoom is specified, span is ignored
14  frame, ##<< (optional) specifies that the resulting image should be framed with a colored blue border. The frame consists of a 5 pixel, 55 % opacity blue border.
15  hl, ##<< (optional) defines the language to use for display of labels on map tiles. Note that this paramater is only supported for some country tiles; if the specific language requested is not supported for the tile set, then the default language for that tile set will be used.
16  sensor = "true",  ##<< specifies whether the application requesting the static map is using a sensor to determine the user`s location. This parameter is now required for all static map requests.
17  maptype = c("roadmap","mobile","satellite","terrain","hybrid","mapmaker-roadmap","mapmaker-hybrid")[2], ##<< defines the type of map to construct. There are several possible maptype values, including satellite, terrain, hybrid, and mobile.
18  format = c("gif","jpg","jpg-baseline","png8","png32")[5],  ##<< (optional) defines the format of the resulting image. By default, the Static Maps API creates GIF images. There are several possible formats including GIF, JPEG and PNG types. Which format you use depends on how you intend to present the image. JPEG typically provides greater compression, while GIF and PNG provide greater detail. This version supports only PNG.
19  extraURL="", ##<< custom URL suffix
20  RETURNIMAGE = TRUE, ##<< return image yes/no default: TRUE
21  GRAYSCALE =FALSE, ##<< Boolean toggle; if TRUE the colored map tile is rendered into a black & white image, see \link{RGB2GRAY}
22  NEWMAP = TRUE, ##<< if TRUE, query the Google server and save to \code{destfile}, if FALSE load from destfile.
23  SCALE = 1, ##<< use the API's scale parameter to return higher-resolution map images. The scale value is multiplied with the size to determine the actual output size of the image in pixels, without changing the coverage area of the map
24  API_console_key, ##<< API key (formerly optional, now mandatory). If missing, the function "stitches" a static map from map tiles
25  type = c("google", "google-m","google-s","osm", "osm-hot", "stamen-toner", "stamen-terrain", "stamen-watercolor")[1],  ##<< choice of tile server
26  urlBase = "http://mt1.google.com/vt/lyrs=m" , ##<< tileserver URL, alternatives would be "http://a.tile.openstreetmap.org/", "http://tile.stamen.com/toner/","http://tile.stamen.com/watercolor/"
27  tileDir= "/tmp/", ##<< map tiles can be stored in a local directory, e.g. "~/mapTiles/Google/"
28  verbose=0 ##<< level of verbosity
29){
30  ##note<<Note that size is in order (lon, lat)
31
32  if (!NEWMAP) {
33    #no new map download
34    myMap <- ReadMapTile(destfile);
35    return(myMap);
36  }
37
38  if (is.character(center)) {
39    if (verbose) cat("geocoding ", center, "\n")
40    center = getGeoCode(center,verbose)
41  }
42  if (all(c("lat","lon") %in% names(center))) center = center[c("lat","lon")]
43  ##seealso<< \link{GetMap.bbox}
44
45#   if (is.null(names(center))) {
46#     names(center) = c("lat", "lon");
47#   } else stopifnot( all(names(center) %in% c("lat", "lon")) )
48#
49  if (!is.null(center)) {
50    centerNum = center
51    center <- paste(center,collapse=",")
52  }
53
54  if (!missing(API_console_key)){
55    print("API key provided")
56
57    stopifnot(all(size <=640));
58
59    if (length(size) < 2) {s <- paste(size,size,sep="x")} else {s <- paste(size,collapse="x");}
60
61    # if (missing(format)){
62    #   if ( fileExt == "png") format <- "png32"
63    # }
64
65    googleurl <- "https://maps.googleapis.com/maps/api/staticmap?"# "http://maps.google.com/maps/api/staticmap?"; # googleurl <- 'http://maps.google.com/staticmap?';
66
67  	if (!missing(span)){#Images may specify a viewport (defined by latitude and longitude values expressed as degrees) to display around a provided center point by passing a span parameter. Defining a minimum viewport in this manner obviates the need to specify an exact zoom level. The static map service uses the span parameter in conjunction with the size parameter to construct a map of the proper zoom level which includes at least the given viewport constraints.
68  		span <- paste(span,collapse=",")
69  		urlStr <- paste(googleurl, "center=", center, "&span=", span,  "&size=",  s, "&maptype=", maptype, "&format=", format, "&sensor=", sensor, sep="")
70
71  	} else 	if (is.null(center) & missing(zoom)) {#let the Static Maps API determine the correct center and zoom level implicitly, based on evaluation of the position of the markers:
72  		stopifnot(!missing(markers) | path != "");
73  		urlStr <- paste(googleurl,  "size=",  s, "&maptype=", maptype, "&format=", format, "&sensor=", sensor, sep="")
74  	} else {
75  		stopifnot(!is.null(center), !missing(zoom));
76  		urlStr <- paste(googleurl, "center=", center, "&zoom=", zoom,  "&size=",  s, "&maptype=", maptype, "&format=", format, "&sensor=", sensor, sep="")
77  	}
78    if (!is.null(API_console_key))
79      urlStr <- paste0(urlStr,"&key=", API_console_key);
80  	urlStr <- paste(urlStr, path, sep="");
81  	urlStr <- paste(urlStr, extraURL, sep="");
82
83    if (!missing(hl)) urlStr <- paste0(urlStr, "&language=",hl);
84    if (SCALE == 2) urlStr <- paste(urlStr, "&scale=", SCALE, sep="");
85
86  	if (!missing(markers)) {
87  		#assumes markers is a list with names lat, lon, size (optional), color (optional), char (optional)
88  		#if(is.data.frame(markers)) markers<-as.matrix(markers)
89  		if ( is.matrix(markers) | is.data.frame(markers)) {
90  		  stopifnot(all(c("lat","lon") %in% colnames(markers)))
91  		  latlon = which(colnames(markers) %in% c("lat","lon"))
92  		  for (i in 1:nrow(markers)){
93  		  	m1 <- paste(markers[i,c("lat","lon")], collapse=",");
94  		  	if (any(c("size","color","label") %in% colnames(markers) ) ) {
95  		  	  m2 <- paste(colnames(markers)[-latlon], markers[i,-latlon], collapse="|",sep=":");
96  		  	  m <- paste(m2,m1,sep="|")
97  		  	} else {
98  		  	  m <- m1
99  		  	}
100  		  	#m <- paste("&markers=",m,sep="")
101  		  	#print(m)
102  		  	if (i==1){
103  		  		markers.string <- paste0("&markers=",m);
104  			  } else {
105  				  markers.string <- paste(markers.string,paste0("&markers=",m), sep="");
106  		    }
107  		    #if (verbose) print(markers.string);
108  		  }
109  		  #browser()
110  		  #markers.string <- paste("&markers=", markers.string,sep="")
111  		} else if (is.character(markers)) {#already in the correct string format:
112  		  markers.string <- markers;
113  		}
114
115  		urlStr <- paste(urlStr, markers.string, sep="");
116
117  	}
118
119  	if (verbose) print(urlStr);
120  	if (is.null(center)) {
121  	  if (verbose) print("Note that when center and zoom are not specified, no meta information on the map tile can be stored. This basically means that R cannot compute proper coordinates. You can still download the map tile and view it in R but overlays are not possible.");
122  	  #ans <- readLines(n=1);
123  	  #if (ans != "y") return();
124  	  MetaInfo <- list(lat.center = NULL, lon.center  = NULL, zoom = zoom,
125  	                   url = "google", BBOX = NULL, size=size, SCALE = SCALE);
126
127  	} else if ( is.numeric(centerNum) & !missing(zoom)) {
128  	  myMap <- list(lat.center = centerNum[1], lon.center  = centerNum[2], zoom = zoom, SCALE = SCALE);
129  	  BBOX <- list(ll = XY2LatLon(myMap, -size[1]/2 + 0.5, -size[2]/2 - 0.5), ur = XY2LatLon(myMap, size[1]/2 + 0.5, size[2]/2 - 0.5) );
130  	  MetaInfo <- list(lat.center = centerNum[1], lon.center  = centerNum[2], zoom = zoom,
131  	                   url = "google", BBOX = BBOX, size=size, SCALE = SCALE);
132  	}
133  	if (missing(destfile)) destfile=file.path(tempdir(),"mapTile.png")
134  	if (verbose == -1) browser();
135
136  	if (NEWMAP) {
137  	  if (!missing(destfile)){ # destfile=file.path(tempdir(),"mapTile.png")
138    	  fileBase <- substring(destfile,1, nchar(destfile)-4);
139    	  fileExt <-  substring(destfile,nchar(destfile)-2,nchar(destfile));
140    	  #save meta information about the image:
141    	  save(MetaInfo, file = paste(destfile,"rda",sep="."));
142  	    download.file(urlStr, destfile, mode="wb", quiet = TRUE);
143  	    myTile <- readPNG(destfile, native=TRUE);
144  	  } else { #do not save to file, read direcly from connection
145  	    #o new dependency on curl package: static maps are not saved to file by default any longer, instead read directly from connection
146    	  #connectStr=enc2utf8(gsub(' ','%20',"http://maps.google.com/maps/api/staticmap?center=42,-76&zoom=16&size=640x640&maptype=mobile&format=png32&sensor=true")) #Encode URL Parameters
147  	    #connectStr=enc2utf8(gsub(' ','%20', urlStr))
148  	    #does not work in RStudio, see http://stackoverflow.com/questions/18407177/load-image-from-website !!
149    #     con <- curl(urlStr, "rb")
150    # 	  binMap=readBin(con,raw(), 1E5)
151    #
152  	    # 	  myTile <- readPNG(binMap, native=FALSE);
153    # 	  close(con)
154  	  }
155  	} else { #no new map download
156  	  myMap <- ReadMapTile(destfile);
157  	}
158  } else {# end of missing API key
159
160    f=genStaticMap(center=centerNum, destfile = destfile, type=type, urlBase=urlBase, tileDir=tileDir, zoom=zoom, size=size,verbose=verbose)
161
162    myMap <- list(lat.center = centerNum[1], lon.center  = centerNum[2], zoom = zoom, SCALE = SCALE);
163    BBOX <- list(ll = XY2LatLon(myMap, -size[1]/2 + 0.5, -size[2]/2 - 0.5), ur = XY2LatLon(myMap, size[1]/2 + 0.5, size[2]/2 - 0.5) );
164    url = "google"
165    if (grepl("google", urlBase)) url = "google"
166    if (grepl("stamen", urlBase)) url = "stamen"
167    if (grepl("openstreetmap", urlBase)) url = "OSM"
168
169    MetaInfo <- list(lat.center = centerNum[1], lon.center  = centerNum[2], zoom = zoom,
170                     url = url, BBOX = BBOX, size=size, SCALE = SCALE);
171
172
173    fileBase <- substring(destfile,1, nchar(destfile)-4);
174    fileExt <-  substring(destfile,nchar(destfile)-2,nchar(destfile));
175    #save meta information about the image:
176    save(MetaInfo, file = paste(destfile,"rda",sep="."));
177    myTile <- readPNG(destfile, native=TRUE);
178  }
179
180
181	if (GRAYSCALE) {
182		#browser()
183		myTile <- RGB2GRAY(myTile);
184		if (!missing(destfile)) writePNG(myTile, destfile)
185	 }
186
187	if (RETURNIMAGE){
188 	  #myMap <- ReadMapTile(destfile);
189	  myMap <- list(lat.center= MetaInfo$lat.center, lon.center=MetaInfo$lon.center, zoom=MetaInfo$zoom,
190	                myTile=myTile, BBOX = MetaInfo$BBOX, url = MetaInfo$url, size=size, SCALE=MetaInfo$SCALE);
191	  class(myMap) =  "staticMap"
192  	return(myMap);
193 	}
194
195	invisible(urlStr)
196### map structure or URL used to download the tile.
197}, ex = function(){
198  if (0){#takes too long to run for CRAN check
199
200    lat = c(40.702147,40.718217,40.711614);
201    lon = c(-74.012318,-74.015794,-73.998284);
202    center = c(mean(lat), mean(lon));
203    zoom <- min(MaxZoom(range(lat), range(lon)));
204    #this overhead is taken care of implicitly by GetMap.bbox();
205    markers = paste0("&markers=color:blue|label:S|40.702147,-74.015794&markers=color:",
206                     "green|label:G|40.711614,-74.012318&markers=color:red|color:red|",
207                     "label:C|40.718217,-73.998284")
208    myMap <- GetMap(center=center, zoom=zoom,markers=markers);
209    #Note that in the presence of markers one often needs to add some extra padding to the
210    #latitude range to accomodate the extent of the top most marker
211
212    #add a path, i.e. polyline:
213    myMap <- GetMap(center=center, zoom=zoom,
214                    path = paste0("&path=color:0x0000ff|weight:5|40.737102,-73.990318|",
215                    "40.749825,-73.987963|40.752946,-73.987384|40.755823,-73.986397"));
216    #use implicit geo coding
217    BrooklynMap <- GetMap(center="Brooklyn", zoom=13)
218    PlotOnStaticMap(BrooklynMap)
219
220    #use implicit geo coding and display labels in Korean:
221    BrooklynMap <- GetMap(center="Brooklyn", zoom=13, hl="ko")
222    PlotOnStaticMap(BrooklynMap)
223
224    #no highways
225    ManHatMap <- GetMap(center="Lower Manhattan", zoom=14,
226                        extraURL="&style=feature:road.highway|visibility:off",
227                        destfile = "LowerManhattan.png")
228    PlotOnStaticMap(ManHatMap)
229
230    #reload the map without a new download:
231    ManHatMap <- GetMap(destfile = "LowerManhattan.png",NEWMAP=FALSE)
232    PlotOnStaticMap(ManHatMap)
233
234    #The example below defines a polygonal area within Manhattan, passed a series of
235    #intersections as locations:
236    #myMap <- GetMap(path = paste0("&path=color:0x00000000|weight:5|fillcolor:0xFFFF0033|",
237    #          "8th+Avenue+%26+34th+St,New+York,NY|8th+Avenue+%26+42nd+St,New+York,NY|",
238    #          "Park+Ave+%26+42nd+St,New+York,NY,NY|Park+Ave+%26+34th+St,New+York,NY,NY"),
239    #            destfile = "MyTile3a.png");
240
241    #note that since the path string is just appended to the URL you can "abuse" the path
242    #argument to pass anything to the query, e.g. the style parameter:
243    #The following example displays a map of Brooklyn where local roads have been changed
244    #to bright green and the residential areas have been changed to black:
245    # myMap <- GetMap(center="Brooklyn", zoom=12, maptype = "roadmap",
246    #path = paste0("&style=feature:road.local|element:geometry|hue:0x00ff00|",
247    #        "saturation:100&style=feature:landscape|element:geometry|lightness:-100"),
248    #        sensor='false', destfile = "MyTile4.png",  RETURNIMAGE = FALSE);
249
250    #In the last example we set RETURNIMAGE to FALSE which is a useful feature in general
251    #if png is not installed. In that cases, the images can still be fetched
252    #and saved but not read into R.
253
254    #In the following example we let the Static Maps API determine the correct center and
255    #zoom level implicitly, based on evaluation of the position of the markers.
256    #However, to be of use within R we do need to know the values for zoom and
257    #center explicitly, so it is better practice to compute them ourselves and
258    #pass them as arguments, in which case meta information on the map tile can be saved as well.
259
260    #myMap <- GetMap(markers = paste0("&markers=color:blue|label:S|40.702147,-74.015794&",
261    #          "markers=color:green|label:G|40.711614,-74.012318&markers=color:red|",
262    #          "color:red|label:C|40.718217,-73.998284"),
263    #           destfile = "MyTile1.png",  RETURNIMAGE = FALSE);
264  }
265})
266
267