3  Learn Leaflet (1)

August 17-20, 2025

Objectives

Learn the basics of {leaflet} using my RWB data as practical example

3.1 Preliminary remark

After following Making maps with R (chapter 9 of the online book Geocomputation with R) and making my own research (see my notes in Chapter 2) it turned out that {leaflet} / leaflet.js is the most mature and widely used interactive mapping package in R interactive map drawing software. {leaflet} has twice more downloads than {tmap} which is as the moment the leading spatial geographic map tool. (See Listing / Output 3.1 which contains a list of downloads for several map drawing packages.)

Note 3.1: Added {maps} to Listing / Output 3.1

I just learned in the section Section 3.3.3 that {maps} is another important package ranking with it download numbers in the second place!

I added this package therefore to Listing / Output 3.1. The above paragraph is not correct anymore as {maps} has about 50% more downloads than {leaflet}.

R Code 3.1 : Overview of download numbers of map packages

Listing / Output 3.1: Overview of download numbers of map packages
Code
my_pkgs_dl(
  pkgs = c(
    "tmap", 
    "plotly", 
    "mapview",
    "mapdeck",
    "googleway",
    "mapsf",
    "mapmisc",
    "leaflet",
    "Rgooglemaps",
    "ggmap",
    "mapedit",
    "ggiraph",
    "maps"
    ),
  period = "last-month", 
  days = 30
)
#> # A tibble: 13 × 4
#>    package     average from       to        
#>    <chr>         <dbl> <date>     <date>    
#>  1 plotly         7413 2025-07-30 2025-08-28
#>  2 maps           4657 2025-07-30 2025-08-28
#>  3 leaflet        2513 2025-07-30 2025-08-28
#>  4 tmap            775 2025-07-30 2025-08-28
#>  5 mapview         718 2025-07-30 2025-08-28
#>  6 ggiraph         564 2025-07-30 2025-08-28
#>  7 ggmap           563 2025-07-30 2025-08-28
#>  8 mapdeck          56 2025-07-30 2025-08-28
#>  9 mapedit          55 2025-07-30 2025-08-28
#> 10 mapsf            52 2025-07-30 2025-08-28
#> 11 googleway        51 2025-07-30 2025-08-28
#> 12 mapmisc          24 2025-07-30 2025-08-28
#> 13 Rgooglemaps       0 2025-07-30 2025-08-28

Although {leaflet} has with about 3.000 downloads/day only about the half of the {plotly} downloads it is a specialized map drawing software whereas {plotly} is a general interactive web graphics tool.

It’s worth noting that {plotly} aims to be a general purpose visualization library, and thus, doesn’t aim to be the most fully featured geo-spatial visualization toolkit. That said, there are benefits to using {plotly}-based maps since the mapping APIs are very similar to the rest of {plotly}, and you can leverage larger {plotly} ecosystem (e.g., linking views client side … ). However, if you run into limitations with {plotly}’s mapping functionality, there is a very rich set of tools for interactive geospatial visualization in R, including but not limited to: {leaflet}, {mapview}, {mapedit}, {tmap}, and {mapdeck} (Robin Lovelace 2019). Quoted from chapter 4 of (Sievert 2019).

Another option to consider would be to use ggplot2::geom_sf() together with

  • plotly::ggplotly() or Hmisc::ggpltlyr() (Converts a ggplot2::ggplot() object to a {plotly} object, in {Hmisc} variant removes extraneous text that ggplotly() puts in hover text)
  • ggiraph::geom_sf_interactive() (Make ‘ggplot2’ Graphics Interactive)
  • {ggspatial} (Spatial Data Framework for ‘ggplot2’)
  • {geofacet} (‘ggplot2’ Faceting Utilities for Geographical Data) This has the advantage that I am using with {ggplot2} known territory. But in this chapter I will focus on {leaflet}.

Resource 3.1 : Leaflet resources on the Internet

I am ignoring to list tutorials using Leaflet.js from scratch and collect here only resources for learning the R {leaflet} package. Many tutorials cover some special actions like adding markers but my special focus at the moment is on choropleth maps.

Most important resources

Educational Online Courses

Additional resources

Videos

Important 3.1: This is already my second pass of the {leaflet} documentation

I have already previously worked on the {leaflet} tutorials1.

3.2 Getting started

3.2.1 Basic usage

Procedure 3.1 : Four basic steps to produce a Leaflet map

  1. Create a map widget by calling leaflet::leaflet().
  2. Add layers (i.e., features) to the map by using layer functions (e.g., leaflet::addTiles(), leaflet::addMarkers(), leaflet::addPolygons()) to modify the map widget.
  3. Repeat step 2 as desired.
  4. Print the map widget to display it.

3.2.2 Basic example

R Code 3.2 : Basic R leaflet example

Listing / Output 3.2: Basic R leaflet example
Code
library(leaflet)

m <- leaflet() |>               # (1)  Create the ma widget  
  addTiles() |>                 # (2) Add default OpenStreetMap map tiles
    addMarkers(lng = 174.768,   # (3) Add another element to the map
               lat = -36.852,
               popup = "The birthplace of R")
m                               # (4) Print the map
Graph 3.1: A basic R leaflet example. Click at the marker to see what it indicates.

3.2.3 Next step

The {leaflet} tutorial highly recommends to start with the article The Map Widget before exploring the rest of the site. It is the first article under the menu “Articles”. It seems to me that the best way to learn leaflet is to proceed with the offered article in their sequence from top to the bottom of the menu.

Watch out! 3.1: {leaflet} Documentation refers to leaflet.js version 1.34

The links to the details leaflet.js documentation refers to the very old leaflet.js version 1.34 only available through the internet archive. The current stable version is Leaflet 1.9.4, and version 2.0 is already in preparation (see: Leaflet 2.0.0-alpha).

Yesterday (August 16, 2025) the blog article Leaflet 2.0 Alpha released has been updated. It explains the new features in Leaflet.js 2.0. I assume that in the near future the R {leaflet} package will be updated.

Instead of the internet archive of the current offical documentation I will use in this book to documentation of the current latest stable version 1.9.4.

3.3 The Map Widget

3.3.1 Initializing options

The function leaflet::leaflet() returns a Leaflet map widget, which stores a list of objects that can be modified or updated later. The map widget can be initialized with certain parameters. This is achieved by populating the options argument as in the following example.

Code
# Set value for the minZoom and maxZoom settings.
leaflet(options = leafletOptions(minZoom = 0, maxZoom = 18))

The leaflet::leafletOptions() can be passed any option described in the leaflet reference document.

Using leaflet::leafletOptions(), you can set a custom CRS and have your map displayed in a non spherical Mercator projection as described in the article Working with projections in Leaflet.

3.3.2 Map methods

You can manipulate the attributes of the map widget using a series of methods.

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.

For more detailed information see the help file Methods to manipulate the map widget. The help file also refers to a detailed table listing Methods for modifying map state.

3.3.3 The data object

Both leaflet::leaflet() and the map layer functions have an optional data parameter that is designed to receive spatial data in one of several forms:

  • From base R:
    • lng/lat matrix
    • data frame with lng/lat columns
  • From the {maps} package:
    • the data frame returned from maps::map()
  • Simple features from the {sf} package:

leaflet::leaflet() is, additionally, back-compatible with {sp} SpatialDataFrames, although the use of these is discouraged for new users.

Important 3.2

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.

For a normal matrix or data frame, any numeric column could potentially contain spatial data. So we resort to guessing based on column names:

  • latitude variable is guessed by looking for columns named lat or latitude (case-insensitive)
  • longitude variable is guessed by looking for lng, long, or longitude

You can always explicitly identify latitude/longitude columns by providing lng and lat arguments to the layer function.

A map layer may use a different data object to override the data provided in leaflet::leaflet().

R Code 3.3 : Overriding data provided with leaflet::leaflet() in another layer

Listing / Output 3.3: Overriding data provided with leaflet::leaflet() in another layer
Code
df = data.frame(Lat = 1:10, Long = rnorm(10))
leaflet::leaflet()  |>  leaflet::addCircles(data = df)
glue::glue("------------------------------------------------------------------------")
leaflet::leaflet()  |>  leaflet::addCircles(data = df, lat = ~ Long, lng = ~ Lat)
Graph 3.2: The data from the first leaflet call was reversed by the second layer on the second leaflet call
#> ------------------------------------------------------------------------
Graph 3.3: The data from the first leaflet call was reversed by the second layer on the second leaflet call

In the next code chunk I am not following the tutorial but will use my own {sf} RWB dataset.

R Code 3.4 : Using {sf} RWB dataset as my first leaflet example

Listing / Output 3.4: Using {sf} RWB dataset as my first leaflet example
Code
rwb_map_2025 <- readRDS(paste0(here::here(), "/data/chap041/rwb_map_2025.rds"))


leaflet::leaflet() |> 
    leaflet::addPolygons(data = rwb_map_2025) |> 
    leaflet::addTiles(options = leaflet::tileOptions(noWrap = TRUE)) |> 
    leaflet::clearBounds()
Graph 3.4: I succeeded with my first leaflet example using myRWB dataset. But the result is still ugly. I still have to learn about the many parameters to get better maps.
Note 3.2: Evaluation of the result in Listing / Output 3.4

My first example worked. Although I have added two more lines than necessary the result is still ugly. There are so many options I have still to learn!

3.3.4 The Formula Interface

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).

R Code 3.5 : Formula Interface

Listing / Output 3.5: Arguments as R objects and the formula interface
Code
# preventing message 'Assuming "lng" and "lat" are longitude and latitude, respectively'

df = data.frame(
  lat = rnorm(100),   
  lng = rnorm(100),  
  size = runif(100, 5, 20),
  color = sample(colors(), 100)
)

m = leaflet::leaflet(df)  |>  leaflet::addTiles()
m  |>  
    leaflet::addCircleMarkers(radius = ~size, color = ~color, fill = FALSE)
glue::glue("------------------------------------------------------------------------")
m  |>  
    leaflet::addCircleMarkers(radius = runif(100, 4, 10), color = c('red'))
Graph 3.5: Arguments as R objects and the formula interface
#> ------------------------------------------------------------------------
Graph 3.6: Arguments as R objects and the formula interface

Glossary Entries

term definition
Choropleth A choropleth map is a type of thematic map that uses shading or coloring to represent statistical data across predefined geographic regions. These regions can be political boundaries, such as states or countries, or natural divisions. Each area is colored based on the value of the variable being represented, typically using a gradient where darker shades indicate higher values and lighter shades indicate lower values. This visualization technique helps viewers quickly grasp patterns and trends within the dataset. It is important to note that choropleth maps can sometimes lead to misinterpretation due to the bias introduced by the size of the regions. Larger regions may appear to have more significance simply because of their size, even if their data values are the same as smaller regions. In this case use Cartograms.
CRS A Coordinate Reference System (CRS) defines how locations on the Earth’s surface are represented in a two-dimensional plane. This is essential for accurate mapping and spatial analysis. CRSs are often defined using the PROJ.4 notation, which is a standard way to describe coordinate systems. CRSs can also be identified using EPSG codes, which are unique identifiers for different coordinate systems.
Mercator-projection The Mercator projection is a conformal cylindrical map projection first presented by Flemish geographer and mapmaker Gerardus Mercator in 1569. It became the standard map projection for navigation in the 18th century due to its unique property of representing any course of constant bearing as a straight segment on the map. This feature made it invaluable for marine navigation, as sailors could plot a straight-line course on the map and maintain a constant compass heading to reach their destination. The projection is created by projecting the spherical Earth onto a cylinder that is tangent to the equator, then unrolling the cylinder into a flat plane. This process preserves angles and the shapes of small objects, making it conformal, but it significantly distorts the size of landmasses as latitude increases away from the equator. Consequently, regions near the poles, such as Greenland and Antarctica, appear much larger than they actually are relative to landmasses near the equator. For example, Africa is approximately 14 times larger than Greenland in reality, yet on a Mercator map, they appear similar in size. Despite its distortions, the Mercator projection remains widely used, particularly in internet web maps. The Web Mercator projection, a variant that models the Earth as a perfect sphere, is employed by major mapping services like Google Maps and OpenStreetMap for its computational efficiency and suitability for zooming and panning. While it is no longer the standard for commercial and educational world maps due to its unbalanced representation, it persists in digital mapping applications. The projection's continued use highlights the trade-off between preserving navigational accuracy and representing true landmass sizes, a fundamental challenge in cartography.
RWB Reporters Without Borders (RWB), known by its French name Reporters sans frontières and acronym RSF, is an international non-profit and non-governmental organization headquartered in Paris, France, founded in 1985 in Montpellier by journalists Robert Ménard, Rémy Loury, Jacques Molénat, and Émilien Jubineau. It is dedicated to safeguarding the right to freedom of information and defends journalists and media personnel who are imprisoned, persecuted, or at risk for their work. The organization has consultative status at the United Nations, UNESCO, the Council of Europe, and the International Organisation of the Francophonie.

Session Info

Session Info

Code
xfun::session_info()
#> R version 4.5.1 (2025-06-13)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sequoia 15.6.1
#> 
#> Locale: en_US.UTF-8 / en_US.UTF-8 / en_US.UTF-8 / C / en_US.UTF-8 / en_US.UTF-8
#> 
#> Package version:
#>   askpass_1.2.1           base64enc_0.1.3         bslib_0.9.0            
#>   cachem_1.1.0            class_7.3-23            classInt_0.4-11        
#>   cli_3.6.5               commonmark_2.0.0        compiler_4.5.1         
#>   cpp11_0.5.2             cranlogs_2.1.1          crayon_1.5.3           
#>   crosstalk_1.2.2         curl_7.0.0              DBI_1.2.3              
#>   digest_0.6.37           dplyr_1.1.4             e1071_1.7-16           
#>   evaluate_1.0.5          farver_2.1.2            fastmap_1.2.0          
#>   fontawesome_0.5.3       fs_1.6.6                generics_0.1.4         
#>   glossary_1.0.0          glue_1.8.0              graphics_4.5.1         
#>   grDevices_4.5.1         grid_4.5.1              here_1.0.1             
#>   highr_0.11              htmltools_0.5.8.1       htmlwidgets_1.6.4      
#>   httr_1.4.7              jquerylib_0.1.4         jsonlite_2.0.0         
#>   kableExtra_1.4.0        KernSmooth_2.23-26      knitr_1.50             
#>   labeling_0.4.3          lattice_0.22.7          lazyeval_0.2.2         
#>   leaflet_2.2.2           leaflet.providers_2.0.0 lifecycle_1.0.4        
#>   litedown_0.7            magrittr_2.0.3          markdown_2.0           
#>   MASS_7.3.65             memoise_2.0.1           methods_4.5.1          
#>   mime_0.13               openssl_2.3.3           pillar_1.11.0          
#>   pkgconfig_2.0.3         png_0.1.8               proxy_0.4-27           
#>   R6_2.6.1                rappdirs_0.3.3          raster_3.6.32          
#>   RColorBrewer_1.1-3      Rcpp_1.1.0              renv_1.1.5             
#>   rlang_1.1.6             rmarkdown_2.29          rprojroot_2.1.1        
#>   rstudioapi_0.17.1       rversions_2.1.2         rvest_1.0.5            
#>   s2_1.1.9                sass_0.4.10             scales_1.4.0           
#>   selectr_0.4.2           sf_1.0-21               sp_2.2.0               
#>   stats_4.5.1             stringi_1.8.7           stringr_1.5.1          
#>   svglite_2.2.1           sys_3.4.3               systemfonts_1.2.3      
#>   terra_1.8.60            textshaping_1.0.1       tibble_3.3.0           
#>   tidyselect_1.2.1        tinytex_0.57            tools_4.5.1            
#>   units_0.8-7             utf8_1.2.6              utils_4.5.1            
#>   vctrs_0.6.5             viridisLite_0.4.2       withr_3.0.2            
#>   wk_0.9.4                xfun_0.53               xml2_1.4.0             
#>   yaml_2.3.10

References

Hahn, Nico. 2020. Making Maps with r. https://bookdown.org/nicohahn/making_maps_with_r5/docs/introduction.html.
Lovelace, Robin, Jakub Nowosad, and Jannes Muenchow. 2025. Geocomputation With R. 2nd ed. Boca Raton, FL: Chapman & Hall/CRC.
Sievert, Carson. 2019. Interactive Web-Based Data Visualization with r, Plotly, and Shiny. https://plotly-r.com/.

  1. I should get into the custom to document the writing dates for specific chapters. I have documented this now for this chapter directly unter the chapter heading.↩︎