The 2019/2020 Australian bushfires had a devastating impact on the natural landscape, threatening our native biodiversity. More than ever, decision makers need curated, open access biodiversity data to help respond effectively to future bushfires.
Our team at the Atlas of Living Australia (ALA) has been working with Invertebrates Australia and CSIRO National Research Collections team to collate biodiversity datasets that can be used for off-the-shelf bushfire assessments. The two datasets contain data on Australian taxonomic groups that are often overlooked and severely affected during bushfires: invertebrates (insects, molluscs, spiders) and vascular plants.
We are thrilled to announce that these datasets are available from CSIRO’s data access portal!
This post expands on our last post about using convex and alpha hulls to visualise distributions of data-deficient species.
Here, we show you how to compute a new form of spatial polygon — a concave hull — and use it to represent a species’ range and to calculate the overlap with fire-burned areas (range overlap). Unlike convex hulls, concave hulls have the added flexibility to adjust their tightness to the data (or concavity). This flexibility allows more accurate estimation of species ranges, making it a useful approach to rapidly assess how natural disasters like bush fires affected biodiversity.
Download data
First we will load the R packages we need:
# install.packages("pacman")
::p_load(tidyverse, here, rmapshaper, sf, ggpointdensity, viridis, ozmaps, concaveman, cowplot, patchwork) pacman
Next, we will go to the Data Access Portal to download the invertebrate and vascular plant datasets.
Click on the Files tab (under the main title), then the Download button (in the top right corner), and select Download all files as Zip archive. Save this zip file in a local folder inside your current R project and be sure to unzip it.
Now we can read the curated datasets into R:
# Invertebrates data
<- read_csv(here("your_directory_name", "invertebrate.data.03.2023.csv"))
inverts
# Vascular plants data
<- read_csv(here("your_directory_name", "vascularplant.data.03.2023.csv")) |>
vplants rename(latitude = latitude_used, # Rename coordinate variables for consistency
longitude = longitude_used)
Overview of data
Both datasets are based on studies that investigated the impact of the Black Summer bushfires and are designed to support future modelling and impact assessments. The invertebrate dataset spans across Australia, whereas the vascular plant dataset is restricted to South-Eastern Australia and contains only species where more than 50% of their range was affected by the 2019/2020 fires.
Summary of Invertebrate and Vascular Plant Data
Taxonomic Group | Classes | Families | Species | Records |
---|---|---|---|---|
Invertebrates | 46 | 2,044 | 44,146 | 300,987 |
Vascular Plants | 7 | 76 | 896 | 41,572 |
A total of 44,146 invertebrate species and 896 species of vascular plants are represented in these datasets. Below, we’ve attempted to show the geographic range of these data. So much data makes it challenging to visualise all data points and concave hulls at once. As such, we created the concave hull maps (left) below by randomly selecting one invertebrate species from each class and one plant species from each family.
Code
# Identify species that have more than 4 observations
<- inverts |>
more_than_4_obs group_by(scientific_name) |>
summarise(n_obs = n()) |>
filter(n_obs > 4) |>
pull(scientific_name)
# Subset species with more than 4 observations and appear on mainland Australia + Tasmania
<- inverts |>
inverts_subset filter(scientific_name %in% more_than_4_obs) |>
filter(latitude < -10, latitude >= -45,
>= 113, longitude <= 155) |>
longitude select(scientific_name:family, longitude, latitude)
# Nest occurrence data
<- inverts_subset |>
inverts_nest nest(coords = c(longitude, latitude))
# Subset a random species from each class
set.seed(123) # Set seed so we all get the same results
<- inverts_nest |>
subset group_by(class) |>
slice_sample(n = 1)
# Convert coordinates into sf object and compute concave hulls as list columns.
<- subset |>
subset_concave mutate(points_sf = map(.x = coords,
~ st_as_sf(.x, coords = c("longitude", "latitude"),
crs = 4326)),
concave_sf = map(points_sf,
~ concaveman(.x)))
# Unnest the concave hull list column
<- subset_concave |>
subset_concave select(scientific_name:family, concave_sf) |>
unnest(cols = c(concave_sf)) |>
ungroup() |>
st_sf(crs = 4326)
# Retrieve Australia polygon
<- st_transform(ozmap_country, 4326)
aus
# Plotting spatial distributions
<- ggplot() +
inverts_concave geom_sf(data = aus, colour = "black", fill = NA) +
geom_sf(data = subset_concave, fill = "#609966", alpha = 0.2, lwd = 0) +
coord_sf(xlim = c(110, 155)) +
theme_void()
# Create plot showing overlapping points
<- ggplot() +
inverts_points_map geom_pointdensity(data = inverts_subset,
mapping = aes(x = longitude,
y = latitude)) +
geom_sf(data = aus, colour = "white", fill = NA) +
scale_color_viridis(option = "E", begin = 0.1) +
coord_sf(xlim = c(110, 155)) +
guides(alpha = "none",
colour = guide_colorbar(title = "Number of \noverlapping points")) +
theme_void() +
theme(legend.position = "bottom",
legend.margin = margin(0, 0, 0, 0),
legend.box.margin = margin(0, 0, 0, 0),
legend.justification = "left"
)
+ inverts_points_map + plot_annotation(title = "Invertebrate Dataset") inverts_concave
Code
# Identify species that have more than 4 observations
<- vplants |>
more_than_4_obs_plants group_by(scientific_name) |>
summarise(n_obs = n()) |>
filter(n_obs > 4) |>
pull(scientific_name)
# Subset species with more than 4 observations and appear on mainland Australia + Tasmaina
<- vplants |>
vplant_subset filter(scientific_name %in% more_than_4_obs_plants) |>
filter(latitude < -10, latitude >= -45,
>= 113, longitude <= 155) |>
longitude select(species, class:genus, longitude, latitude)
# Nest occurrence data
<- vplant_subset |>
vplant_nest nest(coords = c(longitude, latitude))
# Subset a random species from each family
set.seed(123) # Set seed so we all get the same results
<- vplant_nest |>
plant_subset group_by(family) |>
slice_sample(n = 1)
# Compute concave hulls
<- plant_subset |>
pl_subset_concave mutate(points_sf = map(.x = coords,
~ st_as_sf(.x, coords = c("longitude", "latitude"),
crs = 4326)),
concave_sf = map(points_sf,
~ concaveman(.x)))
# Unnest the data
<- pl_subset_concave |>
pl_subset_concave select(species:family, concave_sf) |>
unnest(cols = c(concave_sf)) |>
st_as_sf(crs = 4326)
# Plotting spatial distributions
<- ggplot() +
plant_concave geom_sf(data = aus, colour = "black", fill = NA) +
geom_sf(data = pl_subset_concave, fill = "#609966", colour = NA, alpha = 0.15, lwd = 0) +
coord_sf(xlim = c(140, 158),
ylim = c(-23, -43)) +
theme_void()
# Create plot showing overlapping points
<- ggplot() +
plants_points_map geom_pointdensity(data = vplant_subset,
mapping = aes(x = longitude,
y = latitude)) +
geom_sf(data = aus, colour = "black", fill = NA) +
scale_color_viridis(option = "E", begin = 0.1) +
coord_sf(xlim = c(140, 158),
ylim = c(-23, -43)) +
guides(alpha = "none",
colour = guide_colorbar(title = "Number of \noverlapping points")) +
theme_void() +
theme(legend.position = "bottom",
legend.margin = margin(0, 0, 0, 0),
legend.box.margin = margin(0, 0, 0, 0),
legend.justification = "left"
)
+ plants_points_map + plot_annotation(title = "Vascular Plant Dataset") plant_concave
Pre-cleaning
Let’s use these datasets to calculate concave hulls and range overlaps with burned regions. One benefit of using these curated datasets is that they do not contain any duplicates or missing values. This makes data cleaning an easier job!
However, there are still a few steps we need to do before computing concave hulls:
Remove data-deficient species
First, we need to filter out any data-deficient species with fewer than 4 data points because concave hulls are best estimated with at least 4 data points. To do this, we’ll calculate the number of observations for each species, then identify which ones have more than 4 records. Using this list of species, we can extract their data.
<- inverts |>
more_than_4_obs group_by(scientific_name) |>
summarise(n_obs = n()) |>
filter(n_obs > 4) |>
pull(scientific_name)
|> head() more_than_4_obs
[1] "Aaaaba fossicollis" "Aaaaba nodosus" "Aades cultratus"
[4] "Aades griseatus" "Aaroniella rawlingsi" "Abantiades latipennis"
<- inverts |>
inverts_subset filter(scientific_name %in% more_than_4_obs)
Restrict data to mainland Australia and Tasmania
The invertebrate dataset includes records on offshore islands which can drastically skew the shape of a species’ concave hull. For the purpose of this post, we will filter these out and only use records that occur on mainland Australia and Tasmania.
<- inverts_subset |>
subset_mainland filter(latitude < -10, latitude >= -45,
>= 113, longitude <= 155) |>
longitude select(scientific_name:family, longitude, latitude)
List columns and nesting occurrence data
For the majority of calculations in this post, we will be making use of list columns, a very useful data structure for iterative analyses. You can think of a list column as mini data frames nested within a column by a grouping variable.
In this case we are nesting the coordinate data by species, which will make operations at the species level more efficient.
<- subset_mainland |>
inverts_nest nest(coords = c(longitude, latitude))
|>
inverts_nest print(n = 6)
# A tibble: 16,347 × 4
scientific_name class family coords
<chr> <chr> <chr> <list>
1 Aaaaba fossicollis Insecta Buprestidae <tibble [12 × 2]>
2 Aaaaba nodosus Insecta Buprestidae <tibble [16 × 2]>
3 Aades cultratus Insecta Curculionidae <tibble [20 × 2]>
4 Aades griseatus Insecta Curculionidae <tibble [5 × 2]>
5 Aaroniella rawlingsi Insecta Philotarsidae <tibble [35 × 2]>
6 Abantiades latipennis Insecta Hepialidae <tibble [10 × 2]>
# ℹ 16,341 more rows
You can inspect elements in the list column like this:
|>
inverts_nest pluck("coords", 1) |> # 1 refers to the first element of the list column
print(n = 6)
# A tibble: 12 × 2
longitude latitude
<dbl> <dbl>
1 153. -28.4
2 151. -33.8
3 153. -31.0
4 146. -37.8
5 153. -30.3
6 151. -33.8
# ℹ 6 more rows
The biggest change with working with list columns is that you have to iterate across each element. To do this, we will use various functions from the {purrr}
package for the next calculation steps.
Species range overlap with fire-burned areas
Get fire layer
Shapefiles for the 2019-2020 fire season are available through the National Indicative Aggregated Fire Extent Dataset from the Department of Climate Change, Energy, the Environment and Water.
Click on Download Data (near the top of the page), then click on NIAFED_v20200623.zip to download the zip file. Save the zip file in your project folder and unzip to retrieve the shapefiles.
Now we can read the shapefile into R and set the projection to EPSG:4326. To speed up the computation of concave hulls and overlaps, we will remove elevation values and simplify the edges of the fire POLYGON
.
<- st_read(here("your_directory_name", "NIAFED_20190701_20200622_v20200623.shp")) |>
fire st_transform(crs = 4326) |>
st_zm() |> # Remove Z or M values
ms_simplify() # Simplify edges of the fire layer
Choose taxonomic group
While it is possible to calculate range overlap with burned areas for all species in the dataset, it can take a lot of memory and processing time. Instead, we will demonstrate our workflow with — the bee family (Apidae) — as a working example.
# Filter invertebrate data to Apidae
<- inverts_nest |>
bees filter(family == "Apidae")
Compute concave hull
In the next steps, we will work through the coordinate data for each species iteratively using map
.
We will transform each species’ coordinates into an sf
object using st_as_sf()
, then compute the concave hulls with the concaveman()
function. You can adjust the tightness of the hull boundary around a set of points using the concavity
argument - the smaller the value, the tighter the hull. We’ve wrapped mutate()
around these steps so the output will become variables in our tibble
.
<- bees |>
bees_concave mutate(points_sf = map(.x = coords,
~ st_as_sf(.x, coords = c("longitude", "latitude"), # Set as sf object
crs = 4326) |>
rename(points = geometry)), # Rename geometry variable to something intuitive
concave_sf = map(points_sf,
~ concaveman(.x, concavity = 2) |> # Compute concave hulls
rename(concave = polygons)) # Rename geometry variable to something intuitive
)
|> print(n = 6) bees_concave
# A tibble: 54 × 6
scientific_name class family coords points_sf concave_sf
<chr> <chr> <chr> <list> <list> <list>
1 Amegilla (Asaropoda) bombiformis Insecta Apidae <tibble> <sf> <sf>
2 Amegilla (Asaropoda) calva Insecta Apidae <tibble> <sf> <sf>
3 Amegilla (Asaropoda) dawsoni Insecta Apidae <tibble> <sf> <sf>
4 Amegilla (Asaropoda) paracalva Insecta Apidae <tibble> <sf> <sf>
5 Amegilla (Asaropoda) rhodoscymna Insecta Apidae <tibble> <sf> <sf>
6 Amegilla (Notomegilla) aeruginosa Insecta Apidae <tibble> <sf> <sf>
# ℹ 48 more rows
Compute range overlap and descriptive statistics
To compute range overlaps, we need to set our geometry calculations to assume the Earth is flat and not spherical by setting sf_use_s2(FALSE)
. This may be a limitation of the method but it still gives us a good approximation.
# Disable spherical geometry
sf_use_s2(FALSE)
Using st_intersection()
, we can identify the overlap between each species’ concave hull with fire-burned areas. We can then use st_area()
to calculate the area (m2) of overlap and convert it into a percentage of each species’ original range so that all species are comparable.
Using possibly()
with our map()
functions allows the calculations to return NA
for species that did not overlap with burned areas. Once calculations are complete, we will un-nest the variables: overlap_area
and percent_overlap
, so they appear as regular columns in our tibble
.
# Calculate range overlap
<- bees_concave |>
bees_overlap mutate(
overlap_sf = map(concave_sf,
possibly(~ st_intersection(fire, .x) |> select(-Id) |> rename(overlap = geometry))), # Identify overlap
overlap_area = map(overlap_sf,
possibly( ~ st_area(.x))), # Calculate area
percent_overlap = map2(.x = overlap_area,
.y = concave_sf,
possibly( ~ (.x / st_area(.y)) * 100))) |> # Calculate percentage
unnest(cols = c(overlap_area, percent_overlap)) # Unnest the area and percentage columns
|> print(n = 6) bees_overlap
# A tibble: 23 × 9
scientific_name class family coords points_sf concave_sf overlap_sf
<chr> <chr> <chr> <list> <list> <list> <list>
1 Amegilla (Asaropoda) bo… Inse… Apidae <tibble> <sf> <sf> <sf>
2 Amegilla (Asaropoda) da… Inse… Apidae <tibble> <sf> <sf> <sf>
3 Amegilla (Asaropoda) pa… Inse… Apidae <tibble> <sf> <sf> <sf>
4 Amegilla (Zonamegilla) … Inse… Apidae <tibble> <sf> <sf> <sf>
5 Austroplebeia cassiae Inse… Apidae <tibble> <sf> <sf> <sf>
6 Braunsapis clarissima Inse… Apidae <tibble> <sf> <sf> <sf>
# ℹ 17 more rows
# ℹ 2 more variables: overlap_area [m^2], percent_overlap [1]
Rank species by fire impact
Next, we will take the top 3 species with the highest percentage range overlap with fire-burned areas (percent_overlap
) for our data visualisation. The top 3 species include a stingless bee, a reed bee, and a carpenter bee.
<- bees_overlap |>
top3 slice_max(order_by = percent_overlap,
n = 3)
top3
# A tibble: 3 × 9
scientific_name class family coords points_sf concave_sf overlap_sf
<chr> <chr> <chr> <list> <list> <list> <list>
1 Austroplebeia cassiae Inse… Apidae <tibble> <sf> <sf> <sf>
2 Exoneura (Exoneura) hac… Inse… Apidae <tibble> <sf> <sf> <sf>
3 Xylocopa (Koptortosoma)… Inse… Apidae <tibble> <sf> <sf> <sf>
# ℹ 2 more variables: overlap_area [m^2], percent_overlap [1]
Make map
We will now select the variables we need and un-nest the relevant ones (points_sf
, concave_sf
, overlap_sf
) for plotting - this gives us everything we need to create our maps!
<- top3 |>
bee_map_data select(scientific_name, points_sf, concave_sf, overlap_sf) |>
unnest(cols = c(points_sf, concave_sf, overlap_sf))
|> print(n = 6) bee_map_data
# A tibble: 31 × 4
scientific_name points concave
<chr> <POINT [°]> <POLYGON [°]>
1 Austroplebeia cassiae (143.45 -14.08) ((143.27 -14.08, 143.28 -14.05, 142.92 …
2 Austroplebeia cassiae (143.27 -14.08) ((143.27 -14.08, 143.28 -14.05, 142.92 …
3 Austroplebeia cassiae (143.32 -14.1) ((143.27 -14.08, 143.28 -14.05, 142.92 …
4 Austroplebeia cassiae (145.13 -15.67) ((143.27 -14.08, 143.28 -14.05, 142.92 …
5 Austroplebeia cassiae (143.28 -14.05) ((143.27 -14.08, 143.28 -14.05, 142.92 …
6 Austroplebeia cassiae (142.92 -13.42) ((143.27 -14.08, 143.28 -14.05, 142.92 …
# ℹ 25 more rows
# ℹ 1 more variable: overlap <MULTIPOLYGON [°]>
Create the base map
Let’s create our base map with the outline of Australia and the fire-burned area. You can see a majority of burnt areas are located in Northern Australia and the South-East coast.
# Retrieve Australia polygon
<- st_transform(ozmap_country, 4326)
aus
<- ggplot() +
base_map geom_sf(data = aus, colour = "black", fill = "white") +
geom_sf(data = fire, fill = "#FEC3A6", colour = "#FEC3A6") +
theme_void()
Add species range overlap
Now we can add the range overlap of our three bee species. We use geometry
within aes
to specify which variable from bee_map_data
we want to plot.
<- base_map +
main_map geom_sf(data = bee_map_data,
aes(geometry = concave,
colour = scientific_name,
fill = scientific_name),
size = 1.5, alpha = 0.1) +
geom_sf(data = bee_map_data,
aes(geometry = overlap),
colour = "#FF925C", fill = "#FF925C") + # To highlight area of overlap
geom_sf(data = bee_map_data,
aes(geometry = points,
colour = scientific_name),
size = 1) +
scale_colour_manual(values = c("#023E50", "#7B8A6A", "#3C908E")) +
scale_fill_manual(values = c("#023E50", "#7B8A6A", "#3C908E")) +
guides(colour = guide_legend(override.aes = list(alpha = 1))) +
coord_sf(xlim = c(128, 158)) +
theme(legend.title= element_blank(),
legend.position = "bottom")
Create inset map
The navy blue hull in the top right corner of Australia is very small (Austroplebeia cassiae), so we will make an enlarged inset map so we can see it clearer.
<- main_map +
inset coord_sf(
xlim = c(142.8 , 145.3),
ylim = c(-15.9, -13.25),
expand = FALSE
+
) theme_void() +
theme(legend.position = "none",
panel.border = element_rect(colour = "black", fill = NA, linewidth = 0.2))
We’ll also draw a box around the area of interest in the enlarged map.
<- main_map +
main_bbox geom_rect(aes(xmin = 142.8, xmax = 145.3,
ymin = -15.9, ymax = -13.25),
colour = "black",
fill = NA,
lwd = 0.2)
Arrange map components
Finally, we can arrange our base map and inset together for our final map!
This map shows the three bee species with the highest percentage overlap with fire-burned areas. Two of these bee species are located in Northern Australia and one is located in South-Eastern Australia.
<- ggdraw(main_bbox) +
combined_map draw_plot(inset,
x = 0.52, y = 0.63,
width =0.45, height = 0.3)
combined_map
Bonus: Vascular plants
We repeated the same workflow with the vascular plant dataset and created a map of range overlap with burned areas for the genus Daviesia.
Commonly known as bitterpeas, Daviesia comprises plants pollinated by reed bees (Exoneura), which are featured in the bee map above.
Code
# Extract candidate genus
<- vplant_nest |>
daviesia filter(genus == "Daviesia")
# Compute concave hulls
<- daviesia |>
daviesia_concave mutate(points_sf = map(.x = coords,
~ st_as_sf(.x, coords = c("longitude", "latitude"),
crs = 4326)),
concave_sf = map(points_sf,
~ concaveman(.x)))
# Compute range overlap and descriptive statistics and select
<- daviesia_concave |>
daviesia_overlap mutate(overlap_sf = map(concave_sf,
possibly(~ st_intersection(fire, .x) |> select(-Id))),
overlap_area = map(overlap_sf,
possibly(~ st_area(.x))),
percent_overlap = map2(.x = overlap_area,
.y = concave_sf,
possibly(~ (.x / st_area(.y))*100))) |>
unnest(cols = c(overlap_area, percent_overlap))
## Prepare for plotting and rename variables
<- daviesia_overlap |>
daviesia_map_data select(species, overlap_area, percent_overlap, points_sf, concave_sf, overlap_sf) |>
unnest() |>
rename(points = geometry,
concave = polygons,
overlap = geometry1)
## Create main map reusing base_map from above
<- base_map +
daviesia_main_map geom_sf(data = daviesia_map_data,
aes(geometry = concave,
colour = species,
fill = species),
size = 1.5, alpha = 0.005) +
geom_sf(data = daviesia_map_data,
aes(geometry = overlap),
colour = "#FF925C", fill = "#FF925C") +
geom_sf(data = daviesia_map_data,
aes(geometry = points,
colour = species),
size = 0.9) +
scale_colour_manual(values = c("#023E50", "#7B8A6A", "#3C908E" )) +
scale_fill_manual(values = c("#023E50", "#7B8A6A", "#3C908E")) +
guides(colour = guide_legend(override.aes = list(alpha = 1))) +
coord_sf(xlim = c(140, 158),
ylim = c(-23, -43)) +
theme(legend.title= element_blank(),
legend.position = "bottom")
# Inset 1
<- daviesia_main_map +
daviesia_inset_1 coord_sf(
xlim = c(145.5 , 150.3),
ylim = c(-35, -37.95),
expand = FALSE
+
) theme_void() +
theme(legend.position = "none",
panel.border = element_rect(colour = "black", fill = NA, linewidth = 0.3))
# Inset 2
<- daviesia_main_map +
daviesia_inset_2 coord_sf(
xlim = c(151.55 , 152.75),
ylim = c(-28.6, -31.25),
expand = FALSE
+
) theme_void() +
theme(legend.position = "none",
panel.border = element_rect(colour = "black", fill = NA, linewidth = 0.3))
# Drawing the inset boxes on main map
<- daviesia_main_map +
daviesia_bbox geom_rect(aes(xmin = 145.5, xmax = 150.3, # Inset 1
ymin = -35, ymax = -37.95),
colour = "black",
fill = NA, linewidth = 0.2) +
geom_rect(aes(xmin = 151.55, xmax = 152.75, # Inset 2
ymin = -28.6, ymax = -31.25),
colour = "black",
fill = NA, linewidth = 0.2)
# Daviesia plot with insets
<- ggdraw(daviesia_bbox) +
daviesia_combined draw_plot(daviesia_inset_1, x = 0.59, y = 0.15,
width = 0.42, height = 0.30) +
draw_plot(daviesia_inset_2, x = 0.52, y = 0.52,
width = 0.5, height = 0.4)
daviesia_combined
Final thoughts
In natural catastrophes, decision makers have limited time to act. They need ready-to-go data and workflows to assess and manage possible consequences of the catastrophe and any proposed ways to mitigate it. Here, we used curated datasets of Australian invertebrates and vascular plants to illustrate how concave hulls can represent estimate species range and estimate range overlap with natural disasters. We hope our work can aid future assessments of vulnerable species and help prioritise conservation efforts.
Expand for session info
─ Session info ───────────────────────────────────────────────────────────────
setting value
version R version 4.3.2 (2023-10-31 ucrt)
os Windows 10 x64 (build 19045)
system x86_64, mingw32
ui RTerm
language (EN)
collate English_Australia.utf8
ctype English_Australia.utf8
tz Australia/Sydney
date 2024-02-12
pandoc 3.1.1 @ C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown)
─ Packages ───────────────────────────────────────────────────────────────────
package * version date (UTC) lib source
concaveman * 1.1.0 2020-05-11 [1] CRAN (R 4.3.2)
cowplot * 1.1.1 2020-12-30 [1] CRAN (R 4.3.2)
dplyr * 1.1.4 2023-11-17 [1] CRAN (R 4.3.2)
forcats * 1.0.0 2023-01-29 [1] CRAN (R 4.3.2)
ggplot2 * 3.4.4 2023-10-12 [1] CRAN (R 4.3.1)
ggpointdensity * 0.1.0 2019-08-28 [1] CRAN (R 4.3.2)
gt * 0.10.0 2023-10-07 [1] CRAN (R 4.3.2)
here * 1.0.1 2020-12-13 [1] CRAN (R 4.3.2)
htmltools * 0.5.7 2023-11-03 [1] CRAN (R 4.3.2)
lubridate * 1.9.3 2023-09-27 [1] CRAN (R 4.3.2)
ozmaps * 0.4.5 2021-08-03 [1] CRAN (R 4.3.2)
patchwork * 1.1.3 2023-08-14 [1] CRAN (R 4.3.1)
purrr * 1.0.2 2023-08-10 [1] CRAN (R 4.3.2)
readr * 2.1.4 2023-02-10 [1] CRAN (R 4.3.2)
rmapshaper * 0.5.0 2023-04-11 [1] CRAN (R 4.3.2)
sessioninfo * 1.2.2 2021-12-06 [1] CRAN (R 4.3.2)
sf * 1.0-14 2023-07-11 [1] CRAN (R 4.3.2)
stringr * 1.5.1 2023-11-14 [1] CRAN (R 4.3.2)
tibble * 3.2.1 2023-03-20 [1] CRAN (R 4.3.2)
tidyr * 1.3.0 2023-01-24 [1] CRAN (R 4.3.2)
tidyverse * 2.0.0 2023-02-22 [1] CRAN (R 4.3.2)
viridis * 0.6.4 2023-07-22 [1] CRAN (R 4.3.2)
viridisLite * 0.4.2 2023-05-02 [1] CRAN (R 4.3.1)
[1] C:/Users/KEL329/R-packages
[2] C:/Users/KEL329/AppData/Local/Programs/R/R-4.3.2/library
──────────────────────────────────────────────────────────────────────────────