# ETL
library(tidyverse)
library(tidygeocoder)
# Charts
library(leaflet)
library(leaflet.extras)18 Geo Mapping
When we analyze sales, it’s often interesting to map our customers.
For this we can use the package leaflet.
The leaflet R package is a powerful tool for creating interactive, web-based maps directly from R, using the popular open-source JavaScript library Leaflet. Let’s have quick overview of some features.
As usual, let’s start by uploading the needed libraries. Next to the tidyverse for the ETL, we will add tidygeocoder and 2 libraries for the charts, leaflet and leaftlet.extras .
Upload the raw data
To practice, we’re going to upload a simple data frame with 8 variables, especially :
3 dimensions.
customer code.
latitude and longitude : 2 values which are needed to display a location on a map.
1 measure : a sales value, to display the intensity of the sales on a map.
# Upload dataset
# Define the URL of the raw CSV file
url <- "https://raw.githubusercontent.com/nguyennico/chart_practice/main/Local_Sales_for_mapping_data.csv"
# Read the CSV file from the URL
df1 <- read.csv(url)
# formatting
df1$Customer.Code <- as.character(df1$Customer.Code)
df1$Latitude <- as.numeric(df1$Latitude)
df1$Longitude <- as.numeric(df1$Longitude)
df1$Postal.Code <- as.character(df1$Postal.Code)
# keep results
Local_Sales_for_mapping_data <- df1
glimpse(df1)Rows: 474
Columns: 8
$ Customer.Code <chr> "296", "10041", "10074", "10090", "10114", "10141…
$ Distribution.Channel <chr> "PHARMACY", "PHARMACY", "PHARMACY", "PHARMACY", "…
$ Sales.Value <dbl> 261.7200, 34.1640, 177.0390, 66.4800, 64.8180, 18…
$ Postal.Code <chr> "80335", "20537", "30159", "24105", "38100", "311…
$ Latitude <dbl> 48.1427, 53.5506, 52.3736, 54.3347, 52.2647, 52.1…
$ Longitude <dbl> 11.5552, 10.0569, 9.7371, 10.1348, 10.5233, 9.955…
$ State <chr> "Bayern", "Hamburg", "Niedersachsen", "Schleswig-…
$ Place.Name <chr> "M<fc>nchen", "Hamburg", "Hannover", "Kiel", "Bra…
19 Create Maps
19.1 Markers
Our first type of chart will be using the library leaflet and displaying all the customers as markers.
This can be done with just a few lines of code, as below. We will combine 3 functions :
leaflet()on a data frame.then 2 other functions :
addTiles()andaddMarkers().- The function
addMarkers()will use the Longitude and Latitude variables, and can have a popup, here the variable related to the Customer.Code .
- The function
# mark several points in one time
leaflet(Local_Sales_for_mapping_data) |>
addTiles() |>
addMarkers(lng = ~Longitude,
lat = ~Latitude,
popup = ~Customer.Code)When there are many customers, the reading is not easy, though we still can zoom.
So let’s have a look at other features.
19.2 Customizing Marker Icons
Let’s put some colors, depending on the amount of sales.
We start by creating a function that we will call getColor(). This function will affect a color, depending in the variable Sales.Value .
Note : we will have later on a chapter dedicated to the creation of a function; for the time being let’s just look at the methodology.
We then use this function to affect a color to the marker, using the function markerColor(); this function is used inside another function awesomeIcons(), which, on top of affecting a color to a marker, also affects an icon.
This creates an object “icons”, that we will use with the function addAwesomeMarkers() .
The function addAwesomeMarkers() is an enhanced version of the standard addMarkers() function, allowing to specify custom colors and icons from popular icon libraries.
- examples of icons libraries : Font Awesome, Bootstrap Glyphicons, and Ion icons libraries, instead of the default standard pushpin marker.
- the library argument specifies which icon library to use (e.g., ‘fa’, ‘ion’, or ‘glyphicon’), for example
library = 'ion'.
# Set a working df
df1 <- Local_Sales_for_mapping_data
# create a custom function to define colors based on the sales value
getColor <- function(df1) {
sapply(df1$Sales.Value, function(Sales.Value) {
if(Sales.Value <= 200) {
"green"
} else if(Sales.Value <= 2000) {
"orange"
} else {
"red"
} })
}
# define icons' colors
icons <- awesomeIcons(
icon = 'ios-close',
iconColor = 'black',
library = 'ion',
markerColor = getColor(df1)
)
# create map
leaflet(df1) |> addTiles() |>
addAwesomeMarkers(~Longitude,
~Latitude,
icon=icons,
label=~as.character(Sales.Value)
)The result is insightful, but still quite dense.
In this situation, it can be interesting to use another type of marker : clusters.
19.3 Marker Cluster
It’s a convenient way to keep a map light :
giving an overview : locations of the customers and their number.
allowing to zoom, by clicking on it.
Inside the function addMarkers() we will write the syntax : clusterOptions = markerClusterOptions() .
# set a working df
df1 <- Local_Sales_for_mapping_data
# create map
leaflet(df1) |> addTiles() |> addMarkers(
clusterOptions = markerClusterOptions()
)Assuming "Longitude" and "Latitude" are longitude and latitude, respectively
19.4 Represent other forms on the map
The previous maps inform us about the locations of the customers. However, they didn’t inform us about the sales values.
Now, let’s display a map with some circles to indicate where are the highest sales. This time we will use the function addCircles(), with some attributes:
longitude and latitude.
weight and radius.
popup.
Note : we can change the size of the circles with the function radius() .
# set a working df
df1 <- Local_Sales_for_mapping_data
# map using the function addCircles
leaflet(df1) |> addTiles() |>
addCircles(
lng = ~Longitude,
lat = ~Latitude,
weight = 1,
radius = ~sqrt(Sales.Value) * 500,
popup = ~paste(Customer.Code, ":", Sales.Value),
color = "#a500a5", fillOpacity = 0.5
)19.5 Add a gradient of colors and its legend
We also can display using a gradient of colors.
Prior to this, we can create a function colors(), based on the function colorNumeric(), and affect this function to the previous addCircles() one.
The colorNumeric() function from the R package leaflet creates a palette function that maps continuous numeric data to a color gradient.
# set a working df
df1<- Local_Sales_for_mapping_data
# create a function for the colors, with threshold of 5
# YlOrRd means from dark red to very pale yellow; already predefined in R
colors <- colorNumeric("YlOrRd", df1$Sales.Value, n = 5)
# plot data
leaflet(df1) |> addTiles() |>
addCircles(
lng = ~Longitude,
lat = ~Latitude,
weight = 1,
radius = ~sqrt(Sales.Value) * 500,
popup = ~paste(Customer.Code, ":", Sales.Value),
color = ~colors(Sales.Value),
fillOpacity = 0.9
) |>
addLegend(pal = colors, values = ~Sales.Value, opacity = 0.9)19.6 HeatMap
Finally, we’re now going to use the library leaflet.extras, to display a heatmap.
We will use 2 new functions :
addProviderTiles(), selecting the value OpenStreetMap.DE to get the map of Germany.addHeatmap(), which works pretty much the same as the previous functions.
The syntax is as follow :
# set a working df
df1<- Local_Sales_for_mapping_data
# create map
leaflet(df1) |> addProviderTiles(providers$OpenStreetMap.DE) |>
addHeatmap(
lng = ~Longitude,
lat = ~Latitude,
intensity = ~Sales.Value,
blur = 20,
max = 0.05,
radius = 15)20 Get longitude & latitude
To use the package leaflet, we need longitude and latitude coordinates.
How to do if we don’t have them ?
Then, if we have the address, we can do geocoding with the library tidygeocoder and get the longitude and latitude.
First, let’s create a simple data frame with some addresses :
# create a dataframe with addresses
some_addresses <- tibble::tribble(~name,
~addr,
"White House",
"1600 Pennsylvania Ave NW, Washington, DC",
"Transamerica Pyramid",
"600 Montgomery St, San Francisco, CA 94111",
"Martell",
"16 Avenue Paul Firino Martell, 16100 Cognac, France",
"Willis Tower",
"233 S Wacker Dr,
Chicago,
IL 60606")
# display data frame
some_addresses# A tibble: 4 × 2
name addr
<chr> <chr>
1 White House "1600 Pennsylvania Ave NW, Washington, DC"
2 Transamerica Pyramid "600 Montgomery St, San Francisco, CA 94111"
3 Martell "16 Avenue Paul Firino Martell, 16100 Cognac, France"
4 Willis Tower "233 S Wacker Dr, \n Ch…
Now let’s use the function geocode() to get the longitude and latitude of those addresses.
We will use the method = 'osm', related to Open Street Map .
# geocode the addresses
lat_longs <- some_addresses |>
geocode(addr,
method = 'osm',
lat = latitude ,
long = longitude)Passing 4 addresses to the Nominatim single address geocoder
Query completed in: 4.2 seconds
glimpse(lat_longs)Rows: 4
Columns: 4
$ name <chr> "White House", "Transamerica Pyramid", "Martell", "Willis To…
$ addr <chr> "1600 Pennsylvania Ave NW, Washington, DC", "600 Montgomery …
$ latitude <dbl> 38.89764, 37.79519, 45.69219, 41.87874
$ longitude <dbl> -77.0365528, -122.4027902, -0.3305204, -87.6359612
And now, let’s display the locations on a map, with as popup the names of the locations.
# create map
leaflet(lat_longs) |> addTiles() |>
addMarkers(lng = ~longitude,
lat = ~latitude,
popup = ~name)