library(sf)
library(leaflet)
library(RColorBrewer)
library(dplyr)Dynamic mapping
Objectives of the fourth section
This fourth section presents a method for generating dynamic maps for publication on the web. By dynamic, we mean the ability to zoom in and out, and to bring up information when hovering over or clicking on the map. By the end of this sixth section, you will be able to perform the following operations:
- generate a choropleth map in leaflet format
- adjust mouse interactions
Data provided
For this exercise, we will use the following data provided:
- the layer of divisions with population densities (divisions_PK_pop.gpkg see first part)
Interactive choropleth map
We begin by loading the necessary libraries. We will use the sf library rather than terra to load our vector, as leaflet does not support terra’s spatVector format. The RColorBrewer library allows us to use preconfigured colour palettes that are relevant to our project.
We then load the vector layer of divisions containing an attribute relating to population density. We take this opportunity to check the data structure.
# import of the layer of the divisions with densities in sf format
pk_div_pop <- st_read("./Data_bonus/divisions_PK_pop.gpkg")Reading layer `divisions_PK_pop' from data source
`/home/poulpe/Enseignement/hors_UP/LCWU_online/R_for_Geomatics/Data_bonus/divisions_PK_pop.gpkg'
using driver `GPKG'
Simple feature collection with 31 features and 7 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -283875.3 ymin: 2621633 xmax: 1303036 ymax: 4120742
Projected CRS: WGS 84 / UTM zone 42N
# we check the data
print(head(pk_div_pop))Simple feature collection with 6 features and 7 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -283875.3 ymin: 2754998 xmax: 1093622 ymax: 3904815
Projected CRS: WGS 84 / UTM zone 42N
NAME_2 COUNTRY NAME_1 TYPE_2 Census_2017 area_km2 density_inh
1 Azad Kashmir Pakistan Azad Kashmir Division 4032363 13897.56 290.14899
2 Kalat Pakistan Balochistan Division 2174722 137881.15 15.77244
3 Makran Pakistan Balochistan Division 1484788 54564.87 27.21143
4 Nasirabad Pakistan Balochistan Division 1661077 16799.92 98.87409
5 Quetta Pakistan Balochistan Division 3764730 63238.25 59.53248
6 Sibi Pakistan Balochistan Division 963941 26537.85 36.32326
geom
1 MULTIPOLYGON (((950408.5 36...
2 MULTIPOLYGON (((235244.5 28...
3 MULTIPOLYGON (((-19025.22 2...
4 MULTIPOLYGON (((457008.1 31...
5 MULTIPOLYGON (((-111280.5 3...
6 MULTIPOLYGON (((430331.3 32...
print(summary(pk_div_pop$density_inh)) Min. 1st Qu. Median Mean 3rd Qu. Max.
15.77 135.32 310.65 672.74 698.83 4475.84
The leaflet library only handles layers whose reference coordinate system is WGS84. We therefore change the SCR of our layer if necessary.
# We reproject the layer into WGS84 if necessary (required by Leaflet)
if(st_crs(pk_div_pop)$input != "EPSG:4326") {
pk_div_pop <- st_transform(pk_div_pop, 4326)
}We then create a colour palette based on thresholds, referred to here as breaks. We make breaks in 20% quantiles on the population density column. We then apply the colour palette named YlOrRd from RColorBrewer to the population densities according to the thresholds defined above.
# we create a color palette
# we define the breaks from 0 to 100% every 20% (O.2)
breaks <- quantile(pk_div_pop$density_inh, probs = seq(0, 1, 0.2), na.rm = TRUE)
# we ceate the color palette
pal <- colorBin("YlOrRd", domain = pk_div_pop$density_inh, bins = breaks)We will now create the labels that will be displayed when hovering over a division. We will display the name of the division in bold, followed on the line below by the density rounded to one decimal place.
# we define the labels for the popups
labels <- sprintf(
"<strong>%s</strong><br/>
Density : %g inh/km²",
pk_div_pop$NAME_2,
round(pk_div_pop$density_inh, 1)
) %>% lapply(htmltools::HTML)Finally, we create the map itself by adding our layer of divisions on top of an OpenStreetMap background. We adjust various formatting options, add labels, a legend and a zoom level.
# we create the leaflet map
carte <- leaflet(pk_div_pop) %>%
# we add a basemap
addProviderTiles(providers$CartoDB.Positron) %>%
# we add the polygons of the divisions
addPolygons(
fillColor = ~pal(density_inh),
weight = 1,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
highlight = highlightOptions(
weight = 3,
color = "#666",
dashArray = "",
fillOpacity = 0.8,
bringToFront = TRUE
),
label = labels,
labelOptions = labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "15px",
direction = "auto"
)
) %>%
# we add the legend
addLegend(
pal = pal,
values = ~density_inh,
opacity = 0.7,
title = "Population density<br>(inh/km²)",
position = "bottomright"
) %>%
# we center the map on Pakistan
setView(lng = 69.5, lat = 29.7, zoom = 5)
# we display the map
carteIf we wish to export the HTML file corresponding to this interactive map, simply run the following command once the map has been generated.
htmlwidgets::saveWidget(carte, "./Data_processed/map_divisions_densities.html")Complete the following two tasks:
- Change the various layout settings (colour palette, opacity, zoom level, etc.) to understand them better.
- Repeat the same process for the divisions in a district of your choice.
Adding to external data
Here, we will display a tooltip containing a link to the search results for the division we clicked on, using the Duckduckgo search engine. The script is identical to the previous one, except that a new variable popup is created and called. In this pop-up, we retrieve the name of the division and create the generic search URL with this name.
# loading of the necessary libraries
library(sf)
library(leaflet)
library(RColorBrewer)
library(dplyr)
# we imoort the layer of the divisions with densities in sf format
pk_div_pop <- st_read("./Data_bonus/divisions_PK_pop.gpkg")Reading layer `divisions_PK_pop' from data source
`/home/poulpe/Enseignement/hors_UP/LCWU_online/R_for_Geomatics/Data_bonus/divisions_PK_pop.gpkg'
using driver `GPKG'
Simple feature collection with 31 features and 7 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -283875.3 ymin: 2621633 xmax: 1303036 ymax: 4120742
Projected CRS: WGS 84 / UTM zone 42N
# We reproject the layer into WGS84 if necessary (required by Leaflet)
if(st_crs(pk_div_pop)$input != "EPSG:4326") {
pk_div_pop <- st_transform(pk_div_pop, 4326)
}
# we create a color palette
# we define the breaks from 0 to 100% every 20% (O.2)
breaks <- quantile(pk_div_pop$density_inh, probs = seq(0, 1, 0.2), na.rm = TRUE)
# we create the color palette
pal <- colorBin("YlOrRd", domain = pk_div_pop$density_inh, bins = breaks)
# we define the labels for the popups
labels <- sprintf(
"<strong>%s</strong><br/>
Density : %g inh./km²",
pk_div_pop$NAME_2,
round(pk_div_pop$density_inh, 1)
) %>% lapply(htmltools::HTML)
# we create popups with a clickable link
labels_links <- sprintf(
"<strong>%s</strong><br/>
Density : %g inh./km²<br/>
<a href='https://duckduckgo.com/?q=%s&kp=-1&kl=us-en' target='_blank'>
📊 See on the web</a>",
pk_div_pop$NAME_2, # we use the name of the division
round(pk_div_pop$density_inh, 1),
pk_div_pop$NAME_2
) %>% lapply(htmltools::HTML)
# we create the leaflet map
carte <- leaflet(pk_div_pop) %>%
# we add the OSM basemap
addProviderTiles(providers$CartoDB.Positron) %>%
# we add the polygons of the divisions with the relevant symbology
addPolygons(
fillColor = ~pal(density_inh),
weight = 1,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
highlight = highlightOptions(
weight = 3,
color = "#666",
dashArray = "",
fillOpacity = 0.8,
bringToFront = TRUE
),
popup = labels_links, # we add the clickable links
popupOptions = popupOptions(
maxWidth = 250,
closeButton = TRUE
),
label = labels,
labelOptions = labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "13px",
direction = "auto"
)
) %>%
# we add the legend
addLegend(
pal = pal,
values = ~density_inh,
opacity = 0.7,
title = "Population density<br>(inh./km²)",
position = "bottomright"
) %>%
# we center the map on Pakistan
setView(lng = 69.5, lat = 29.7, zoom = 5)
# we display the map
carteWe now have an interactive map that easily redirects us to an external page.
Change the label to display the Goole page for the division you clicked on.