Code
m <- leaflet::leaflet() |>
leaflet::addTiles() |> # Add default OpenStreetMap map tiles
leaflet::addMarkers(
lng = 174.768, lat = -36.852,
popup = "The birthplace of R"
)
m # Print the map
Chapter section list
Procedure B.1 : Basic steps to use {leaflet}
You create a Leaflet map with these basic steps:
Code Collection B.1 : Title for code collection
R Code B.1 : Basic example to demonstrate {leaflet}
m <- leaflet::leaflet() |>
leaflet::addTiles() |> # Add default OpenStreetMap map tiles
leaflet::addMarkers(
lng = 174.768, lat = -36.852,
popup = "The birthplace of R"
)
m # Print the map
R Code B.2 : Born and raised in Vienna
m <- leaflet::leaflet() |>
leaflet::addTiles() |> # Add default OpenStreetMap map tiles
leaflet::addMarkers(
lng = 16.398184, lat = 48.206776,
popup = "Place where I was born and raised"
) |>
leaflet::addMarkers(
lng = 16.399601, lat = 48.205767,
popup = "My elementary and secondary school"
) |>
leaflet::addMarkers(
lng = 16.374392, lat = 48.203045,
popup = "Secondary school with electrotechnical focus"
)
m # Print the map
The function leaflet::leaflet()
returns a Leaflet map widget, which stores a list of objects that can be modified or updated later. Most functions in this package have an argument map
as their first argument, which makes it easy to use the pipe operator %>%
in the {magrittr} package or the R native pipe |>
.
The map widget can be initialized with certain parameters. The leaflet::leafletOptions()
can be passed any option described in the leaflet reference document. Using the leaflet::leafletOptions()
, you can set a custom CRS and have your map displayed in a non spherical Mercator projection as described in Working with projections in Leaflet.
R Code B.3 : Initializing options example
# Set value for the minZoom and maxZoom settings.
leaflet::leaflet(
options = leaflet::leafletOptions(minZoom = 0, maxZoom = 18)
)
You can manipulate the attributes of the map widget using a series of methods. Please see the help page ?setView
for details. (See the list of zoom/panoptions.)
leaflet::setView()
sets the center of the map view and the zoom level;leaflet::fitBounds()
fits the view into the rectangle [lng1, lat1] – [lng2, lat2];leaflet::clearBounds()
clears the bound, so that the view will be automatically determined by the range of latitude/longitude data in the map layers if provided.Both leaflet::leaflet()
and the map layer functions (starting with add[capitalLetter]…
) have an optional data parameter that is designed to receive spatial data in one of several forms:
maps::map()
The data argument is used to derive spatial data for functions that need it; for example, if data is a {sf} Simple Features data.frame, then calling leaflet::addPolygons()
on that map widget will know to add the polygons from the geometry column.
It is straightforward to derive these variables from sf
objects since they always represent spatial data in the same way. On the other hand, for a normal matrix or data frame, any numeric column could potentially contain spatial data. So we resort to guessing based on column names:
You can always explicitly identify latitude/longitude columns by providing lng and lat arguments to the layer function.
For example, we do not specify the values for the arguments lat
and lng
in leaflet::addCircles()
below, but the columns Lat and Long in the data frame df
will be automatically used.
: Reference latitude/longitude columns implicitly & explicitly
R Code B.4 : Reference latitude/longitude columns implicitly
# add some circles to a map
df = base::data.frame(Lat = 1:10, Long = rnorm(10))
leaflet::leaflet(df) |>
leaflet::addCircles()
R Code B.5 : Reference latitude/longitude columns explicitly
# add some circles to a map
df = base::data.frame(Lat = 1:10, Long = rnorm(10))
leaflet::leaflet(df) |>
leaflet::addCircles(lng = ~Long, lat = ~Lat)
See Section B.2.4 for more info on the ~
syntax.
R Code B.6 : Override data layer
leaflet::leaflet() |>
leaflet::addCircles(data = df) |>
leaflet::leaflet() |>
leaflet::addCircles(
data = df,
lat = ~ Lat,
lng = ~ Long,
color = "red"
)
R Code B.7 : Using {sf} data
polygon1 <- sf::st_polygon(
base::list(
base::cbind(
base::c(2, 4, 4, 1, 2),
base::c(2, 3, 5, 4, 2)
)
)
)
polygon2 <- sf::st_polygon(list(cbind(c(5, 4, 2, 5), c(2, 3, 2, 2))))
polygon3 <- sf::st_polygon(list(cbind(c(4, 4, 5, 10, 4), c(5, 3, 2, 5, 5))))
polygon4 <- sf::st_polygon(list(cbind(c(5, 6, 6, 5, 5), c(4, 4, 3, 3, 4))))
multi_polygon <- sf::st_multipolygon(list(
list(cbind(c(4, 4, 5, 10, 4), c(5, 3, 2, 5, 5))),
list(cbind(c(5, 6, 6, 5, 5), c(4, 4, 3, 3, 4)))
))
sf_polygons <- sf::st_sf(geometry = sf::st_sfc(polygon1, polygon2, multi_polygon))
leaflet::leaflet(height = "300px") |>
leaflet::addPolygons(data = sf_polygons)
R Code B.8 : Using {maps} data
mapStates = maps::map("state", fill = TRUE, plot = FALSE)
leaflet::leaflet(data = mapStates) |>
leaflet::addTiles() |>
leaflet::addPolygons(
fillColor = grDevices::topo.colors(10, alpha = NULL),
stroke = FALSE
)
R Code B.9 : Using {rnaturalearth} data
world_map_countries <- base::readRDS("data/chapter09/world_map_countries.rds")
leaflet::leaflet(data = world_map_countries) |>
leaflet::addTiles() |>
leaflet::addPolygons(fillColor =
grDevices::topo.colors(10, alpha = NULL),
stroke = FALSE
)
The arguments of all layer functions can take normal R objects, such as a numeric vector for the lat
argument, or a character vector of colors for the color
argument. They can also take a one-sided formula, in which case the formula will be evaluated using the data argument as the environment. For example, ~ x
means the variable x in the data object, and you can write arbitrary expressions on the right-hand side, e.g., ~ sqrt(x + 1)
.
Code Collection B.2 : Formula interface demonstration
R Code B.10 : Formula interface demonstration: Points varies by size and color
df = base::data.frame(
lat = stats::rnorm(100),
lng = stats::rnorm(100),
size = stats::runif(100, 5, 20),
color = base::sample(grDevices::colors(), 100)
)
m = leaflet::leaflet(df) |>
leaflet::addTiles()
m |>
leaflet::addCircleMarkers(
radius = ~size,
color = ~color,
fill = FALSE
)
#> Assuming "lng" and "lat" are longitude and latitude, respectively
R Code B.11 : Formula interface demonstration: All points are red, random size variation
m |>
leaflet::addCircleMarkers(
radius = stats::runif(100, 4, 10),
color = c('red')
)
#> Assuming "lng" and "lat" are longitude and latitude, respectively
Making choropleths with {leaflet} is easy. In this example, we’ll duplicate the step-by-step choropleth tutorial from the Leaflet.js website.
We’ll start by loading the data from JSON. While the Leaflet.js example loads the JSON directly into JavaScript, with the {leaflet} R package we instead want to load the data into R.
In this case, we’ll use the {sf} package to load the data into an sf
data.frame, which will let us easily manipulate the geographic features, and their properties, in R.
Code Collection B.3 : Loading example US data into R
R Code B.12 : Load example US states data into R
#> [1] "sf" "tbl_df" "tbl" "data.frame"
#> [1] "id" "name" "density" "geometry"
As you can see, we now have an {sf} data.frame with name
(state name) and density
(population density in people/mi^2) columns from the GeoJSON.
R Code B.14 : Load would country WHR data
#> [1] "sf" "tbl_df" "tbl" "data.frame"
#> [1] "iso_a3" "name" "sovereignt" "continent" "area"
#> [6] "pop_est" "pop_est_dens" "economy" "income_grp" "gdp_cap_est"
#> [11] "life_exp" "well_being" "footprint" "HPI" "inequality"
#> [16] "gender" "press" "WHR" "geometry"
Next, let’s make a basic map with just the outline of the states/countries. For our basemap, we’ll use the same “mapbox.light” MapBox style that the example does; if you don’t have a MapBox account, you can just use leaflet::addTiles()
in place of the leaflet::addProviderTiles()
call, or choose a free provider.
Code Collection B.4 : Basic choropleth map
R Code B.15 : Basic US states map
map_states <- leaflet::leaflet(states) |>
leaflet::setView(-96, 37.8, 4) |>
leaflet::addProviderTiles(
provider = "MapBox",
options = leaflet::providerTileOptions(
id = "mapbox.light",
accessToken = Sys.getenv('MAPBOX_PUBLIC_TOKEN')
)
)
map_states |> leaflet::addPolygons()
#> Warning in sf::st_is_longlat(x): bounding box has potentially an invalid value
#> range for longlat data
Explanation of leaflet::setView()
map
lng
(longitude: east to west; -180° to 180°)lat
(latitude: north to south; -90° to 90°)zoom
options = list()
map
is the dataset (states
in this case)lng
/lat
values is point that center on the interesting area. You can find the coordinates for instance with the latlong.net-service.At each zoom level, each tile is divided in four, and its size (length of the edge, given by the tileSize
option) doubles, quadrupling the area. In other words, the width and height of the world is 256 * 2^zoomlevel (in pixels). So the formula in our example is 256 * 2^4
= 4096 pixels.
Most tile services offer tiles up to zoom level 18, depending on their coverage. This is enough to see a few city blocks per tile. (For more information see the Leaflet.JS tutorial on zoom levels.)
R Code B.16 : Basic world map
map_world <- leaflet::leaflet(world_whr_2024) |>
leaflet::setView(0, 10, 1) |>
leaflet::addProviderTiles(
provider = "MapBox",
options = leaflet::providerTileOptions(
id = "mapbox.light",
accessToken = Sys.getenv('MAPBOX_PUBLIC_TOKEN')
)
)
map_world |> leaflet::addPolygons()
The world center point is lng
= 0 and lat
= 0. But because of the height-width ratio of the graphics the north part is a little bit hidden. After some experimentation it turned out that leaflet::setView(0, 10, 1)
is the best match.
R Code B.17 : World map focused to Europe
map_europe <- leaflet::leaflet(world_map_countries) |>
leaflet::setMaxBounds(-24.5, 71.5, 34.3, 41.5) |>
leaflet::addProviderTiles(
provider = "MapBox",
options = leaflet::providerTileOptions(
id = "mapbox.light",
accessToken = Sys.getenv('MAPBOX_PUBLIC_TOKEN'),
minZoom = 3,
maxZoom = 8
)
)
map_europe |> leaflet::addPolygons()
Here I am using coordinates for maximal boundaries with a specific minimum and maximum zoom level. The idea is to focus on Europe.
Now, let’s color the states according to their population density. You have various options for mapping data to colors; for this example we’ll match the Leaflet.js tutorial by mapping a specific set of bins into {RColorBrewer} colors.
Procedure B.2 : Mapping data to colors
leaflet::colorBin()
to generate a palette function that maps the {RColorBrewer} “YlOrRd"
colors to our bins.leaflet::addPolygons()
to use the palette function and the density values to generate a vector of colors for fillColor
, and also add some other static style properties.Code Collection B.5 : Mapping data to colors
R Code B.18 : Mapping data to colors for population densities fo US states
bins_states <- c(0, 10, 20, 50, 100, 200, 500, 1000, Inf)
pal_states <- leaflet::colorBin(
palette = "YlOrRd",
domain = states$density,
bins = bins_states
)
map_states |> leaflet::addPolygons(
fillColor = ~pal_states(density),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7)
#> Warning in sf::st_is_longlat(x): bounding box has potentially an invalid value
#> range for longlat data
R Code B.19 : Mapping WHR world data for countries of the world to colors
bins_world <- c(1, 2, 3, 4, 5, 6, 7, 8)
pal_world <- leaflet::colorBin(
palette = "YlOrRd",
domain = world_whr_2024$WHR,
bins = bins_world
)
map_world |> leaflet::addPolygons(
fillColor = ~pal_world(WHR),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7)
The next thing we’ll want is to make the polygons highlight as the mouse passes over them. The leaflet::addPolygon()
function has a highlight argument that makes this simple.
Code Collection B.6 : Adding interaction to the leaflet map
R Code B.20 : Show boundaries of states with mouse hovering
map_states |> leaflet::addPolygons(
fillColor = ~pal_states(density),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
highlightOptions = leaflet::highlightOptions(
weight = 5,
color = "#666",
dashArray = "",
fillOpacity = 0.7,
bringToFront = TRUE))
#> Warning in sf::st_is_longlat(x): bounding box has potentially an invalid value
#> range for longlat data
R Code B.21 : Show boundaries of countries with mouse hovering
map_world |> leaflet::addPolygons(
fillColor = ~pal_world(WHR),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
highlightOptions = leaflet::highlightOptions(
weight = 2,
color = "black",
dashArray = "",
fillOpacity = 0.7,
bringToFront = TRUE))
(The Leaflet.js tutorial also adds an event handler that zooms into a state when it’s clicked. This isn’t currently possible with the {leaflet} R package, except with either custom JavaScript or using {shiny}, both of which are outside the scope of this example.)
Now let’s expose the state names and values to the user.
The Leaflet.js tutorial shows the hovered-over state’s information in a custom control. Again, that’s possible by adding custom JavaScript or using {shiny}, but for this example we’ll use the built-in labels feature instead.
We’ll generate the labels by handcrafting some HTML, and passing it to base::lapply(htmltools::HTML)
so that {leaflet} knows to treat each label as HTML instead of as plain text. We’ll also set some label options to improve the style of the label element itself.
Code Collection B.7 : Showing custom info when hovering with the mouse
R Code B.22 : Showing states name and population density when hovering with the mouse over the states
labels_states <- base::sprintf(
"<strong>%s</strong><br/>%g people / mi<sup>2</sup>",
states$name, states$density
) |>
base::lapply(htmltools::HTML)
map_states <- map_states |> leaflet::addPolygons(
fillColor = ~pal_states(density),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
highlightOptions = leaflet::highlightOptions(
weight = 5,
color = "#666",
dashArray = "",
fillOpacity = 0.7,
bringToFront = TRUE),
label = labels_states,
labelOptions = leaflet::labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "15px",
direction = "auto"))
#> Warning in sf::st_is_longlat(x): bounding box has potentially an invalid value
#> range for longlat data
map_states
R Code B.23 : Showing country name and WHR score when hovering with the mouse over the country
labels_world <- base::sprintf(
"<strong>%s</strong><br/>%g Well-being score",
world_whr_2024$name, world_whr_2024$WHR
) |>
base::lapply(htmltools::HTML)
map_world <- map_world |> leaflet::addPolygons(
fillColor = ~pal_world(WHR),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
highlightOptions = leaflet::highlightOptions(
weight = 2,
color = "black",
dashArray = "",
fillOpacity = 0.7,
bringToFront = TRUE),
label = labels_world,
labelOptions = leaflet::labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "15px",
direction = "auto"))
map_world
As our final step, let’s add a legend. Because we chose to color our map using leaflet::colorBin()
, the leaflet::addLegend()
function makes it particularly easy to add a legend with the correct colors and intervals.
Code Collection B.8 : Adding legend to a map
R Code B.24 : Adding legend to population density of US states
map_states |> leaflet::addLegend(
pal = pal_states,
values = ~density,
opacity = 0.7,
title = "Pop.density",
position = "bottomright")
R Code B.25 : Adding legend to map of world countries WHR scores
map_world |> leaflet::addLegend(
pal = pal_world,
values = ~WHR,
opacity = 0.7,
title = "WHR",
position = "bottomleft")
The {leaflet} package includes functions to show and hide map layers. You can allow users to decide what layers to show and hide, or programmatically control the visibility of layers using server-side code in Shiny.
In both cases, the fundamental unit of showing/hiding is the group.
A group is a label given to a set of layers. You assign layers to groups by using the group parameter when adding the layers to the map.
R Code B.26 : Understanding groups: faked example
leaflet::leaflet() |>
leaflet::addTiles() |>
leaflet::addMarkers(data = coffee_shops, group = "Food & Drink") |>
leaflet::addMarkers(data = restaurants, group = "Food & Drink") |>
leaflet::addMarkers(data = restrooms, group = "Restrooms")
Many layers can belong to same group. But each layer can only belong to zero or one groups (you can’t assign a layer to two groups).
Groups and Layer IDs may appear similar, in that both are used to assign a name to a layer. However, they differ in that layer IDs are used to provide a unique identifier to individual markers and shapes, etc., while groups are used to give shared labels to many items.
You generally provide one group value for the entire leaflet::addMarkers()
call, and you can reuse that same group value in future leaflet::add*()
calls to add to that group’s membership (as in the example above).
layerId
arguments are always vectorized: when calling e.g., leaflet::addMarkers()
you need to provide one layer ID per marker, and they must all be unique. If you add a circle with a layer ID of "foo"
and later add a different shape with the same layer ID, the original circle will be removed.
You can use {leaflet}’s layers control feature to allow users to toggle the visibility of groups.
Code Collection B.9 : Title for code collection
R Code B.27 : Numbered R Code Title (Original)
outline <-
datasets::quakes[grDevices::chull(
datasets::quakes$long,
datasets::quakes$lat
),
]
map <- leaflet::leaflet(datasets::quakes) |>
# Base groups
leaflet::addTiles(group = "OSM (default)") |>
leaflet::addProviderTiles(
leaflet::providers$CartoDB.Positron,
group = "Positron (minimal)"
) |>
leaflet::addProviderTiles(
leaflet::providers$Esri.WorldImagery,
group = "World Imagery (satellite)"
) |>
# Overlay groups
leaflet::addCircles(
~ long,
~ lat,
~ 10 ^ mag / 5,
stroke = FALSE,
group = "Quakes",
fillColor = "tomato"
) |>
leaflet::addPolygons(
data = outline,
lng = ~ long,
lat = ~ lat,
fill = FALSE,
weight = 2,
color = "#FFFFCC",
group = "Outline"
) |>
# Layers control
leaflet::addLayersControl(
baseGroups = base::c(
"OSM (default)",
"Positron (minimal)",
"World Imagery (satellite)"
),
overlayGroups = base::c(
"Quakes",
"Outline"
),
options = leaflet::layersControlOptions(collapsed = FALSE)
)
map
R Code B.28 : Numbered R Code Title (Tidyverse)
1 + 1
#> [1] 2
leaflet::addLayersControl()
distinguishes between base groups, which can only be viewed one group at a time, and overlay groups, which can be individually checked or unchecked.
Although base groups are generally tile layers, and overlay groups are usually markers and shapes, there is no restriction on what types of layers can be placed in each category.
Only one layers control can be present on a map at a time. If you call leaflet::addLayersControl()
multiple times, the last call will win
You can use leaflet::showGroup()
and leaflet::hideGroup()
to show and hide groups from code. This mostly makes sense in a Shiny context with leaflet::leafletProxy()
, where perhaps you might toggle group visibility based on input controls in a sidebar.
You can also use leaflet::showGroup()
/leaflet::hideGroup()
in conjunction with leaflet::addLayersControl()
to set which groups are checked by default.
Code Collection B.10 : Title for code collection
R Code B.29 : Numbered R Code Title (Original)
map |> leaflet::hideGroup("Outline")
R Code B.30 : Numbered R Code Title (Tidyverse)
1 + 1
#> [1] 2
Finally, you can remove the layers in a group using leaflet::clearGroup()
. Note that this doesn’t just remove the layers from the group, it also removes them from the map. (It does not, however, remove the group itself from the map; it still exists, but is empty.)
If markers are added to different groups, and when using marker clustering as described in the marker page, {leaflet} will generate different sets of clusters for different groups. This allows showing/hiding of marker clusters belonging to a group independently of other marker clusters in other groups.
Code Collection B.11 : Title for code collection
R Code B.31 : Numbered R Code Title (Original)
quakes <- datasets::quakes |>
dplyr::mutate(
mag.level = base::cut(
mag, base::c(3,4,5,6),
labels = base::c('>3 & <=4', '>4 & <=5', '>5 & <=6')))
quakes.df <- base::split(quakes, quakes$mag.level)
l <- leaflet::leaflet() |> leaflet::addTiles()
base::names(quakes.df) |>
purrr::walk(function(df) {
l <<- l |>
leaflet::addMarkers(
data = quakes.df[[df]],
lng = ~ long,
lat = ~ lat,
label = ~ base::as.character(mag),
popup = ~ base::as.character(mag),
group = df,
clusterOptions =
leaflet::markerClusterOptions(
removeOutsideVisibleBounds = FALSE
),
labelOptions = leaflet::labelOptions(
noHide = FALSE,
direction = 'auto'
)
)
}
)
l |>
leaflet::addLayersControl(
overlayGroups = base::names(quakes.df),
options = leaflet::layersControlOptions(collapsed = FALSE)
)
R Code B.32 : Numbered R Code Title (Tidyverse)
1 + 1
#> [1] 2
In this section I want to apply my new knowledge about layers. My practice example are the WHR data of several years. My aim is to show the distribution of the happiness scores for each year with maps of different providers.
I will start with the comparison of two datasets: The first year of WHR data (2011) and the last available year (2024).
My first step is to prepare the data.
R Code B.33 : Prepare data
whr_final <- base::readRDS("data/chapter09/whr_final.rds")
world_map_countries <- base::readRDS("data/chapter09/world_map_countries.rds")
whr_map_final <-
dplyr::left_join(
world_map_countries,
whr_final,
dplyr::join_by(iso3)
) |>
dplyr::filter(wb_group_code == "WLD") |>
dplyr::select(1:4, 8) |>
dplyr::relocate(
geometry,
.after = dplyr::last_col()
) |>
dplyr::rename(score = ladder_score) |>
dplyr::arrange(name) |>
tidyr::pivot_wider(
names_from = year,
values_from = score
)
skimr::skim(whr_map_final)
Name | whr_map_final |
Number of rows | 161 |
Number of columns | 16 |
_______________________ | |
Column type frequency: | |
character | 2 |
numeric | 13 |
sfc | 1 |
________________________ | |
Group variables | None |
Variable type: character
skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
---|---|---|---|---|---|---|---|
iso3 | 0 | 1 | 3 | 3 | 0 | 161 | 0 |
name | 0 | 1 | 4 | 32 | 0 | 161 | 0 |
Variable type: numeric
skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
---|---|---|---|---|---|---|---|---|---|---|
2011 | 9 | 0.94 | 5.40 | 1.11 | 3.01 | 4.58 | 5.23 | 6.25 | 7.86 | ▂▇▇▅▃ |
2012 | 10 | 0.94 | 5.42 | 1.12 | 2.94 | 4.57 | 5.34 | 6.31 | 7.69 | ▂▇▇▅▅ |
2014 | 8 | 0.95 | 5.37 | 1.16 | 2.84 | 4.52 | 5.21 | 6.27 | 7.59 | ▃▇▇▇▆ |
2015 | 10 | 0.94 | 5.39 | 1.15 | 2.90 | 4.40 | 5.31 | 6.30 | 7.53 | ▃▆▇▇▅ |
2016 | 11 | 0.93 | 5.36 | 1.14 | 2.69 | 4.50 | 5.28 | 6.10 | 7.54 | ▂▆▇▇▅ |
2017 | 10 | 0.94 | 5.38 | 1.12 | 2.90 | 4.45 | 5.36 | 6.17 | 7.63 | ▃▇▇▇▅ |
2018 | 10 | 0.94 | 5.42 | 1.11 | 3.08 | 4.54 | 5.37 | 6.19 | 7.77 | ▃▇▇▆▃ |
2019 | 13 | 0.92 | 5.48 | 1.10 | 2.57 | 4.73 | 5.51 | 6.23 | 7.81 | ▂▃▇▆▃ |
2020 | 16 | 0.90 | 5.53 | 1.08 | 2.52 | 4.85 | 5.48 | 6.22 | 7.84 | ▁▅▇▇▃ |
2021 | 19 | 0.88 | 5.55 | 1.09 | 2.40 | 4.89 | 5.57 | 6.29 | 7.82 | ▁▃▇▇▃ |
2022 | 27 | 0.83 | 5.53 | 1.15 | 1.86 | 4.66 | 5.67 | 6.32 | 7.80 | ▁▂▆▇▃ |
2023 | 21 | 0.87 | 5.52 | 1.18 | 1.72 | 4.63 | 5.78 | 6.37 | 7.74 | ▁▂▆▇▅ |
2024 | 17 | 0.89 | 5.57 | 1.16 | 1.36 | 4.67 | 5.86 | 6.43 | 7.74 | ▁▂▅▇▅ |
Variable type: sfc
skim_variable | n_missing | complete_rate | missing | complete | n | n_unique | valid |
---|---|---|---|---|---|---|---|
geometry | 0 | 1 | 0 | 161 | 161 | 1 | 161 |
The first difficulty I have to meet is to get radio buttons for the data of different years. I solved this issue by setting a fixed provider and using the baseGroup
arguments for the data of the different years.
I am not sure if this is the correct approach.
R Code B.34 : Example: Compare WHR data 2011 with 2024
bins_world <- c(1, 2, 3, 4, 5, 6, 7, 8)
pal_2011 <- leaflet::colorBin(
palette = "YlOrRd",
domain = whr_map_final$`2011`,
bins = bins_world
)
pal_2024 <- leaflet::colorBin(
palette = "YlOrRd",
domain = whr_map_final$`2024`,
bins = bins_world
)
map_whr <-
leaflet::leaflet(whr_map_final) |>
leaflet::setView(0, 10, 2) |>
leaflet::addProviderTiles("CartoDB.Positron") |>
# Base groups
leaflet::addPolygons(
fillColor = ~pal_2011(`2011`),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
group = "2011"
) |>
leaflet::addPolygons(
fillColor = ~pal_2024(`2024`),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
group = "2024"
) |>
# Layers control
leaflet::addLayersControl(
baseGroups = base::c(
"2024",
"2011"
),
options = leaflet::layersControlOptions(collapsed = FALSE)
)
map_whr
R Code B.35 : Example: Add custom info for WHR data 2011 with 2024
bins_world <- c(1, 2, 3, 4, 5, 6, 7, 8)
pal_2011 <- leaflet::colorBin(
palette = "YlOrRd",
domain = whr_map_final$`2011`,
bins = bins_world
)
pal_2024 <- leaflet::colorBin(
palette = "YlOrRd",
domain = whr_map_final$`2024`,
bins = bins_world
)
labels_2011 <- base::sprintf(
"<strong>%s: 2011</strong><br/>%g Well-being score",
whr_map_final$name, whr_map_final$`2011`
) |>
base::lapply(htmltools::HTML)
labels_2024 <- base::sprintf(
"<strong>%s: 2024</strong><br/>%g Well-being score",
whr_map_final$name, whr_map_final$`2024`
) |>
base::lapply(htmltools::HTML)
map_whr_info <-
leaflet::leaflet(whr_map_final) |>
leaflet::setView(0, 10, 2) |>
leaflet::addProviderTiles("CartoDB.Positron") |>
# Base groups
leaflet::addPolygons(
fillColor = ~pal_2011(`2011`),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
group = "2011",
highlightOptions = leaflet::highlightOptions(
weight = 2,
color = "black",
dashArray = "",
fillOpacity = 0.7,
bringToFront = TRUE),
label = labels_2011,
labelOptions = leaflet::labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "15px",
direction = "auto"
)
) |>
leaflet::addPolygons(
fillColor = ~pal_2024(`2024`),
weight = 2,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
group = "2024",
highlightOptions = leaflet::highlightOptions(
weight = 2,
color = "black",
dashArray = "",
fillOpacity = 0.7,
bringToFront = TRUE),
label = labels_2024,
labelOptions = leaflet::labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "15px",
direction = "auto"
)
) |>
# Layers control
leaflet::addLayersControl(
baseGroups = base::c(
"2024",
"2011"
),
options = leaflet::layersControlOptions(collapsed = FALSE)
)
map_whr_info
I need to simplify the redundant code by using different functions. This would especially important if I am going to use all 13 years (2011, 2012, 2014-2024). The only difference is the year
variable.
Another issue would be the long legend and the clumsy control by moving the cursor to the legend to choose the desired year and then moving back to map to see the details of the desired country. This destroy in some way a direct comparison of the different colors & values of the specified country.
A complete different approach that comes to my mind is using interactive control via {shiny}. See Section 4.5.3 especially my comments to a serverless {shiny} app in Note 4.1.