Quantifying species range and overlap with fire-burned areas using concave hulls

Calculating range overlap is an efficient way to estimate the impact of natural disasters on biodiversity. Here we’ll use curated datasets to compute concave hulls to visualise the spatial distribution of Apidae (Bees) and Daviesia (Bitterpeas) and their overlap with burned areas of the Black Summer fires of 2019-2020.

Eukaryota
Animalia
Plantae
Summaries
Maps
R
Authors

Fonti Kar

Margot Schneider

Published

April 11, 2023

Author

Fonti Kar
Margot Schneider

Date

11 April 2023

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")
pacman::p_load(tidyverse, here, rmapshaper, sf, ggpointdensity, viridis, ozmaps, concaveman, cowplot, patchwork)

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
inverts <- read_csv(here("your_directory_name", "invertebrate.data.03.2023.csv"))

# Vascular plants data
vplants <- read_csv(here("your_directory_name", "vascularplant.data.03.2023.csv"))  |> 
  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 
more_than_4_obs <- inverts |> 
  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_subset <- inverts |>
  filter(scientific_name %in% more_than_4_obs) |> 
  filter(latitude < -10, latitude >= -45,
         longitude >= 113, longitude <= 155) |> 
  select(scientific_name:family, longitude, latitude)

# Nest occurrence data
inverts_nest <- inverts_subset |> 
  nest(coords = c(longitude, latitude))

# Subset a random species from each class 
set.seed(123)  # Set seed so we all get the same results
subset <- inverts_nest |> 
  group_by(class) |> 
  slice_sample(n = 1) 

# Convert coordinates into sf object and compute concave hulls as list columns.
subset_concave <- subset |>
    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
aus <- st_transform(ozmap_country, 4326)

# Plotting spatial distributions
inverts_concave <- ggplot() + 
  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
inverts_points_map <- ggplot() +
  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_concave + inverts_points_map + plot_annotation(title = "Invertebrate Dataset") 

Code
# Identify species that have more than 4 observations 
more_than_4_obs_plants <- vplants |> 
  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
vplant_subset <- vplants |>
  filter(scientific_name %in% more_than_4_obs_plants) |> 
  filter(latitude < -10, latitude >= -45,
         longitude >= 113, longitude <= 155) |> 
  select(species, class:genus, longitude, latitude) 

# Nest occurrence data
vplant_nest <- vplant_subset |> 
   nest(coords = c(longitude, latitude))

# Subset a random species from each family 
set.seed(123)  # Set seed so we all get the same results
plant_subset <- vplant_nest |> 
  group_by(family) |> 
  slice_sample(n = 1) 

# Compute concave hulls
pl_subset_concave <- plant_subset |>
    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
plant_concave <- ggplot() + 
  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
plants_points_map <- ggplot() +
  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"
        )
  
plant_concave + plants_points_map + plot_annotation(title = "Vascular Plant Dataset")

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.

more_than_4_obs <- inverts |> 
  group_by(scientific_name) |> 
  summarise(n_obs = n()) |> 
  filter(n_obs > 4) |> 
  pull(scientific_name)

more_than_4_obs |> head()
[1] "Aaaaba fossicollis"    "Aaaaba nodosus"        "Aades cultratus"      
[4] "Aades griseatus"       "Aaroniella rawlingsi"  "Abantiades latipennis"
inverts_subset <- inverts |>
  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.

subset_mainland <- inverts_subset |> 
  filter(latitude < -10, latitude >= -45,
         longitude >= 113, longitude <= 155) |> 
  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.

inverts_nest <- subset_mainland |> 
  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.

fire <- st_read(here("your_directory_name", "NIAFED_20190701_20200622_v20200623.shp")) |> 
  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
bees <- inverts_nest |> 
  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_concave <- bees |>
    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
           ) 

bees_concave |> print(n = 6)
# 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_overlap <- bees_concave |>
  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
    
bees_overlap |> print(n = 6)
# 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.

top3 <- bees_overlap |> 
  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!

bee_map_data <- top3 |> 
  select(scientific_name, points_sf, concave_sf, overlap_sf) |> 
  unnest(cols = c(points_sf, concave_sf, overlap_sf)) 

bee_map_data |> print(n = 6)
# 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
aus <- st_transform(ozmap_country, 4326)

base_map <- ggplot() + 
  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.

main_map <- base_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.

inset <- main_map + 
   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_bbox <- main_map + 
   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.

combined_map <- ggdraw(main_bbox) +
  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
daviesia <- vplant_nest |> 
  filter(genus == "Daviesia")

# Compute concave hulls
daviesia_concave <- daviesia |>
    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_overlap <- daviesia_concave |> 
  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_map_data <- daviesia_overlap |> 
  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
daviesia_main_map <- base_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_inset_1 <- daviesia_main_map +
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_inset_2 <- daviesia_main_map +
  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_bbox <- daviesia_main_map + 
   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 
daviesia_combined <- ggdraw(daviesia_bbox) +
  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.

Acknowledgement:

The work in this post is part of a project titled: Curated biodiversity data for rapid assessment of bushfire impact. This project is funded by the Australian Research Data Commons (ARDC) bushfire data challenges program.

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

──────────────────────────────────────────────────────────────────────────────