Getting started with the data

Several packages are used in this tutorial example:

To install them all, execute the following line of code:

install.packages(c("sf","mapsf","rnaturalearth","ggplot2","mapiso","mapview"))

Data Import

The database is delivered in two files:

  • geometries.gpkg is a geographic layer containing the geometries drawn by the respondents, associated with variables that characterize them. **1 line = 1 geometry
  • survey_anon.csv is a table containing all other variables resulting from the survey. 1 line = 1 respondent.

Each of these files is accompanied by a dictionary of variables in Excel format (geom_dictionnary.xlsx and survey_dictionnary.xlsx).

Respondents database

The main database file can be imported using the R-base function read.csv().

BDR <- read.csv(file = "data/survey.csv", 
                header = TRUE, 
                encoding = "UTF8")

# Display column names (variables)
colnames(BDR)
 [1] "X0001_met_respID_aut"         "X0004_met_firact_aut"        
 [3] "A0101_cad_langua"             "A0302_cad_medium_lab_en"     
 [5] "A0303_cad_enviro_lab_en"      "A0405_cad_univer_lab"        
 [7] "A0405_cad_univer_lab_city"    "A0405_cad_univer_lab_country"
 [9] "A0406_cad_fields_lab_rec"     "A0407_cad_levels_lab_en"     
[11] "A0508_cad_gender_lab_en"      "A0609_cad_birthc_lab_en"     
[13] "A0609_cad_birthc_lab_rec"     "A0610_cad_birthy_lab"        
[15] "A0611_cad_iddoc1_lab_en"      "A0611_cad_iddoc1_lab_rec"    
[17] "A0612_cad_iddoc2_lab_en"      "A0612_cad_iddoc2_lab_rec"    
[19] "A0613_cad_belong_lab_rec"     "B0701_lan_langu5_lab_rec"    
[21] "B0701_lan_otlan5_lab_rec"     "B0702_lan_dailan_lab_rec"    
[23] "B0702_lan_otdail_lab_rec"     "C0901_mob_living_lab_en"     
[25] "C1002_mob_livfam_lab_rec"     "C1002_mob_livstu_lab_rec"    
[27] "C1002_mob_livpro_lab_rec"     "C1002_mob_livoth_lab_rec"    
[29] "C1103_mob_travel_lab_en"      "C1204_mob_trlist_lab_rec"    
[31] "D1301_mob_living_lab_en"      "D1402_mob_livpro_lab_rec"    
[33] "D1402_mob_livstu_lab_rec"     "D1503_mob_travel_lab_en"     
[35] "D1604_mob_trlist_lab_rec"     "E1801_map_cresid_lab_en"     
[37] "F2101_med_topics_lab_en"      "F2202_med_intern_lab_en"     
[39] "F2202_med_locale_lab_en"      "F2202_med_nation_lab_en"     
[41] "F2303_med_medias_lab_en"      "F2304_med_langue_lab_rec"    
[43] "F2304_med_lanoth_lab_rec"     "G2501_cul_lgsort_lab_en"     
[45] "G2602_cul_lgview_lab_rec"     "G2602_cul_otview_lab_rec"    
[47] "G2603_cul_lgread_lab_rec"     "G2603_cul_otread_lab_rec"    
[49] "G2704_cul_orsort_lab_en"      "G2805_cul_orread_lab_rec"    
[51] "G2805_cul_orview_lab_rec"     "H3001_eur_words1_lab_rec"    
[53] "H3001_eur_words2_lab_rec"     "H3001_eur_words3_lab_rec"    
[55] "H3001_eur_words1_lab_en"      "H3001_eur_words2_lab_en"     
[57] "H3001_eur_words3_lab_en"      "H3001_eur_words3_lab_fr"     
[59] "H3001_eur_words1_lab_fr"      "H3001_eur_words2_lab_fr"     
[61] "H3102_eur_images_lab_en"      "I3301_cad_educp1_lab_en"     
[63] "I3302_cad_educp2_lab_en"      "I3403_cad_paysp1_lab_en"     
[65] "I3403_cad_paysp1_lab_rec"     "I3404_cad_paysp2_lab_en"     
[67] "I3404_cad_paysp2_lab_rec"     "K3601_met_submit_aut"        

Geometries database

Use the sf package to import the geographic layer containing the mental maps.

library(sf)

# List layers of the geopackage file
st_layers("data/geometries.gpkg")
Driver: GPKG 
Available layers:
   layer_name geometry_type features fields                 crs_name
1 mental_maps Multi Polygon     2744     33 WGS 84 / Pseudo-Mercator

The st_read() function can be used to import geographic data from various formats.

BDG <- st_read(dsn = "data/geometries.gpkg", 
               layer = "mental_maps")

# Display column names (variables)
colnames(BDG)
 [1] "ID_geom"                     "X0001_met_respID_aut"       
 [3] "Index"                       "Zoom"                       
 [5] "wkt"                         "geojson"                    
 [7] "E1902_map_region"            "E1903_map_rgname"           
 [9] "E1903_map_rgname_lab_rec"    "E1903_map_rgname_lab_fr"    
[11] "E1903_map_rgname_lab_en"     "E1903_map_rgname_concept_fr"
[13] "E1903_map_rgname_concept_en" "E1903_map_rgname_scale"     
[15] "E1903_map_rgname_type"       "E1903_map_rgname_parent1_en"
[17] "E1903_map_rgname_parent2_en" "E1903_map_rgname_parent3_en"
[19] "typ_uni"                     "typ_mult_uni"               
[21] "typ_scale"                   "typ_over"                   
[23] "typ_dnk"                     "typ_HS_del"                 
[25] "typ_only_text"               "geom_carto"                 
[27] "weight_geom"                 "weight_geom_tot"            
[29] "weight_resp"                 "weight_resp_tot"            
[31] "weight_scale"                "weight_scale_tot"           
[33] "area"                        "geom"                       

All attributes (variables) in this geographic layer relate exclusively to geometries. For cross-referencing with other survey variables, it is necessary to join the two data files.

Joining databases

Each respondent could draw up to 5 different geometries. We will join the respondents database to the geometries database.

The join key common to both databases is the respondent’s identifier: X0001_met_respID_aut..

To join the two databases, use the merge() function.

BDG_merged <- merge(x = BDG,
                    y = BDR,
                    by = "X0001_met_respID_aut")   # Join fields = respondent id

It is possible to join only a selection of variables from the respondents database, as follows:

BDG_merged <- merge(x = BDG,
                    y = BDR[c("X0001_met_respID_aut",          # Champs de jointure : id répondant
                              "A0405_cad_univer_lab_country",  # Pays de l'université du répondant
                              "A0508_cad_gender_lab_en",       # Genre du répondant en anglais
                              "A0406_cad_fields_lab_rec",      # Champ d'étude en anglais
                              "A0407_cad_levels_lab_en",       # Niveau d'étude en anglais
                              "A0611_cad_iddoc1_lab_en")],     # Papier d'identité 1 en anglais
                    by = "X0001_met_respID_aut")


Descriptive statistics

Contingency table

For a given variable, you can calculate the distribution of individuals per modality with the table() function.

Here is an example with the country of residence variable: A0407_cad_levels_lab_en.

table(BDR$A0407_cad_levels_lab_en)

         A-level or equivalent Bachelors degree or equivalent 
                           623                            907 
  Masters degree or equivalent              PhD or equivalent 
                           418                             46 
          Prefer not to answer 
                            36 

The table() function can be used to calculate a contingency table between two (or more) qualitative variables.

Here is an example with the variables of country of residence and respondent gender:

table(BDR$A0405_cad_univer_lab_country, 
      BDR$A0508_cad_gender_lab_en)
                     
                      Female Male Other Prefer not to answer
  France Antilles        181   48     2                    0
  France Metropolitan    420  272    15                   13
  Germany                347  219     9                    7
  Ireland                 56   40     1                    2
  Tunisia                124   39     0                    1
  Turkey                 125  104     3                    1

Graphical representation

It’s easy to plot a contigency table, either with R-base functions or by using the ggplot2 package.

Contingency table calculation :

freqCountry <- as.data.frame(table(BDR$A0407_cad_levels_lab_en))
A. Plot with R-base
barplot(height = freqCountry$Freq, 
        names = freqCountry$Var1,
        main = "Nb. of students per level of studies",
        ylab = "Nb. students",
        col = "#85c1d3",
        las = 2)

B. Plot with ggplot2
library(ggplot2)

ggplot(freqCountry, aes(x = Var1, y = Freq)) +   
  geom_segment(aes(x = Var1,                
                   xend = Var1, 
                   y = 0, 
                   yend = Freq), 
               color = "#93C1D5", 
               lwd = 2) +
  geom_point(size = 5,                      
             pch = 19,
             color = "#93C1D5") +
  ggtitle("Nb. of students per country") + 
  xlab("Country") +                         
  ylab("Nb. of students")                   

Contingency table calculation :

genderCountry <- table(BDR$A0405_cad_univer_lab_country,
                       BDR$A0508_cad_gender_lab_en)
A. Plot with R-base
plot(t(genderCountry), 
     col = c("#009E7390","#F0E44290", "#0072B290", "#D55E0090", "#CC79A790"), 
     # col = c("#85c1d3"), 
     border = "white", 
     las = 1, 
     off = 0.5,
     main = "Nb. of students per country & gender",  # Title
     xlab = "Gender",                           # Axe x title
     ylab = "Country")

B. Plot with ggplot2

With the ggplot2 package and the ggmosaic extension, you can represent a contingency table without first calculating it.

library(ggmosaic)

ggplot(data = BDR) +
  geom_mosaic(aes(x = product(A0405_cad_univer_lab_country, 
                              A0508_cad_gender_lab_en), 
                  fill= A0405_cad_univer_lab_country)) +
  ggtitle("Nb. of students per country & gender") +     
  xlab("Gender") +                                    
  ylab("Country") + 
  theme(legend.position = "none")                          

Cartography

As part of the ANR-DFG IMAGEUN project, several cartographic processes were carried out on the respondents’ mental maps. Below, we explain how to manipulate and map these geographic data (vector polygons) with R.

We begin by selecting the geometries that can be used for the cartography

BDG_carto <- BDG_merged[BDG_merged$geom_carto == TRUE,]

Retrieving a base map

The rnaturalearth package enables us to retrieve the map of the world as a vector layer.

library(rnaturalearth)

world <- ne_download(scale = 110,
                     category = "cultural",
                     returnclass = "sf")

# Plotting base map
plot(st_geometry(world))

To use this base map as a cover layer for the mental maps, it is necessary to harmonize the map projection used (WGS84 / Pseudo Mercator).

# Reprojection of the base map in "EPSG:3857"
world <- st_transform(world, crs = st_crs(BDG_carto))


Plotting the mental maps

First, select the geometry with its identifier. For example :

# Select geometry with ID "GEOM1564"
BDG_plot <- BDG_carto[BDG_carto$ID_geom %in% "GEOM1564", ]

As with any sf object (geographic layer), it is then very simple to display the geometry on the world base map using the plot() function, or in a more sophisticated way using the mapsf thematic cartography package.

# Plot the geometry to define map bbox
plot(st_geometry(BDG_plot))

# Plot the world base map
plot(st_geometry(world), 
     col = "#cccccc", 
     border = "white",  
     add = TRUE)

# Plot the geometry
plot(st_geometry(BDG_plot),
     col = "#85c1d350", 
     border = "#85c1d3",
     lwd = 2,
     add = TRUE)

It’s also easy to add information to the mental map, for example :

# Title with respondent ID and geometry ID
title(paste0(BDG_plot$X0001_met_respID_aut, " - ", BDG_plot$ID_geom), 
      adj = 0)

# Country of residence
mtext(paste0("Country : ", BDG_plot$A0405_cad_univer_lab_country), 
      side = 3, adj = 0)

# Word associated with the geometry
mtext(text = paste0("Word : ", BDG_plot$E1903_map_rgname), 
      side = 3, adj = 0, line = -1)

# Traduction of the word associated with the geometry
mtext(text = paste0("trad : ", BDG_plot$E1903_map_rgname_lab_en), 
      side = 3, adj = 0, line = -2, font = 3)

library(mapsf)

# Intinializing the map bounding box
mf_init(BDG_plot)

# Plot world base map
mf_map(world, border = "white", 
       col = "gray90", lwd = 2,
       add = TRUE)

# Plot mental map
mf_map(BDG_plot, col = "#85c1d350", 
       border = "#85c1d3", lwd = 2,
       add = TRUE)

# Add other key elements to the map
mf_layout(title = paste0(BDG_plot$X0001_met_respID_aut, " - ", BDG_plot$ID_geom),
          credits = "Sources : ANR - DFG IMAGEUN (2020-2024) - Students Database", 
          arrow = TRUE,
          scale = TRUE, 
          frame = TRUE)

# Country of residency
mtext(paste0("Country : ", BDG_plot$A0405_cad_univer_lab_country), 
      side = 3, adj = 0, line = -0.5)

# Word associated with geometry
mtext(text = paste0("Word : ", BDG_plot$E1903_map_rgname), 
      side = 3, adj = 0, line = -1.5)

# Traduction od the word associated with geometry
mtext(text = paste0("trad : ", BDG_plot$E1903_map_rgname_lab_en), 
      side = 3, adj = 0, line = -2.5, font = 3)

 

The process is similar for displaying multiple geometries on the same map. The mapview library can also be used to quickly display geometries interactively.

First, we select all the geometries drawn by the same respondent. Then, for better map legibility (and interactivity), we order the geometries according to their surface area.

# Select geometries of respondent "9kb8mxs7zfp6"
BDG_plot <- BDG_carto[BDG_carto$X0001_met_respID_aut %in% "9kb8mxs7zfp6", ]

# Order geometries from largest to smallest
BDG_plot <- BDG_plot[order(BDG_plot$area, decreasing = TRUE), ]
mf_init(BDG_plot)

mf_map(world, border = "white",    
       col = "gray90", lwd = 2,
       add = TRUE)

mf_map(BDG_plot, col = "#85c1d350", 
       border = "#85c1d3", lwd = 2,
       add = TRUE)

mf_layout(title = unique(BDG_plot$X0001_met_respID_aut),
          credits = "Sources : ANR - DFG IMAGEUN (2020-2024) - Students Database", 
          arrow = TRUE,
          scale = TRUE, 
          frame = TRUE)

mtext(paste0("Country : ", BDG_plot$A0405_cad_univer_lab_country), 
      side = 3, adj = 0, line = -0.5)

mtext(paste0(BDG_plot$ID_geom, ": ", 
             BDG_plot$E1903_map_rgname, 
             collapse = "\n"), 
      side = 3, adj = 0, line = -4, cex = .8)

library(mapview)

mapview(BDG_plot, zcol = "E1903_map_rgname", 
        layer.name = paste0(BDG_plot$X0001_met_respID_aut[1], " (",  
                            BDG_plot$A0405_cad_univer_lab_country[1], ") "))

Construction of regular grids

The areas drawn by respondents can easily be aggregated (or summed) to produce maps that “summarize” the imaginaries of a group of respondents.

This section describes how to create these aggregated cartographic representations.

Two types of representation are shawn

  1. Addition (cumulation) of drawn areas.
  2. Addition (cumulation) of drawn area limits

In both cases, the “aggregation” method is the same: surfaces (polygons) or perimeters (polylines) intersect with a grid. The result is a regular grid in which the value of each tile equals the number of intersections detected. This provides continuous information on the areas included or excluded by the mental maps drawn.

We construct two types of grid.

  • A classic vector regular grid (tiles)**, intersected by zone boundaries (polylines).
  • A regular vector grid of points**, intersected by surfaces (polygons).

The st_make_grid() function in the sf package is used to construct a regular vector grid. In this case, we are building a 100 square kilometers grid.

# Vector grid creation - sfc object
gridPolyg <- st_make_grid(x = world, 
                     cellsize = 100000, 
                     square = TRUE, 
                     what = "polygons")

# Add an attribute (identifier) to the sfc object (= sf object)
gridPolyg <- st_sf(ID = 1:length(gridPolyg ), geom = gridPolyg )

Here is an extract of the vector grid, on a reduced area (Turkey):

The st_make_grid() function can also be used to construct a regular grid of points.

# Vector grid creation - sfc object
gridCenters <- st_make_grid(x = world, 
                     cellsize = 100000,        
                     square = TRUE, 
                     what = "centers")

# Add an attribute (identifier) to the sfc object (= sf object)
gridCenters <- st_sf(ID = 1:length(gridCenters), geom = gridCenters)

Here is an extract of the vector grid, on a reduced area (Turkey):


Grids and Mental Maps

Before making the geometric intersections, the first step is to select a corpus of mental maps. We start by extracting all the drawn areas specific to the map

BDG_carto <- BDG_merged[BDG_merged$geom_carto == TRUE,]

For this demonstration, we then select all the geometries drawn by the students surveyed in Turkey.

BDG_sub <- BDG_carto[BDG_carto$A0405_cad_univer_lab_country %in% "Turkey", ]

We then intersect the set of selected polygons with the grids using the st_intersects() function. The number of intersections detected for each grid tile is stored in the “count” attribute using the lengths() function.

We detect the intersections of the drawn polygons with the regular point grid using the st_intersects() function.

# Grid - polygon intersection calculation
result_intersection <- st_intersects(x = gridCenters, 
                                     y = BDG_sub, 
                                     sparse = TRUE) 

# Add the number of detected intersections in the "count" attribute
gridCenters$count <- lengths(result_intersection) 

We then calculate the proportion of intersection of each point with respect to the set of polygons drawn.

gridCenters$pct <- gridCenters$count / nrow(BDG_sub) * 100

All that remains is to replace the values “0” with “NA”.

gridCenters$pct[gridCenters$pct == 0] <- NA

First, we transform the drawn polygons into polylines. This allows only the contour of the demarcated areas to be taken into account and not their surface. To do this, we use the function st_cast().

BDG_sub_2 <- st_cast(BDG_sub, to = "MULTILINESTRING")

We detect the intersections of the drawn contours (polylines) with the grid using the st_intersects() function.

# Calculation of grid - polyline intersections
result_inter_polyligne <- st_intersects(x = gridPolyg, 
                                        y = BDG_sub_2, 
                                        sparse = TRUE) 

# Add the number of detected intersections in the "count" attribute
gridPolyg$count <- lengths(result_inter_polyligne) 

We then calculate the percentage of intersection of each tile with respect to the set of polylines drawn.

gridPolyg$pct <- gridPolyg$count / nrow(BDG_sub_2) * 100

Then we replace the values “0” with “NA”.

gridPolyg$pct[gridPolyg$pct == 0] <- NA

Finally, we extract the centroids of the tiles from the vector grid. This allows you to retrieve a regular grid of points.

gridPolyg_pts <- st_centroid(gridPolyg)


Creating Isobands

From the regular grids of points and their count attribute which contains the number of intersections detected for each point of the grid, we can construct polygon contours that group the points according to the value of this attribute (isobands).

The construction of isobands is based on discretization in order to determine classes. In this example, we opt for a discretization by equal amplitudes, but a more in-depth study on the statistical distribution can be carried out to choose a more adequate discretization.

To calculate class bounds, we use the mf_get_break() of the mapsf package. The construction of the isobands is then carried out with the mapiso() function of the package of the same name.

library(mapsf)

# discretization by equal amplitudes
discr <- mf_get_breaks(gridCenters$pct, breaks = "equal")

Isobands construction :

library(mapiso)

iso_surface <- mapiso(x = gridCenters,
                      var = "pct", 
                      breaks = discr)

Plot of the result (sf object, polygons):

mf_map(iso_surface)

library(mapsf)

# discretization by equal amplitudes
discr2 <- mf_get_breaks(gridPolyg_pts$pct, breaks = "equal")

Isobands construction

library(mapiso)

iso_limites <- mapiso(x = gridPolyg_pts,
               var = "pct", 
               breaks = discr2)

Plot of the result (sf object, polygons):

mf_map(iso_limites)


Cartography of the results

First, we define the geographical bounding box of the area of the world that we want to represent on the map. For this, we use the st_bbox() function.

bbox <- st_bbox(c(xmin = -4200000, 
                  ymin = 2500000,
                  xmax = 8450000, 
                  ymax = 10000000), 
                crs = st_crs(3857))


# Transformation of the bbox into an sfc object
bbox <-  st_as_sfc(bbox) 

The locator() function allows you to retrieve geographical coordinates interactively in the graphics window. In our example, we use it to place the text element that informs about the number of students and mapped geometries. To do this, display a basemap and then run the locator() function. All you have to do is click in the graphics window and the coordinates of the selected point are returned to the console.

mf_map(world)

# One click  (n = 1)
locator(n = 1)

All that remains is to map the regions drawn by the Turkish students.

# Initializing the bounding box
mf_map(bbox, col = NA, border = NA)

# Add base map
mf_map(world, 
       border = "lightblue",
       col = "gray90",
       lwd = 0.5,
       add = TRUE)

# Cartography of the intersections
mf_map(x = iso_surface, 
       var = "isomin", 
       type = "choro", 
       breaks = discr, 
       border = NA, 
       pal = "Rocket",
       alpha = 0.6, 
       leg_pos = "left", 
       leg_title = "Prevalence rate (%) of\nsurface drawings",
       leg_val_rnd = 1,
       add = TRUE)

# Overlaying the countries
mf_map(world, 
       border = "lightblue",
       col = NA,
       lwd = 0.2,
       add = TRUE)

# Plot the additionnal text
text(x = 7100000, 
     y = 9986523,
     cex = .8, 
     labels = paste0(length(unique(BDG_sub$X0001_met_respID_aut)), " respondents",
                     "\n", nrow(BDG_sub), " draws"))

# Finale key elements
mf_layout(
  title = "Turkish students' sense of belonging (2022)",
  credits = "Authors : Elina Marveaux, Hugues Pecout\nSources : ANR - DFG IMAGEUN (2020-2024) - Students Database", 
  arrow = FALSE,
  scale = FALSE, 
  frame = TRUE)

mf_arrow(pos = "topright")

# Initializing the bounding box
mf_map(bbox, col = NA, border = NA)

# Add base map
mf_map(world, 
       border = "lightblue",
       col = "gray90",
       lwd = 0.5,
       add = TRUE)

# Cartograpy of the intersections
mf_map(x = iso_limites, 
       var = "isomin", 
       type = "choro", 
       breaks = discr2, 
       border = NA, 
       pal = "Rocket",
       alpha = 0.6, 
       leg_pos = "left", 
       leg_title = "Prevalence rate (%) of\ndelimitation drawings",
       leg_val_rnd = 1,
       add = TRUE)

# Overlaying the countries 
mf_map(world, 
       border = "lightblue",
       col = NA,
       lwd = 0.2,
       add = TRUE)

# Plot the additionnal text
text(x = 7100000, 
     y = 9986523,
     cex = .8, 
     labels = paste0(length(unique(BDG_sub$X0001_met_respID_aut)), " respondents",
                     "\n", nrow(BDG_sub), " draws"))

# Add final key elements
mf_layout(
  title = "Turkish students' sense of belonging (2022)",
  credits = "Authors : Elina Marveaux, Hugues Pecout\nSources : ANR - DFG IMAGEUN (2020-2024) - Students Database", 
  arrow = FALSE,
  scale = FALSE, 
  frame = TRUE)

mf_arrow(pos = "topright")

Session informations

R version 4.5.3 (2026-03-11 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26200)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=French_France.utf8  LC_CTYPE=French_France.utf8   
[3] LC_MONETARY=French_France.utf8 LC_NUMERIC=C                  
[5] LC_TIME=French_France.utf8    

time zone: Europe/Paris
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggmosaic_0.4.0      rnaturalearth_1.2.0 ggplot2_4.0.3.9000 
 [4] mapview_2.11.4      mapsf_1.2.0         mapiso_0.3.0       
 [7] terra_1.9-11        sf_1.1-0            readODS_2.3.5      
[10] readxl_1.4.5        dplyr_1.2.1         kableExtra_1.4.0   

loaded via a namespace (and not attached):
 [1] tidyselect_1.2.1        viridisLite_0.4.3       farver_2.1.2           
 [4] S7_0.2.2                fastmap_1.2.0           lazyeval_0.2.3         
 [7] leaflet_2.2.3           digest_0.6.39           lifecycle_1.0.5        
[10] magrittr_2.0.5          compiler_4.5.3          rlang_1.2.0            
[13] tools_4.5.3             leafpop_0.1.0           yaml_2.3.12            
[16] data.table_1.18.2.1     knitr_1.51              brew_1.0-10            
[19] htmlwidgets_1.6.4       sp_2.2-1                classInt_0.4-11        
[22] plyr_1.8.9              xml2_1.5.2              RColorBrewer_1.1-3     
[25] KernSmooth_2.23-26      minty_0.0.6             withr_3.0.2            
[28] purrr_1.2.2             productplots_0.1.2      grid_4.5.3             
[31] stats4_4.5.3            e1071_1.7-17            leafem_0.2.5           
[34] scales_1.4.0            isoband_0.3.0           cli_3.6.6              
[37] rmarkdown_2.31          generics_0.1.4          otel_0.2.0             
[40] rstudioapi_0.18.0       httr_1.4.8              tzdb_0.5.0             
[43] DBI_1.3.0               proxy_0.4-29            stringr_1.6.0          
[46] s2_1.1.9                cellranger_1.1.0        base64enc_0.1-6        
[49] vctrs_0.7.3             jsonlite_2.0.0          ggrepel_0.9.8          
[52] systemfonts_1.3.2       crosstalk_1.2.2         maplegend_0.6.3        
[55] plotly_4.12.0           jquerylib_0.1.4         tidyr_1.3.2            
[58] units_1.0-1             glue_1.8.1              leaflet.providers_3.0.0
[61] codetools_0.2-20        stringi_1.8.7           gtable_0.3.6           
[64] raster_3.6-32           tibble_3.3.1            pillar_1.11.1          
[67] htmltools_0.5.9         satellite_1.0.6         R6_2.6.1               
[70] wk_0.9.5                textshaping_1.0.5       evaluate_1.0.5         
[73] lattice_0.22-9          png_0.1-9               class_7.3-23           
[76] uuid_1.2-2              Rcpp_1.1.1-1.1          zip_2.3.3              
[79] svglite_2.2.2           xfun_0.57               pkgconfig_2.0.3