Googleway Vignette

D Cooley

2022-01-24

Googleway provides access to Google Maps APIs, and the ability to plot an interactive Google Map overlayed with various layers and shapes, including markers, circles, rectangles, polygons, lines (polylines) and heatmaps. You can also overlay traffic information, transit and cycling routes.

The API functions are

Plotting a Google Map uses the JavaScript API, and the functions used to create a map and overlays are

Downloading a static streetview map

There are also functions that directly open in a browser, but don’t return any data

Finally, the package includes the helper functions, encode_pl() and decode_pl() for encoding and decoding polylines.

API Key

To use most of the functions in this package you will need a valid API KEY (follow instructions here to get a key) for the API you wish to use. The same API key can be used for all the functions, but you need to register it with each API first.

The exceptions to this are the functions that don’t return any data

Setting / Using keys

All the API functions have a key argument which you can use to provide your API key. Alternatively, you can use set_key() to set the key once, and make it available for all further API calls.

If you use one key for all API calls you can just provide the key argument (it will automatically get set as your default key).

If you use many different keys, you can specify which API they are for in the api argument.

library(googleway)
## not specifying the api will add the key as your 'default'
key <- "my_api_key"
set_key(key = key)
google_keys()
## Google API keys
##  -  default : my_api_key 
##  -  map :  
##  -  directions :  
##  -  distance :  
##  -  elevation :  
##  -  geocode :  
##  -  places :  
##  -  find_place :  
##  -  place_autocomplete :  
##  -  place_details :  
##  -  reverse_geocode :  
##  -  roads :  
##  -  streetview :  
##  -  timezone :
## specifying the specific API will only make that key available for that API.
clear_keys() ## clear any previously set keys
key <- "my_api_key"
set_key(key = key, api = "directions")
google_keys()
## Google API keys
##  -  default :  
##  -  map :  
##  -  directions : my_api_key 
##  -  distance :  
##  -  elevation :  
##  -  geocode :  
##  -  places :  
##  -  find_place :  
##  -  place_autocomplete :  
##  -  place_details :  
##  -  reverse_geocode :  
##  -  roads :  
##  -  streetview :  
##  -  timezone :

API Use

Google’s API pricing and plans contains the most up-to-date information on their use and restrictions.

For the free tier this is 2,500 web-service API requests (e.g., geocoding, directions, distances, etc) per day (the places API is slightly different), and 25,000 map loads per day.

Common use cases

Common use-cases for R users are where you might have a data.frame of

In these cases Google’s API can only accept one request at a time. Therefore it’s not possible to ‘vectorise’ these functions as they have to operate one row at a time.

The solution, therefore, will be to write some sort of loop to iterate over each row of the data.frame.

An exaple (taken from user @Jazzurro’s answer on StackOverflow) being where you have 3 pairs of coordinates, and you want to find the route (polyline) between each pair.

In this example they used an lapply to iterate over the rows, but any looping mechanism would have worked as well.

library(googleway)

mydf <- data.frame(region = 1:3,
                   from_lat = 41.8674336,
                   from_long = -87.6266382,
                   to_lat = c(41.887544, 41.9168862, 41.8190937),
                   to_long = c(-87.626487, -87.64847, -87.6230967))

mykey <- "your_api_key"

pls <- lapply(1:nrow(mydf), function(x){

    foo <- google_directions(origin = unlist(mydf[x, 2:3]),
                             destination = unlist(mydf[x, 4:5]),
                             #key = mykey,
                             mode = "driving",
                             simplify = TRUE)

    ## Decode the polyline into lat/lon coordinates
    pl <- decode_pl(foo$routes$overview_polyline$points)

    return(pl)
})

str(pls)

List of 3
 $ :'data.frame':   46 obs. of  2 variables:
  ..$ lat: num [1:46] 41.9 41.9 41.9 41.9 41.9 ...
  ..$ lon: num [1:46] -87.6 -87.6 -87.6 -87.6 -87.6 ...
 $ :'data.frame':   142 obs. of  2 variables:
  ..$ lat: num [1:142] 41.9 41.9 41.9 41.9 41.9 ...
  ..$ lon: num [1:142] -87.6 -87.6 -87.6 -87.6 -87.6 ...
 $ :'data.frame':   72 obs. of  2 variables:
  ..$ lat: num [1:72] 41.9 41.9 41.9 41.9 41.9 ...
  ..$ lon: num [1:72] -87.6 -87.6 -87.6 -87.6 -87.6 ...
  

Result accessors

For the API calls that return data, I have provided some helper functions to access specific data points returned from the API calls. The full list is given in the help file ?access_result. Examples of its use is demonstrated throughout this vignette.

If there is a specific data point you would like added to the function, please file an issue on my github page


Google Directions API

Google Maps allows users to find directions between locations.

The Google Maps Directions API is a service available to developers that calculates directions between locations.

Searching Google Maps for directions from Melbourne to Sydney generates the route:

Melbourne to Sydney

The same query using the developers API generates the data in JSON

{
   "geocoded_waypoints" : [
      {
         "geocoder_status" : "OK",
         "place_id" : "ChIJ90260rVG1moRkM2MIXVWBAQ",
         "types" : [ "colloquial_area", "locality", "political" ]
      },
      {
         "geocoder_status" : "OK",
         "place_id" : "ChIJP3Sa8ziYEmsRUKgyFmh9AQM",
         "types" : [ "colloquial_area", "locality", "political" ]
      }
   ],
   "routes" : [
      {
         "bounds" : {
            "northeast" : {
               "lat" : -33.8660005,
               "lng" : 151.2176931
            },
            "southwest" : {
               "lat" : -37.8136598,
               "lng" : 144.8875036
            }
         },
         "copyrights" : "Map data ©2017 Google",
         "legs" : [
            {
               "distance" : {
                  "text" : "878 km",
                  "value" : 878208
               },
               "duration" : {
                  "text" : "8 hours 44 mins",
                  "value" : 31447
               },
               "end_address" : "Sydney NSW, Australia",
               "end_location" : {
                  "lat" : -33.8689894,
                  "lng" : 151.2091978
               },
               "start_address" : "Melbourne VIC, Australia",
               "start_location" : {
                  "lat" : -37.8136598,
                  "lng" : 144.9629147
               },
    ... etc
    

This result can be returned in R using the google_directions() function. By default the result will be coerced to the simplest R structure possible using jsonlite::fromJSON(). If you want the result in JSON set simplify = FALSE.

library(googleway)

key <- "your_api_key"
df <- google_directions(origin = "Melbourne, Australia",
                        destination = "Sydney, Australia",
                        key = key,
                        mode = "driving",
                        simplify = TRUE)

The data used to draw the route on the map is the overview_polyline. This string represents a sequence of lat/lon pairs, encoded using a lossy compression algorithm (https://developers.google.com/maps/documentation/utilities/polylinealgorithm) that allows you to store the series of coordinates as a single string.

You can extract the polyline manually

pl <- df$routes$overview_polyline$points

Or use the direction_polyline() accessor

pl <- direction_polyline(df)
pl
# [1] "rqxeF_cxsZgr@xmCekBhMunGnWc_Ank@vBpyCqjAfbAqmBjXydAe{AoF{oEgTqjGur@ch@qfAhUuiCww@}kEtOepAtdD{dDf~BsgIuj@}tHi{C{bGg{@{rGsmG_bDbW{wCuTyiBajBytF_oAyaI}K}bEkqA{jDg^epJmbB{gC}v@i~D`@gkGmJ_kEojD_O{`FqvCetE}bGgbDm_BqpD}pEqdGiaBo{FglEg_Su~CegHw`Cm`Hv[mxFwaAisAklCuUgzAqmCalJajLqfDedHgyC_yHibCizK~Xo_DuqAojDshAeaEpg@g`Dy|DgtNswBcgDiaAgEqgBozB{jEejQ}p@ckIc~HmvFkgAsfGmjCcaJwwD}~AycCrx@skCwUqwN{yKygH}nF_qAgyOep@slIehDcmDieDkoEiuCg|LrKo~Eb}Bw{Ef^klG_AgdFqvAaxBgoDeqBwoDypEeiFkjBa|Ks}@gr@c}IkE_qEqo@syCgG{iEazAmeBmeCqvA}rCq_AixEemHszB_SisB}mEgeEenCqeDab@iwAmZg^guB}cCk_F_iAmkGsu@abDsoBylBk`Bm_CsfD{jFgrAerB{gDkw@{|EacB_jDmmAsjC{yBsyFaqFqfEi_Ei~C{yAmwFt{B{fBwKql@onBmtCq`IomFmdGueD_kDssAwsCyqDkx@e\\kwEyUstC}uAe|Ac|BakGpGkfGuc@qnDguBatBot@}kD_pBmmCkdAgkB}jBaIyoC}xAexHka@cz@ahCcfCayBqvBgtBsuDxb@yiDe{Ikt@c{DwhBydEynDojCapAq}AuAksBxPk{EgPgkJ{gA}tGsJezKbcAcdK__@uuBn_AcuGsjDwvC_|AwbE}~@wnErZ{nGr_@stEjbDakFf_@clDmKkwBbpAi_DlgA{lArLukCBukJol@w~DfCcpBwnAghCweA}{EmyAgaEbNybGeV}kCtjAq{EveBwuHlb@gyIg\\gmEhBw{G{dAmpHp_@a|MsnCcuGy~@agIe@e`KkoA}lBspBs^}sAmgIdpAumE{Y_|Oe|CioKouFwuIqnCmlDoHamBiuAgnDqp@yqIkmEqaIozAohAykDymA{uEgiE}fFehBgnCgrGmwCkiLurBkhL{jHcrGs}GkhFwpDezGgjEe_EsoBmm@g}KimLizEgbA{~DwfCwvFmhBuvBy~DsqCicBatC{z@mlCkkDoaDw_BagA}|Bii@kgCpj@}{E}b@cuJxQwkK}j@exF`UanFzM{fFumB}fCirHoTml@CoAh`A"

Having retrieved the polyline, you can decode it into latitude and longitude coordinates using decode_pl().

polyline <- "rqxeF_cxsZgr@xmCekBhMunGnWc_Ank@vBpyCqjAfbAqmBjXydAe{AoF{oEgTqjGur@ch@qfAhUuiCww@}kEtOepAtdD{dDf~BsgIuj@}tHi{C{bGg{@{rGsmG_bDbW{wCuTyiBajBytF_oAyaI}K}bEkqA{jDg^epJmbB{gC}v@i~D`@gkGmJ_kEojD_O{`FqvCetE}bGgbDm_BqpD}pEqdGiaBo{FglEg_Su~CegHw`Cm`Hv[mxFwaAisAklCuUgzAqmCalJajLqfDedHgyC_yHibCizK~Xo_DuqAojDshAeaEpg@g`Dy|DgtNswBcgDiaAgEqgBozB{jEejQ}p@ckIc~HmvFkgAsfGmjCcaJwwD}~AycCrx@skCwUqwN{yKygH}nF_qAgyOep@slIehDcmDieDkoEiuCg|LrKo~Eb}Bw{Ef^klG_AgdFqvAaxBgoDeqBwoDypEeiFkjBa|Ks}@gr@c}IkE_qEqo@syCgG{iEazAmeBmeCqvA}rCq_AixEemHszB_SisB}mEgeEenCqeDab@iwAmZg^guB}cCk_F_iAmkGsu@abDsoBylBk`Bm_CsfD{jFgrAerB{gDkw@{|EacB_jDmmAsjC{yBsyFaqFqfEi_Ei~C{yAmwFt{B{fBwKql@onBmtCq`IomFmdGueD_kDssAwsCyqDkx@e\\kwEyUstC}uAe|Ac|BakGpGkfGuc@qnDguBatBot@}kD_pBmmCkdAgkB}jBaIyoC}xAexHka@cz@ahCcfCayBqvBgtBsuDxb@yiDe{Ikt@c{DwhBydEynDojCapAq}AuAksBxPk{EgPgkJ{gA}tGsJezKbcAcdK__@uuBn_AcuGsjDwvC_|AwbE}~@wnErZ{nGr_@stEjbDakFf_@clDmKkwBbpAi_DlgA{lArLukCBukJol@w~DfCcpBwnAghCweA}{EmyAgaEbNybGeV}kCtjAq{EveBwuHlb@gyIg\\gmEhBw{G{dAmpHp_@a|MsnCcuGy~@agIe@e`KkoA}lBspBs^}sAmgIdpAumE{Y_|Oe|CioKouFwuIqnCmlDoHamBiuAgnDqp@yqIkmEqaIozAohAykDymA{uEgiE}fFehBgnCgrGmwCkiLurBkhL{jHcrGs}GkhFwpDezGgjEe_EsoBmm@g}KimLizEgbA{~DwfCwvFmhBuvBy~DsqCicBatC{z@mlCkkDoaDw_BagA}|Bii@kgCpj@}{E}b@cuJxQwkK}j@exF`UanFzM{fFumB}fCirHoTml@CoAh`A"

df <- decode_pl(polyline)
head(df)
##         lat      lon
## 1 -37.81418 144.9632
## 2 -37.80598 144.9404
## 3 -37.78867 144.9380
## 4 -37.74520 144.9341
## 5 -37.73494 144.9270
## 6 -37.73554 144.9023

And, of course, to encode a series of lat/lon coordinates you use encode_pl()

encode_pl(lat = df$lat, lon = df$lon)
## [1] "pqxeF}bxsZer@vmCgkBlMunGjWc_Apk@vBpyCqjAfbAqmBjXydAg{AoFyoEgTojGsr@gh@sfAhUuiCuw@}kEtOepAvdD{dDd~BsgIuj@{tHi{C{bGg{@{rGsmGabDbW{wCuTwiBajBytFaoA{aI{K{bEkqA{jDg^gpJkbB{gC_w@g~D`@ikGmJ_kEojD_O}`FqvCctE}bGgbDk_BspD_qEodGgaBo{FilEi_Su~CcgHw`Cm`Hv[mxFwaAisAklCuUgzAsmCalJajLqfDcdHgyCayHibCezK~Xo_DuqAqjDshAeaEpg@g`Dy|DgtNqwBegDkaAeEogBozB{jEejQ_q@ckIc~HkvFkgAufGmjCcaJwwD}~AycCrx@skCwUqwN}yKygH}nF}pAgyOep@qlIghDemDgeDkoEkuCc|LtKq~E`}Bw{Ef^klG}@gdFsvAcxBeoDcqByoDypEciFkjBc|Ku}@gr@a}IkE_qEoo@syCgG}iEczAkeBmeCqvA}rCq_AgxEgmHszB}RksB}mEgeEgnCoeD_b@iwAmZi^guB}cCm_F_iAikGsu@cbDsoB{lBk`Bi_CsfD}jFerAgrB{gDiw@}|EacB_jDkmAsjC}yBsyFaqFofEk_Ei~C{yAowFv{ByfBwKsl@onBktCq`IqmFmdGueDakDssAssCyqDmx@e\\kwEyUstC}uAe|Ac|BakGpGmfGuc@mnDguBctBmt@}kDapBmmCkdAgkB}jBcIyoC}xAexHia@cz@ahCafCayBqvBitBuuDzb@wiDe{Imt@e{DwhBwdEynDqjCapAo}AuAmsBzPi{EiPgkJ{gA}tGsJezKbcAcdK__@wuBn_AauGsjDwvC_|AybE{~@unEpZ{nGt_@stEhbDakFh_@elDoKiwBdpAi_DjgAylArLykCDukJql@u~DfCepBwnAghCweA{{EmyAgaEbN{bGcV{kCrjAq{EveByuHlb@eyIg\\gmEhBw{GydAopHn_@_|MsnCcuGw~@agIg@e`KkoA}lBspBu^}sAmgIfpAsmE}Ya|Oc|CgoKouFwuIsnCmlDmHamBkuAinDop@wqIkmEsaIqzAmhAwkD{mA}uEciE}fFghBenCirGowCgiLsrBmhL}jHerGs}GihFupDezGgjEg_EuoBim@g}KmmLizEebAy~DwfCyvFmhBuvBy~DsqCicBatC{z@klCkkDqaDw_BagA}|Bii@kgCpj@}{E{b@cuJxQwkK}j@exF~TanFzM{fFumB_gCirHmTml@AoAd`A"

Google Distance API

The Google Maps Distance API is a service that provides travel distance and time for a matrix of origins and destinations.

Finding the distances between Melbourne Airport, the MCG, a set of coordinates (-37.81659, 144.9841), to Portsea, Melbourne.

df <- google_distance(origins = list(c("Melbourne Airport, Australia"),
                                     c("MCG, Melbourne, Australia"),
                                     c(-37.81659, 144.9841)),
                      destinations = c("Portsea, Melbourne, Australia"),
                      key = key)

head(df)
$destination_addresses
[1] "Melbourne Rd, Victoria, Australia"

$origin_addresses
[1] "Melbourne Airport (MEL), Departure Dr, Melbourne Airport VIC 3045, Australia"
[2] "Jolimont Station, Wellington Cres, East Melbourne VIC 3002, Australia"       
[3] "176 Wellington Parade, East Melbourne VIC 3002, Australia"                   

$rows
                                                        elements
1 130 km, 129501, 1 hour 38 mins, 5853, 1 hour 36 mins, 5770, OK
2 104 km, 104393, 1 hour 20 mins, 4819, 1 hour 20 mins, 4792, OK
3 104 km, 104350, 1 hour 20 mins, 4814, 1 hour 20 mins, 4788, OK

$status
[1] "OK"

Google Elevation API

The Google Maps Elevation API provides elevation data for all locations on the surface of the earth, including depth locations on the ocean floor (which return negative values).

Finding the elevation of 20 points between the MCG, Melbourne and the beach at Elwood, Melbourne

google_elevation(df_locations = data.frame(lat = c(-37.81659, -37.88950),
                                           lon = c(144.9841, 144.9841)),
                  location_type = "path",
                  samples = 20,
                  key = key,
                  simplify = TRUE)
                       
$results
    elevation location.lat location.lng resolution
1  20.8899250    -37.81659     144.9841   9.543952
2   7.8955822    -37.82043     144.9841   9.543952
3   8.4334993    -37.82426     144.9841   9.543952
4   5.4820895    -37.82810     144.9841   9.543952
5  33.5920677    -37.83194     144.9841   9.543952
6  30.4819584    -37.83578     144.9841   9.543952
7  15.0097895    -37.83961     144.9841   9.543952
8  10.9842978    -37.84345     144.9841   9.543952
9  13.8762951    -37.84729     144.9841   9.543952
10 13.4834013    -37.85113     144.9841   9.543952
11 13.3473139    -37.85496     144.9841   9.543952
12 24.9176636    -37.85880     144.9841   9.543952
13 16.7720604    -37.86264     144.9841   9.543952
14  5.8330226    -37.86648     144.9841   9.543952
15 10.7889471    -37.87031     144.9841   9.543952
16  6.9589133    -37.87415     144.9841   9.543952
17  3.9915009    -37.87799     144.9841   9.543952
18  5.3637657    -37.88183     144.9841   9.543952
19  7.1594319    -37.88566     144.9841   9.543952
20  0.6697893    -37.88950     144.9841   9.543952

$status
[1] "OK"

Google Timezone API

The Google Maps Time zone API provides time offset data for locations on the surface of the earth. You request the time zone information for a specific latitude/longitude pair and date. The API returns the name of that time zone, the time offset from UTC, and the daylight savings offset.

Finding the timezone of the MCG in Melbourne

google_timezone(location = c(-37.81659, 144.9841), 
                timestamp = as.POSIXct("2016-06-05"), 
                key = key,
                simplify = FALSE)
                
[1] "{"                                                         
[2] "   \"dstOffset\" : 0,"                                     
[3] "   \"rawOffset\" : 36000,"                                 
[4] "   \"status\" : \"OK\","                                   
[5] "   \"timeZoneId\" : \"Australia/Hobart\","                 
[6] "   \"timeZoneName\" : \"Australian Eastern Standard Time\""
[7] "}"

Google Geocode API

The Google Maps Geocoding API is a service that provides geocoding and reverse geocoding of addresses.

Finding the location details for Flinders Street Station, Melbourne


df <- google_geocode(address = "Flinders Street Station",
                     key = key,
                     simplify = TRUE)

df$results$formatted_address
[1] "Flinders St, Melbourne VIC 3000, Australia"

## If your search responde multiple results, you can 
## bound the search, for example

bounds <- list(c(-37.81962,144.9657),
               c(-37.81692, 144.9684))

df <- google_geocode(address = "Flinders Street Station",
                     bounds = bounds,
                     key = key,
                     simplify = TRUE)

## (in this example only one result was returned in the original call)

The coordinates of the location can be accessed with

geocode_coordinates(df)
#         lat      lng
# 1 -37.81827 144.9671

Google Reverse Geocode API

The Google Maps Reverse Geocoding API is a service that converts geographic coordinates into a human-readable address.

Finding the street address for a set of coordinates, using result_type and location_type as bounding parameters:

df <- google_reverse_geocode(location = c(-37.81659, 144.9841),
                             result_type = c("street_address", "postal_code"),
                             location_type = "rooftop",
                             key = key,
                             simplify = TRUE)

df$results$address_components
[[1]]
          long_name        short_name                                  types
1               176               176                          street_number
2 Wellington Parade Wellington Parade                                  route
3    East Melbourne    East Melbourne                    locality, political
4          Victoria               VIC administrative_area_level_1, political
5         Australia                AU                     country, political
6              3002              3002                            postal_code

df$results$geometry
  location.lat location.lng location_type viewport.northeast.lat viewport.northeast.lng viewport.southwest.lat
1    -37.81608     144.9842       ROOFTOP              -37.81473               144.9855              -37.81743
  viewport.southwest.lng
1               144.9828

Google Places API

The Google Maps Places API gets data from the same database used by Google Maps and Google+ Local. Places features more than 100 million businesses and points of interest that are updated frequently through owner-verified listings and user-moderated contributions.

There are three types of search you can perform

A Nearby Search lets you search for places within a specified area. You can refine your search request by supplying keywords or specifying the type of place you are searching for.

A Text Search Service is a web service that returns information about a set of places based on a string — for example “pizza in New York” or “shoe stores near Ottawa” or “123 Main Street”. The service responds with a list of places matching the text string and any location bias that has been set.

A Place Detail search (using google_place_details()) can be performed when you have a given place_id from one of the other three search methods.

Text

For a text search you are required to provide a search_string

For example, here’s a query for “restaurants in Melbourne”


res <- google_places(search_string = "Restaurants in Melbourne, Australia",
                     key = key)

## View the names of the returned restaurantes, and whether they are open or not

cbind(res$results$name, res$results$opening_hours)

                                            res$results$name open_now weekday_text
1                                               Vue de monde     TRUE         NULL
2                                                      ezard    FALSE         NULL
3                                                     MoVida     TRUE         NULL
4                           Flower Drum Restaurant Melbourne     TRUE         NULL
5                                             The Press Club    FALSE         NULL
6                                                       Maha     TRUE         NULL
7                                                  Bluestone       NA         NULL
8                                                  Chin Chin     TRUE         NULL
9                                               Taxi Kitchen     TRUE         NULL
10                                           Max on Hardware     TRUE         NULL
11                                                    Attica    FALSE         NULL
12                                       Nirankar Restaurant    FALSE         NULL
13                                                  The Mill     TRUE         NULL
14                                   The Left Bank Melbourne     TRUE         NULL
15                           The Colonial Tramcar Restaurant     TRUE         NULL
16                                      Rockpool Bar & Grill     TRUE         NULL
17 Lane Restaurant Cafe & Bar - Novotel Melbourne on Collins     TRUE         NULL
18                                          Melba Restaurant     TRUE         NULL
19                                              CUMULUS INC.     TRUE         NULL
20                                    radii restaurant & bar    FALSE         NULL

A single query will return 20 results per page. You can view the next 20 results using the next_page_token that is returned as part of the initial query.


res_next <- google_places(search_string = "Restaurants in Melbourne, Australia",
                          page_token = res$next_page_token,
                          key = key)
                          
cbind(res_next$results$name, res_next$results$opening_hours)

                     res_next$results$name open_now weekday_text
1  Moshi Moshi Japanese Seafood Restaurant     TRUE         NULL
2                      Grill Steak Seafood     TRUE         NULL
3                             Conservatory     TRUE         NULL
4                                    Sarti    FALSE         NULL
5                                  Tsindos     TRUE         NULL
6                 The Cerberus Beach House     TRUE         NULL
7                   Stalactites Restaurant     TRUE         NULL
8            Hanabishi Japanese Restaurant    FALSE         NULL
9                          GAZI Restaurant     TRUE         NULL
10                           Om Vegetarian    FALSE         NULL
11                           Shark Fin Inn     TRUE         NULL
12                           Om Vegetarian     TRUE         NULL
13                 The Atlantic Restaurant     TRUE         NULL
14                                  Takumi     TRUE         NULL
15                              Pei Modern       NA         NULL
16                 Bamboo House Restaurant     TRUE         NULL
17                 Byblos Bar & Restaurant     TRUE         NULL
18                              Waterfront     TRUE         NULL
19                                   No 35     TRUE         NULL
20                        Bistro Guillaume     TRUE         NULL

Nearby

For a nearby search you are required to provide a location as a pair of latitude/longitude coordinates. You can refine your search by providing a keyword and / or a radius.

res <- google_places(location = c(-37.918, 144.968),
                    keyword = "Restaurant",
                    radius = 5000,
                    key = key)


cbind(res$results$name, res$results$opening_hours)
#                         res$results$name open_now weekday_text
# 1                              Melbourne       NA         NULL
# 2              Quest Brighton on the Bay     TRUE         NULL
# 3                         Brighton Savoy    FALSE         NULL
# 4  Caroline Serviced Apartments Brighton     TRUE         NULL
# 5      The Buckingham Serviced Apartment       NA         NULL
# 6                      Sandringham Hotel     TRUE         NULL
# 7               Indian Palace Restaurant     TRUE         NULL
# 8                       Elsternwick Park     TRUE         NULL
# 9                         Brown Cow Cafe     TRUE         NULL
# 10              Bok Choy Chinese Cuisine    FALSE         NULL
# 11                          Marine Hotel     TRUE         NULL
# 12                          Daily Planet     TRUE         NULL
# 13                         Riva St Kilda     TRUE         NULL
# 14                         New Bay Hotel     TRUE         NULL
# 15          Flight Centre North Brighton     TRUE         NULL
# 16              Vintage Cellars Brighton     TRUE         NULL
# 17                   Palace Brighton Bay     TRUE         NULL
# 18                       Brighton Toyota     TRUE         NULL
# 19                            Sportsgirl     TRUE         NULL
# 20                           Saint Kilda       NA         NULL

Google Roads

The Google Roads API provides three functions

Snap to Roads

The snap to roads function takes up to 100 GPS points collected along a route and returns the points snapped to the most likely roads that were travelled along.


df_path <- read.table(text = "lat lon
-35.27801 149.12958
-35.28032 149.12907
-35.28099 149.12929
-35.28144 149.12984
-35.28194 149.13003
-35.28282 149.12956
-35.28302 149.12881
-35.28473 149.12836
", header = T, stringsAsFactors = F)


res <- google_snapToRoads(df_path = df_path, key = key)

res$snappedPoints
  location.latitude location.longitude originalIndex                     placeId
1         -35.27800           149.1295             0 ChIJr_xl0GdNFmsRsUtUbW7qABM
2         -35.28032           149.1291             1 ChIJOyypT2hNFmsRZBtscGL0htw
3         -35.28101           149.1292             2 ChIJv5r0smlNFmsR5nunau79Fv4
4         -35.28147           149.1298             3 ChIJ601MoWlNFmsR5mvkfPp2ovA
5         -35.28194           149.1300             4 ChIJ601MoWlNFmsR5mvkfPp2ovA
6         -35.28282           149.1296             5 ChIJaUpThGlNFmsRMHWxoH7EOsc
7         -35.28313           149.1289             6 ChIJWSb8ImpNFmsRp_4JAxJFE1A
8         -35.28473           149.1283             7 ChIJtWxAZmpNFmsRlbUCkc6VtnA

The result includes the column originalIndex. This is a zero-based index that indicates which of the input coordinates has been snapped to the given location.latitude/location.longitude coordinates. So in this example, originalIndex 0 is the first row of df_path, originalIndex 1 is the second row of df_path, and so on.

Nearest Roads

The nearest roads function takes up to 100 independent coordinates and returns the closest road segment for each point.

df_points <- read.table(text = "lat lon
60.1707 24.9426
60.1708 24.9424
60.1709 24.9423", header = T)

res <- google_nearestRoads(df_points, key = key)

res$snappedPoints
  location.latitude location.longitude originalIndex                     placeId
1          60.17070           24.94272             0 ChIJNX9BrM0LkkYRIM-cQg265e8
2          60.17081           24.94271             1 ChIJNX9BrM0LkkYRIM-cQg265e8
3          60.17091           24.94270             2 ChIJNX9BrM0LkkYRIM-cQg265e8

Google Maps

A google map can be made using the google_map() function. Without any data present, or no location value set, the map will default to Melbourne, Australia.

You can also display traffic, transit (public transport) or bicycle routes using the functions add_traffic(), add_transit() and add_bicycling() respectively.

You can also include a search box in your map by using the argument search_box = TRUE, which allows you to search the maps just like you would when using Google Maps.

map_key <- "your_api_key"
google_map(key = map_key, search_box = T) %>%
  add_traffic()

Melbourne


Markers

Markers and circles can be used to show points on the map.

In this example I’m using the tram_stops data set provided with googleway.

You can specify a column in the data.frame to use to populate a popup info_window that will be displayed when clicking on a maker. The info window can display any valid HTML, as demonstrated in this Stack Overflow answer.

df <- tram_stops
df$info <- paste0("<b>Stop Name: </b>", df$stop_name)

map_key <- "your_api_key"
google_map(data = df, key = map_key) %>%
  add_markers(lat = "stop_lat", lon = "stop_lon", info_window = "info")

Marker Info Window

Heatmap

You can create a heatmap using Google’s Heatlayer.

google_map(data = tram_stops, key = map_key) %>%
  add_heatmap(lat = "stop_lat", lon = "stop_lon", option_radius = 0.0025)

Heatmap

There are a few options you can configure to change how the heatmap is plotted, for example changing the colours, and weight associated with each point in the data set

## the colours can be any of those given by colors()
tram_stops$weight <- 1:nrow(tram_stops)

google_map(data = tram_stops, key = map_key) %>%
  add_heatmap(lat = "stop_lat", lon = "stop_lon", option_radius = 0.0025,
              weight = 'weight', 
              option_gradient = c("plum1", "purple1", "peachpuff"))

Heatmap Colour


Polyline

Polylines in Google Maps are formed from a set of latitude/longitude coordinates, encoded into a polyline string.

Both the add_polylines() and add_polygons() functions in googleway can plot the encoded polyline to save the amount of data set to the browser. (They can also plot coordinates, but this is often slower).

To draw a line on a map you use the add_polylines() function. This function takes a data.frame with at least one column of data containing the polylines, or two columns containing the series of lat/lon coordinates.

Here we can plot the polyline we generated earlier from querying the directions from Melbourne to Sydney.


df <- data.frame(polyline = pl)

google_map(key = map_key) %>%
    add_polylines(data = df, polyline = "polyline", stroke_weight = 9)

Polyline


Polygon

A polygon represents an area enclosed by one or more polylines. Holes are denoted by defining an inner path wound in the opposite direction to the outer path.

To draw a polygon on a map use the add_polygons() function. This function takes a data.frame, where the polygons can be specified in one of three ways

The melbourne data set provided with googleway is a data.frame of polygons of Melbourne and the surrounding suburbs. The coordinates of the polygons are encoded in to polylines.

melbourne[melbourne$polygonId == 338 & melbourne$pathId %in% c(1, 2), ]
##     polygonId pathId     SA2_NAME             SA3_NAME             SA4_NAME
## 386       338      1 Point Nepean Mornington Peninsula Mornington Peninsula
## 387       338      2 Point Nepean Mornington Peninsula Mornington Peninsula
##     AREASQKM               polyline
## 386  67.1875 `haiFgzjrZJMJBBRIJMCCQ
## 387  67.1875 z{biFyqlrZARO@SUTOB@JL

Plotting this data is done using add_polygons()

google_map(key = "your_api_key") %>% 
  add_polygons(data = melbourne, polyline = "polyline", fill_colour = "SA4_NAME")

Melbourne Polygons

In this example I’ve specified the fill_colour to be one of the columns of the melbourne data set. The plotting functions in googleway will map the variables in the column to a given colour. The default colour scale is taken from viridisLite::viridis().

Colours & Legend

Where applicable the map layer functions provide two arguments that you can use to plot colours

Each of those arguments can be mapped to a column of data (as in the Polygon example), or if you want to use your own colours you can either

google_map(key = "your_api_key") %>% 
  add_polygons(data = melbourne, polyline = "polyline", fill_colour = "SA4_NAME", palette = viridisLite::inferno)

Colour Palette

colours <- RColorBrewer::brewer.pal(9, "Set1")
melbourne$randomColour <- sample(colours, size = nrow(melbourne), replace = T)
google_map(key = "your_api_key") %>% 
  add_polygons(data = melbourne, polyline = "polyline", fill_colour = "randomColour")

Colour Column

google_map(key = "your_api_key") %>% 
  add_polygons(data = melbourne, polyline = "polyline", fill_colour = "#FF00FF")

Single Colour

Setting legend = TRUE will give you a legend

google_map(key = "your_api_key") %>% 
  add_polygons(data = melbourne, polyline = "polyline", fill_colour = "SA4_NAME", legend = T)

Legend

You can customise the legend by supplying any of the arguments in a list to the legend_options argument

If you are displaying two legends, one for stroke_colour and one for fill_colour, you can specify different options for the different colour attributes

legendOpts <- list(
  fill_colour = list(position = "TOP_RIGHT"),
  stroke_colour = list(position = "BOTTOM_LEFT", title = "SA3")
  )

google_map(key = "your_api_key") %>% 
  add_polygons(data = melbourne, polyline = "polyline", fill_colour = "SA4_NAME", stroke_colour = "SA3_NAME", legend = T, legend_options = legendOpts)

Custom Legend

Simple Features

googleway supports plotting certain geometries from sf objects (from library(sf))

This example uses and plots the polygons from the North Carolina data set supplied with library(sf)

library(sf)
nc <- sf::st_read(system.file("shape/nc.shp", package="sf"))

google_map(data = nc) %>%
  add_polygons()

sf North Carolina

If the sf objects contains multiple geometries, you can also use the various add_* functions to add specific rows of the sf object. The mapping between the functions and the sf objects is

df <- data.frame(myId = c(1,1,1,1,1,1,1,1,2,2,2,2),
  lineId = c(1,1,1,1,2,2,2,2,1,1,1,2),
    lon = c(-80.190, -66.118, -64.757, -80.190,  -70.579, -67.514, -66.668, -70.579, -70, -49, -51, -70),
    lat = c(26.774, 18.466, 32.321, 26.774, 28.745, 29.570, 27.339, 28.745, 22, 23, 22, 22))

p1 <- as.matrix(df[4:1, c("lon", "lat")])
p2 <- as.matrix(df[8:5, c("lon", "lat")])
p3 <- as.matrix(df[9:12, c("lon", "lat")])

point <- sf::st_sfc(sf::st_point(x = c(df[1,"lon"], df[1,"lat"])))
multipoint <- sf::st_sfc(sf::st_multipoint(x = as.matrix(df[1:2, c("lon", "lat")])))
polygon <- sf::st_sfc(sf::st_polygon(x = list(p1, p2)))
linestring <- sf::st_sfc(sf::st_linestring(p3))
multilinestring <- sf::st_sfc(sf::st_multilinestring(list(p1, p2)))
multipolygon <- sf::st_sfc(sf::st_multipolygon(x = list(list(p1, p2), list(p3))))

sf <- rbind(
    sf::st_sf(geometry = polygon),
    sf::st_sf(geometry = multipolygon),
    sf::st_sf(geometry = multilinestring),
    sf::st_sf(geometry = linestring),
    sf::st_sf(geometry = point),
  sf::st_sf(geometry = multipoint)
)


google_map(data = sf) %>%
  add_markers() %>%
  add_polylines()

sf marker & line

GeoJSON

The geo_melbourne data set is a GeoJSON representation of a subset of the melbourne data set. You can use add_geojson to plot this data

google_map() %>%
  add_geojson(data = geo_melbourne)

Geojson

By default add_geojson() will apply any styles included in the GeoJSON file. In the geo_melbourne data you can see the styles fillColor, strokeColor and strokeWeight

substr(geo_melbourne[1], 1, 300)
## [1] "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{\"SA2_NAME\":\"Abbotsford\",\"polygonId\":70,\"SA3_NAME\":\"Yarra\",\"AREASQKM\":1.7405,\"fillColor\":\"#440154\",\"strokeColor\":\"#440154\",\"strokeWeight\":1},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[144.9925232,-37.8024902],[144.9926453,-37."

You can also supply a string of JSON to style all the features in the GeoJSON

style <- '{ "fillColor" : "green" , "strokeColor" : "black", "strokeWeight" : 0.5}'
google_map(key = "your_api_key") %>%
  add_geojson(data = geo_melbourne, style = style)

Or you can use a named list to have the same effect

style <- list(fillColor = "red" , strokeColor = "blue", strokeWeight = 0.5)
google_map(key = "your_api_key") %>%
  add_geojson(data = geo_melbourne, style = style)

Drag & Drop Geojson

The function add_dragdrop() creates a map on to which you can drag & drop a GeoJSON file. There is an example of this in action on my blog

Rectangles

Rectangles can be added by supplying north, east, south and west coordinates to add_rectangles()

df <- data.frame(north = 33.685, south = 33.671, east = -116.234, west = -116.251)

google_map(key = "your_api_key") %>%
  add_rectangles(data = df, north = 'north', south = 'south',
                 east = 'east', west = 'west')

Drawing

You can create your own shapes on a map using add_drawing(). The shapes you can create are

This can be particularly useful when working with shiny as the shape information can be returned when drawing is completed.

For example, use the following code to create a shiny that enables drawing circles.

library(shiny)

ui <- fluidPage(
  google_mapOutput(outputId = "map", height = "800px")
)

server <- function(input, output){

  mapKey <- "your_map_key"
  output$map <- renderGoogle_map({
    google_map(key = mapKey) %>%
      add_drawing(drawing_modes = c("circle"))
  })


  observeEvent(input$map_circlecomplete, {
    print(input$map_circlecomplete)
  })
}
shinyApp(ui, server)

When a circle is drawin, the print(input$map_circlecomplete) function prints the following information to the console (it will vary based on where you draw the circle)

$center
$center$lat
[1] -36.80489

$center$lng
[1] 144.2889


$radius
[1] 57970.9

$bounds
$bounds$south
[1] -37.32565

$bounds$west
[1] 143.6385

$bounds$north
[1] -36.28413

$bounds$east
[1] 144.9393


$randomValue
[1] 0.3688953

KML & GeoRSS

The add_kml() function renders KML and GeoRSS elements on a Google Map. All you need to specify is a URL containing the kml data.

kmlUrl <- 'https://googlemaps.github.io/js-v2-samples/ggeoxml/cta.kml'

google_map(key = "your_api_key") %>%
  add_kml(kml_url = kmlUrl)

KML

Overlays

Overalys are objects on the map that are tied to latitude/longitude coordinates, so they move when you drag or zoom the map.

You need to supply the lat/lon coordinates to the north (lat), south (lat), east (lon) and west (lon) arguments, and a URL to the overlay.

google_map(key = "your_api_key") %>%
  add_overlay(north = 40.773941, south = 40.712216, east = -74.12544, west = -74.22655,
               overlay_url = "https://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg")

Overlay


Shiny

As google_map() is an HTMLWidget, it inherently works in Shiny. As with all shiny apps the two functions you need to include are

The simplest app can be built with

library(shiny)

ui <- fluidPage(
   google_mapOutput(outputId = "map")
)

server <- function(input, output){

  map_key <- 'your_api_key'

  output$map <- renderGoogle_map({
    google_map(key = map_key)
  })
}

shinyApp(ui, server)

But, of course, this isn’t very interesting.

You can use all the standard add_* functions that have already been discussed to add various shapes and layers. But there’s also various clear_* and update_* functions that let you update those shapes and layers dynamically within the shiny app.

The clear_* functions are designed to remove objects from the app.

The update_* functions are designed to update those objects that are already on the map. This is useful for when you want to update existing polylines, polygons, rectangles, circles and markers.

library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "opacity", label = "opacity", min = 0, max = 1, 
              step = 0.01, value = 1),
  google_mapOutput(outputId = "map")
)

server <- function(input, output){
  
  map_key <- 'your_api_key'
  
  output$map <- renderGoogle_map({
    
    google_map(key = map_key) %>%
      add_polygons(data = melbourne, id = "polygonId", pathId = "pathId", 
                   polyline = "polyline", fill_opacity = 1)
  })
  
  ## observe the opacity slider changing
  observeEvent(input$opacity, {
    
    melbourne$opacity <- input$opacity
    
    google_map_update(map_id = "map") %>%
      update_polygons(data = melbourne, fill_opacity = "opacity", id = "polygonId")
  })
  
}

shinyApp(ui, server)

Note that to use the udpate_* functions you need to provide the id of the object you want to update.

The exception to the update_* functions is update_heatmap(), which allows you to both add and remove points from the heat layer.

library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "sample", label = "sample", min = 1, max = 10,
              step = 1, value = 10),
  google_mapOutput(outputId = "map")
)

server <- function(input, output){

  #map_key <- 'your_api_key'

  set.seed(20170417)
  df <- tram_route[sample(1:nrow(tram_route), size = 10 * 100, replace = T), ]
  
  output$map <- renderGoogle_map({
    google_map(key = map_key) %>%
      add_heatmap(data = df, lat = "shape_pt_lat", lon = "shape_pt_lon",
                  option_radius = 0.001)
  })

  observeEvent(input$sample,{
    
    df <- tram_route[sample(1:nrow(tram_route), size = input$sample * 100, replace = T), ]

    google_map_update(map_id = "map") %>%
      update_heatmap(data = df, lat = "shape_pt_lat", lon = "shape_pt_lon")
  })
}

shinyApp(ui, server)

Returning Data

One of the more powerful features of googleway mapping is the ability to return data from the maps to R using Shiny.

Data can be returned using

Within the google_map() call you can pass one of two values to the event_return_type argument, “list” or “json”. This controls the type of data structure returned back to R from Shiny.

Clicking on the map

Data can be retrieved from the map by clicking on it and “observing” the clicks.

The structure of the observed event is

where

library(shiny)
ui <- fluidPage(
 google_mapOutput('myMap')
)
server <- function(input, output){
 output$myMap <- renderGoogle_map({
   google_map(key = "your_api_key", event_return_type = "list")
 })
 observeEvent(input$myMap_map_click, {
    print(input$myMap_map_click)
 })
}
shinyApp(ui, server)

Clicking on the map in this example will return the following fields

$id
[1] "myMap"

$lat
[1] -37.68382

$lon
[1] 145.08

$centerLat
[1] -37.9

$centerLon
[1] 144.5

$zoom
[1] 8

$randomValue
[1] 0.4043373

Similarly, observing input$myMap_bounds_changed will observe panning the map, and input$myMap_zoom_changed will observe zooming the map.

Shape / layer clicks

Data can be retrieved from shapes by clicking on them and “observing” the clicks.

The structure of the observed event is

where

library(shiny)
ui <- fluidPage(
 google_mapOutput('map')
)
server <- function(input, output){
 output$map <- renderGoogle_map({
   google_map(key = "your_api_key", event_return_type = "list") %>%
     add_polygons(data = melbourne, polyline = "polyline")
 })
 observeEvent(input$map_polygon_click, {
    print(input$map_polygon_click)
 })
}
shinyApp(ui, server)

Clicking on one of the polygons in this example will return the fields

$id
[1] 22

$lat
[1] -38.16911

$lon
[1] 144.6872

$outerPath
[1] "dlsgFyduq..." ## truncated

$allPaths
$allPaths[[1]]
[1] "dlsgFyduqZ..." ## truncated


$randomValue
[1] 0.5917599

$layerId
[1] "defaultLayerId"

Editing & Dragging Shapes

The shape layers that have a editable argument also return data when the shape is edited.

In this case you would observe input$<map_id>_<shape>_edited

Similarly, when shapes are dragged you would observe input$<map_id>_<shape>_dragged