Geocoding: OpenStreetMap + R + Tableau | by jose Luis Rodriguez | Towards Data Science

In the past, Google Maps did not require an API token, which made it helpful for short use cases. Now, one must create a cloud account. To be honest, it is surprising that Google allowed the token free API to go on for as long as it did.
In looking for an alternative for short use cases requiring the geo location of a street address, I found that OpenStreetMap is a simple and useful replacement.
This feature is primarily targeted toward users who already have some experience working with R. These users will find the integration beneficial for several reasons:
Full access to any R package or function that has been installed in R
Use of Tableau capabilities to further analyze and understand data
Provides a platform to integrate open source data and software with proprietary software
Users must have access to R/RStudio and Tableau. The two softwares can either be present on the same computer or can be present remotely (cloud).
There is a way to execute R or Python commands from Tableau in order to allow us to leverage the capabilities of these programming languages (functions, models and packages). You can do this via calculated fields that dynamically communicate with an R or Python instance, and the results are returned back to Tableau as a predetermined data type (INT, REAL, BOOL, STR).
This integration is helpful when there is a need for dynamically computed fields to support analysts’ needs without having to run code separately and send a new dataset back. For this reason, we will not be using this capability, but you can check a related blog below.
In many cases, we have access to data with location information (e.g. Address, City, County, Zip Code, Country). If we want to visualize this data with more detail, we need to use geolocation.
The geolocation coordinates (longitude, latitude) need to be calculated based on the location data.
Nominatim is a free search engine for OpenStreetMap data. Using Nominatim, we can easily calculate geolocation coordinates from a given address.
OpenStreetMap consists of data built by a community of mappers who contribute and maintain data about roads, trails, cafés, railway stations, and much more, all over the world.
https://www.openstreetmap.org/about
To load packages in R/RStudio, we are going to use tidyverse, which is a collection of R packages designed for data science as well as other packages to help with data cleaning and processing. The code blocks below allow you to:
Check if the required package is installedInstall uninstalled packagesLoad the package after installation
Check if the required package is installed
Install uninstalled packages
Load the package after installation
Package Tidyverse
www.tidyverse.org
# CHECK IF PACKAGE IS INSTALLEDif(!require("tidyverse")){  # IF PACKAGE NOT FOUND, INSTALL IT  install.packages("tidyverse", dependencies = TRUE)  # LOAD PACKAGE AFTER INSTALLETION  library("tidyverse")}
Package rvest
github.com
# CHECK IF PACKAGE IS INSTALLEDif(!require("rvest")){# IF PACKAGE NOT FOUND, INSTALL IT  install.packages("rvest", dependencies = TRUE)# LOAD PACKAGE AFTER INSTALLETION  library("rvest")}
Package stringi
github.com
# CHECK IF PACKAGE IS INSTALLEDif(!require("stringi")){# IF PACKAGE NOT FOUND, INSTALL IT  install.packages("stringi", dependencies = TRUE)# LOAD PACKAGE AFTER INSTALLETION  library("stringi")}
Package jsonlite
github.com
# CHECK IF PACKAGE IS INSTALLEDif(!require("jsonlite")){# IF PACKAGE NOT FOUND, INSTALL IT  install.packages("jsonlite", dependencies = TRUE)# LOAD PACKAGE AFTER INSTALLETION  library("jsonlite")}
The geocode function below allows us to make requests to Nominatim (a search engine for OpenStreetMap data).
Nominatim: https://nominatim.openstreetmap.org/
Resources: https://nominatim.org/release-docs/develop/api/Search
The function takes and inputs the following parameters which can be modified depending on your needs: [Name], [Address], [City] , [State] , [Zipcode]
geocode <- function(name, address, city, state, zipcode){  # NOMINATIM SEARCH API URL  src_url <- "https://nominatim.openstreetmap.org/search?q="    # CREATE A FULL ADDRESS  addr <- paste(address, city, state, zipcode, sep = "%2C")    # CREATE A SEARCH URL BASED ON NOMINATIM API TO RETURN GEOJSON  requests <- paste0(src_url, query, "&format=geojson")    # ITERATE OVER THE URLS AND MAKE REQUEST TO THE SEARCH API  for (i in 1:length(requests)) {        # QUERY THE API TRANSFORM RESPONSE FROM JSON TO R LIST    response <- read_html(requests[i]) %>%      html_node("p") %>%      html_text() %>%      fromJSON()        # FROM THE RESPONSE EXTRACT LATITUDE AND LONGITUDE COORDINATES    lon <- response$features$geometry$coordinates[[1]][1]    lat <- response$features$geometry$coordinates[[1]][2]        # CREATE A COORDINATES DATAFRAME    if(i == 1) {      loc <- tibble(name = name[i],                     address = str_replace_all(addr[i], "%2C", ","),                    latitude = lat, longitude = lon)    }else{      df <- tibble(name = name[i],                    address = str_replace_all(addr[i], "%2C", ","),                   latitude = lat, longitude = lon)      loc <- bind_rows(loc, df)    }  }  return(loc)}
This information is derived from inspections of restaurants and other food establishments in Chicago from January 1, 2010 to the present. Inspections are performed by staff from the Chicago Department of Public Health’s Food Protection Program using a standardized procedure.
data.cityofchicago.org
The food inspection data has geolocation coordinates, and we can use them to check the accuracy of our results. In this sample dataset, we will only use the location columns to find the coordinates of restaurants.
In the code chunk below, we are reading the data and making some changes to make sure that the address works with the Nominatim API.
Reading the data
# READ THE DATAdata <- read_csv("food_inspection_sample.csv")# REMOVE SPACE FROM COLUMNScolnames(data) <- str_replace_all(colnames(data)," ", "_")
Cleaning The Data
# REMOVE SPACE FROM RESULTSdata$Results <- data$Results %>%   str_replace_all(" w/ ", " with ") %>%   str_replace_all(" ", "_")# EXTRACT RISK LEVEL data$Risk <- tolower(data$Risk) %>%   str_extract("\\(.*\\)") %>%   str_extract("[a-z]+")head(data)
In this section, we will prepare the address variable to match the Nominatim API style.
# EXTRACT THE ADDRESS VARIABLE address <- data$Address
Modifying the address variable to match the API
# CLEAN SPECIAL CASES (e.g. 1 N MAY BLDG)query <- str_replace_all(string = address,                          pattern = "BLDG",                          replacement = " ")# CLEAN SPECIAL CASES (e.g. 3333-3339 N CLARK)query <- stri_replace(str = query,                       replacement = " ",                       regex = "(-[0-9]+\\s)")# REPLACE SPACES (\\s) OR COMMAS (,) WITH PLUS SIGN (+)query <- str_replace_all(string = query,                          pattern = "\\s|,",                          replacement = "+")
After the address variable is ready for the API, we can use the geocode function created at the beginning to find the geo coordinates of the restaurants.
df <- geocode(name = data$DBA_Name,              address = query,              city = data$City,               state = data$State,              zipcode = data$Zipcode)
Finally, we can merge the two datasets to create a main one and work in Tableau. To achieve this, we use a left_join by the variable name on the returned DataFrame and DBA_Name variable from the original dataset.
geo_df <- data %>% left_join(df, by = c("DBA_Name" = "name"))
Now that there is a main dataset with geo coordinates, we can import it to Tableau for further analysis.
To start working on Tableau, connect to the file viaText file (CSV). This is the file that was previously created in R.
Tableau data interpreter helps to identify the data types of your columns, but it is always important to double check to ensure that the data type is correct. In this case, we are going to make sure that the variables (latitude and longitude) data type (geographic role) is correct.
A good practice when working in Tableau is to Extract the dataset that you are working on by selecting the option on the top right corner of the screen. This will prevent you from overwriting/damaging the original data.
When finished making changes to the data types, go to the highlighted worksheet section on the bottom left side of the screen. A prompt to save the dataset (extract) will appear. Follow the prompt to save it in the default location.
On the worksheet, you will see the dataset columns in two sections depending of the data type (Dimensions and Measures). In the measures section, you will find the Longitude and Latitude variables. To map the coordinates, follow the steps below:
Double-click on the Longitude variable and Tableau will place the variable in the correct axis (columns).Double-click on the Latitude variable, and it will be placed in the correct axis (rows).After placing the variables in the correct axis, change the data type from Measure to Dimension for each variable.
Double-click on the Longitude variable and Tableau will place the variable in the correct axis (columns).
Double-click on the Latitude variable, and it will be placed in the correct axis (rows).
After placing the variables in the correct axis, change the data type from Measure to Dimension for each variable.
With the addresses located in the map, we can now add more features, such as county boundaries, zip codes, location details, and color.
Double-click on the DBA Name variable to automatically assign it as a Detail Mark to see the name of each location.Double-click on the Risk variable. Since there are three categories, the Color Mark is automatically assigned.When hovering the mouse over the points, we should be able to see the name of the restaurant, risk, and geo coordinates.
Double-click on the DBA Name variable to automatically assign it as a Detail Mark to see the name of each location.
Double-click on the Risk variable. Since there are three categories, the Color Mark is automatically assigned.
When hovering the mouse over the points, we should be able to see the name of the restaurant, risk, and geo coordinates.
Tableau allows us to add additional layers to the default map. In this case, it would be helpful to know zip code labels and boundaries.
On the main toolbar, select Map → Map Layers.A Map Layer view should come out on the left corner of the worksheet. Scroll down and enable the Zip Code Boundaries and Zip Code Labels options.
On the main toolbar, select Map → Map Layers.
A Map Layer view should come out on the left corner of the worksheet. Scroll down and enable the Zip Code Boundaries and Zip Code Labels options.
Finally, you can zoom in to take a closer look at a specific area.
Keep in mind, this blog post only scratches the surface of what is possible when we integrate OpenStreetMap, R, and Tableau. OpenStreetMap API does not require a token, which others (Google, Mapquest) require.
By leveraging OpenStreetMap as a resource, simple use cases can be deeply enriched. With that being said, be mindful of the amount of requests you make at any given time.