Plotting a grid with distance to the nearest amenity

In an earlier post I plotted the distance to the nearest pub using two data sources to find the location of pubs - Google and the Food Standards Agency. My initial code to talk to google was python based. While the FSA data was much better quality, Google data has the benefit of being global. So I decided to translate the python code across to R as a more general function that could be easily changed.

Below is three examples of the output. Essentially what the code does is:

1. draw grid over an area of land - wherever in the world you tell it to
2. at each point in the grid, call google and search for the closest amenity that you specified
3. then, take the amenity (for instance, if you search for pub, you now have the closest pub to that point) and get back how long (minutes) and far (metres) the location is from that point in the grid if you are walking
4. then, plot a line from each point on the grid to the closest amenity, colouring based on how many metres it is walking (this is not the distance as the crow flies)

Variables that change

Below are the master inputs that need to be set. This can easily be edited for more specific searches. For example, in the supermarket plot above, I added in name=coop|migros to limit the search to only the two major supermarket chains (so no budget or ethnic supermarkets are in that plot).

# Variables to input
google_key <- "YOUR_GOOGLE_API_KEY"
# bounding box limits for map
top_lat <- 47.565
bottom_lat <- 47.54
left_lng <- 7.57
right_lng <- 7.61
zoom_level=13 # resolution of map
# steps for grid
steps = 20
# thing to search for
type=bar
# see link for full list
# https://developers.google.com/places/supported_types

Functions

Pull nearest amenity

This function will take the location frame you give it, make a grid (the number of points defined by steps) and get the closest type of place you are looking for.

jb_pullnearby <- function(
GOOGLE_API_KEY = google_key,
# Map corners
lat_NW = 47.56232,
lng_NW = 7.57373,
lat_SE = 47.54263,
lng_SE = 7.60274,
steps=100,
type="restaurant"
){
library(jsonlite)
library(dplyr)

# 100 steps left and 100 down
lat_incr = (lat_SE-lat_NW)/steps
lng_incr = (lng_SE-lng_NW)/steps
# Start in the northwest and iterate to the southeast
lat_curr = lat_NW
lng_curr = lng_NW

# clear output
data_output <- NULL

# Open loop

for(i_lat in 1:steps){
for(i_lng in 1:steps){
# current location
curr_location = paste0(lat_curr,",",lng_curr)
# url to call api
url <- paste0('https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=',
curr_location,
'&key=',
GOOGLE_API_KEY,
'&rankby=distance&types=',
type)
response <- fromJSON(txt=url)$results if(!is.null(nrow(response))){ temp_location <- response$geometry$location temp_info <- response %>% select(place_id,icon,name,vicinity) temp_data <- cbind(temp_location,temp_info) # Make line of data from response # note rankby means sorted by proxomity! temp_data <- temp_data %>% mutate( n = 1:n(), loc_lat = lat, loc_lng = lng, lat = lat_curr, lng = lng_curr, i_lat = i_lat, i_lng = i_lng ) # add data data_output <- rbind(data_output,temp_data) } # Move along one lng increment lng_curr <- lng_curr+lng_incr } # longitiude loop # reset longitude lng_curr = lng_NW # Move along one lat increment lat_curr <- lat_curr+lat_incr } # latitude loop return(data_output) } # close function Get walking time and distance This function will take two locations and get back the walking time and distance from Google. jb_googledist <- function( origin=paste0(lat,",",lng), destination=paste0(lat,",",lng), GOOGLE_API_KEY=google_key){ library(XML) library(RCurl) xml.url <- paste0( 'https://maps.googleapis.com/maps/api/distancematrix/xml?origins=', origin,'&destinations=', destination, '&mode=walking&key=', GOOGLE_API_KEY, '&sensor=false') xmlfile <- xmlParse(getURL(xml.url)) time <- xmlValue(xmlChildren(xpathApply(xmlfile,"//duration")[[1]])$value)
time <- round(as.numeric(time)/60,1)
dist <- xmlValue(xmlChildren(xpathApply(xmlfile,"//distance")[[1]])$value) distance <- as.numeric(dist) output <- data.frame(time=time,distance=distance) return(output) } Run functions and plot This final code is an example of how to use the two functions to make a plot like the three above. The first code block (with the changing inputs needs to also be run, as this code uses those inputs). # Get the nearest data_locations <- jb_pullnearby( GOOGLE_API_KEY = google_key, # Map corners lat_NW = top_lat, lng_NW = left_lng, lat_SE = bottom_lat, lng_SE = right_lng, steps=steps, type=type ) # Drop to closest restaurant dataset <- data_locations %>% filter(n==1) # Get google distance # empty results df dataset_distances <- NULL # start loop over data for(i in 1:nrow(dataset)){ # current iteration i_origin = paste0(dataset$lat[i],",",dataset$lng[i]) i_destination = paste0(dataset$loc_lat[i],",",dataset$loc_lng[i]) # get distances i_distance <- jb_googledist( origin=i_origin, destination=i_destination, GOOGLE_API_KEY = google_key) # load into data dataset_distances <- rbind(dataset_distances,i_distance) } # add to data dataset <- cbind(dataset,dataset_distances) # map it library(ggmap) ## get the map from stamen basemap <- get_stamenmap( bbox = c(left = left_lng, bottom = bottom_lat, right = right_lng, top = top_lat), zoom=zoom_level, source='stamen',crop = TRUE, maptype="terrain-lines", color="bw") # Order points high to low dataset <- dataset[order(-dataset$distance),]

# Plot - pubs
ggmap(basemap,extent = 'device') +
geom_segment(
aes(x=lng, xend=loc_lng,
y=lat, yend=loc_lat,
colour=distance,
alpha=0.5),
size=2, data=dataset) +
scale_colour_gradient(limits=c(0, 2500),
low="blue", high="red")+
geom_point(aes(x=dataset$loc_lng, y=dataset$loc_lat),size=3)

ggsave("map.svg", width=10, height=10)
A post about: , , and
James Black

Epidemiologist and data scientist.