Maps are one of the most common and useful data visualisation tools in an ecologist’s tool belt. Making a quick and simple map of species observations is especially useful when first investigating where a species has occurred. Viewing locations of points can also help to understand the extent of your data (and spot possible errors or outliers).
In this post, we will use either Python or R to make a map of observations of Peron’s tree frog (Litoria peronii) in New South Wales since 2018 recorded by FrogID.
Download data
Peron’s Tree frog is one of the most recorded frog species in the Atlas of Living Australia. Growing up to 7cm in length, it is well-known for its eyes which often look like they have a black cross on them!
Left: Litoria peronii (Giverny CC-BY-NC 4.0 (Int), Middle: Litoria peronii (debtaylor142 CC-BY-NC 4.0 (Int)), Right: Litoria peronii (Ernst Weiher, iNaturalist CC-BY-NC 4.0 (Int))
First, let’s import galah-python.
import galahSearch for taxa
When trying to download data about any species or clade, we can search using search_taxa(). It’s recommended to use search_taxa() to check whether a taxonomic search returns what you were expecting (even if you know the scientific name)! We can check taxonomic information about Peron’s tree frog with search_taxa(), which returns extra details about the species when a match is found.
galah.search_taxa("Litoria peronii")    scientificName scientificNameAuthorship  ...     vernacularName   issues
0  Litoria peronii          (Tschudi, 1838)  ...  Peron's Tree Frog  noIssue
[1 rows x 12 columns]Search for fields
Next, we can search for fields and field IDs for filtering our query. In this case, we are interested in filtering to a specific year, state/territory and data resource.
We can start by searching for any fields containing the word “year” using search_all().
galah.search_all(
  fields="year"
  )                    id                                        description   type                                               link
0                 year  The year in which an occurrence was observed. ...  field  https://github.com/AtlasOfLivingAustralia/ala-...
1         endDayOfYear          http://rs.tdwg.org/dwc/terms/endDayOfYear  field                                                NaN
2        datePrecision  The precision of the date information for the ...  field                                                NaN
3       occurrenceYear  Year ranges for a search. Calculated based on ...  field                                                NaN
4       startDayOfYear        http://rs.tdwg.org/dwc/terms/startDayOfYear  field                                                NaN
5  namePublishedInYear   http://rs.tdwg.org/dwc/terms/namePublishedInYear  field                                                NaNWe can do the same kind of search to find fields with information of australian states/territories and data resource names.
galah.search_all(
  fields="states"
  )        id                                        description    type link
0     cl22  Australian States and Territories Australian S...  layers     
1    cl927  States including coastal waters States (includ...  layers     
2   cl2013  ASGS Australian States and Territories Austral...  layers     
3  cl10902  Forests of Australia (2013) v2.0 Forests of Au...  layers     
4  cl10903  Tenure of Australia's forests (2013) v2.0 Tenu...  layers     
5  cl10925     PSMA States (2016) Australian State boundaries  layers     
6  cl10930  NRM Regions 2017 The Natural Resource Manageme...  layers     galah.search_all(
  fields="dataresourcename"
  )                     id                                        description   type link
0       dataResourceUid  A list (concatenated and separated) of prepara...  field  NaN
1      dataResourceName  The data resource that supplies the record. Th...  field  NaN
2   raw_dataResourceUid                                                NaN  field  NaN
3  raw_dataResourceName                                                NaN  field  NaNIf you are ever uncertain which field ID to choose, you can use show_values() to see what possible values are within a field. For example, let’s see what values are in field ID cl22.
galah.show_values(field="cl22")   field                      category
0   cl22               New South Wales
1   cl22                      Victoria
2   cl22                    Queensland
3   cl22               South Australia
4   cl22             Western Australia
5   cl22            Northern Territory
6   cl22  Australian Capital Territory
7   cl22                      Tasmania
8   cl22                      Unknown1
9   cl22             Coral Sea Islands
10  cl22   Ashmore and Cartier IslandsWe can also search for a value using search_values(), which might be handy to check that “FrogID” is a value in dataResourceName.
galah.search_values(field="dataResourceName", value="frogid")              field category
0  dataResourceName   FrogIDDownload observations
Now we are ready to build our query to download observations of Peron’s tree frog in New South Wales since 2018 recorded by FrogID.
For those unfamiliar with Australian geography, New South Wales is this one:
First, let’s find the number of records that match our query. This is good practice before downloading occurrence records because you can check exactly how many records you are intending to download (and avoid an accidental massive download)!
We’ll use atlas_counts() to download record counts, specifying the taxon using the taxa argument, and narrowing the year range, state/territory and data resource using the filters argument.
galah.atlas_counts(                      # *Download record counts*
    taxa="litoria peronii",              # *of Peron's tree frog*
    filters=["year>=2018",               # *since 2018*
             "cl22=New South Wales",     # *in New South Wales*
             "dataResourceName=FrogID"]  # *by FrogID*
)   totalRecords
0         27647Now we can use atlas_occurrences() to download occurrence records, which returns each observation’s location coordinates and event date.
You will need to first provide a registered email with the ALA using galah_config() before retrieving records.
galah.galah_config(email = "your-email-here")
frogs = galah.atlas_occurrences(
    taxa="litoria peronii",
    filters=["year>=2018",
             "cl22=New South Wales",
             "dataResourceName=FrogID"]
)
frogs       decimalLatitude  decimalLongitude             eventDate   scientificName                                     taxonConceptID                              recordID dataResourceName occurrenceStatus
0           -37.246800        149.375000  2020-12-27T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  a5cd2fcd-5225-4d19-977c-b16ca5e8f1dd           FrogID          PRESENT
1           -37.089036        149.699526  2020-12-14T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  eebde5ef-cac4-4897-af00-cb2e39a0684f           FrogID          PRESENT
2           -37.077693        149.874402  2018-01-06T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  35340478-97c1-48a4-a463-991fe3a8daa0           FrogID          PRESENT
3           -37.077241        149.874787  2018-01-06T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  a7abc9f3-362f-469e-9076-5b55a2447b69           FrogID          PRESENT
4           -37.070746        149.896011  2020-12-13T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  1cc9dda8-f2d4-4f55-acf6-11c93b26da9e           FrogID          PRESENT
...                ...               ...                   ...              ...                                                ...                                   ...              ...              ...
27642       -28.207514        153.442592  2018-11-15T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  b094fed1-5bff-4df8-b556-cabd693c533a           FrogID          PRESENT
27643       -28.207472        153.442497  2018-11-15T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  5cc24fbd-8c6b-4a76-9b28-fec76ee08f37           FrogID          PRESENT
27644       -28.207442        153.442328  2020-02-07T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  61aa50a1-4c79-4fc3-b3ab-93538faa37b1           FrogID          PRESENT
27645       -28.207108        153.443021  2021-02-19T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  0324b8d1-77b0-4bf3-9ec2-6ad9efff18f2           FrogID          PRESENT
27646       -28.186157        153.445556  2018-11-16T00:00:00Z  Litoria peronii  https://biodiversity.org.au/afd/taxa/c584f24b-...  bcf83a54-a900-4265-960a-9436356a7107           FrogID          PRESENT
[27647 rows x 8 columns]Make a map
It’s time to make our map!
In order to draw our map of New South Wales, we’ll download a shapefile of the latest state and territory boundaries from the Australian Bureau of statistics. Download the “States and Territories - 2021 - Shapefile” zip file, and save the zip file in the same folder you are coding in.
Let’s load our States and Territories shapefile with read_file() and save it as states. Then, we will filter the shapefile to New South Wales and quickly plot it (specifying that the edges are black, the inside is white, and the figure size is 12 x 6 inches).
from matplotlib import pyplot as plt
import geopandas as gpd
# Load Australian state and territory boundaries
states = gpd.read_file("STE_2021_AUST_GDA94.shp")
# Filter to New South Wales and plot
states[states["STE_NAME21"] == "New South Wales"].plot(edgecolor = "#5A5A5A", linewidth = 0.5, facecolor = "white", figsize = (12,6))Our shapefile has plotted nicely, but there are many different ways to display our shape of NSW, which exists on a spherical globe (the Earth), onto a flat surface (our map). Our shapefile already has a projection, determined by its Coordinate Reference System (CRS) of GDA94.
# see the details of our shape's CRS
nsw.crs<Geographic 2D CRS: EPSG:4283>
Name: GDA94
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: Australia including Lord Howe Island, Macquarie Island, Ashmore and Cartier Islands, Christmas Island, Cocos (Keeling) Islands, Norfolk Island. All onshore and offshore.
- bounds: (93.41, -60.55, 173.34, -8.47)
Datum: Geocentric Datum of Australia 1994
- Ellipsoid: GRS 1980
- Prime Meridian: GreenwichTo make it clearer how the CRS changes the projection of a map, here are 3 maps of NSW projected with 3 different CRS:
- GDA94 (the current projection of our NSW shapefile)
- EPSG:8058 (a state/territory-specific projection for NSW)
- EPSG:2955 (intended for Canadian territories)
Code
nsw.plot(edgecolor = "#5A5A5A", facecolor = "white")
nsw_gda2020 = nsw.to_crs(8058)
nsw_gda2020.plot(edgecolor = "#5A5A5A", facecolor = "white")
nsw_nad83 = nsw.to_crs(2955)
nsw_nad83.plot(edgecolor = "#5A5A5A", facecolor = "white")The CRS projection of ALA data is EPSG:4326 (also known as “WGS84”). Reprojecting the CRS of our shapefile allows us to make sure the points of our data align correctly with our shapefile.
nsw = nsw.to_crs(4326)Now, we will add species observations to our map. First, we will plot our reprojected shapefile. Then, we will overlay a scatter plot using decimalLongitude as your x axis and decimalLatitude as your y axis. We’ll set the colour (c) and adjust the alpha to make our points partially transparent.
nsw.plot(edgecolor = "#5A5A5A", linewidth = 0.5, facecolor = "white", figsize = (12,6))
plt.scatter(frogs['decimalLongitude'],frogs['decimalLatitude'], c = "#6fab3f", alpha = 0.5)For some final touches (to make the map prettier), we can add a title and remove the border.
We’ll add a custom Google font, Roboto, by downloading it from Google Fonts, saving the folder in your current directory, unzipping the folder, and loading it with the matplotlib library.
import matplotlib as mpl
from matplotlib import font_manager
font_files = font_manager.findSystemFonts(fontpaths="Roboto/")
for ff in font_files:
  font_manager.fontManager.addfont(ff)For further information or troubleshooting tips for installing Roboto on your computer, this post is an excellent guide, as well as the matplotlib.font_manager documentation.
nsw.plot(edgecolor = "#5A5A5A", linewidth = 0.5, facecolor = "white", figsize = (12,6))
plt.scatter(frogs['decimalLongitude'],frogs['decimalLatitude'], c = "#6fab3f", alpha = 0.5, label = "Litoria peronii")
plt.suptitle("Peron's tree frog",fontsize=36,font='Roboto')
plt.title("FrogID observations in New South Wales since 2018",fontsize=28,font='Roboto')
plt.axis('off')(140.0937672995, 160.0147167105, -37.972572696499995, -27.6894786335)To save your plot in your current folder, you can use:
plt.savefig("perons_tree_frog_nsw.png")Final thoughts
We hope this post has helped make the basic steps of making a map simple and easy to understand. For more advanced mapping in Python, check out our ALA Labs article on how to map invasive species.
Expand for session info
-----
galah               0.8.1
geopandas           0.14.1
matplotlib          3.8.2
natsort             8.4.0
pandas              2.1.3
session_info        1.0.0
-----
Python 3.12.0 (tags/v3.12.0:0fb18b0, Oct  2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)]
Windows-10-10.0.19045-SP0
-----
Session information updated at 2024-02-13 11:02Download data
Peron’s Tree frog is one of the most recorded frog species in the Atlas of Living Australia. Growing up to 7cm in length, it is well-known for its eyes which often look like they have a black cross on them!
Left: Litoria peronii (Giverny CC-BY-NC 4.0 (Int), Middle: Litoria peronii (debtaylor142 CC-BY-NC 4.0 (Int)), Right: Litoria peronii (Ernst Weiher, iNaturalist CC-BY-NC 4.0 (Int))
First, let’s load some packages.
library(galah)
library(ggplot2)
library(dplyr)
library(sf)
library(here)
library(showtext)Search for taxa
When trying to download data about any species or clade, we can search using search_taxa(). It’s recommended to use search_taxa() to check whether a taxonomic search returns what you were expecting (even if you know the scientific name)! We can check taxonomic information about Peron’s tree frog with search_taxa(), which returns extra details about the species when a match is found.
search_taxa("Litoria peronii")# A tibble: 1 × 15
  search_term     scientific_name scientific_name_autho…¹ taxon_concept_id rank 
  <chr>           <chr>           <chr>                   <chr>            <chr>
1 Litoria peronii Litoria peronii (Tschudi, 1838)         https://biodive… spec…
# ℹ abbreviated name: ¹scientific_name_authorship
# ℹ 10 more variables: match_type <chr>, kingdom <chr>, phylum <chr>,
#   class <chr>, order <chr>, family <chr>, genus <chr>, species <chr>,
#   vernacular_name <chr>, issues <chr>Search for fields
Next, we can search for fields and field IDs for filtering our query. In this case, we are interested in filtering to a specific year, state/territory and data resource.
We can start by searching for any fields containing the word “year” using search_all().
search_all(fields, "year")# A tibble: 8 × 3
  id                  description            type  
  <chr>               <chr>                  <chr> 
1 year                Year                   fields
2 raw_year            Year (unprocessed)     fields
3 endDayOfYear        End Day Of Year        fields
4 startDayOfYear      Start Day Of Year      fields
5 occurrenceYear      Date (by decade)       fields
6 raw_endDayOfYear    <NA>                   fields
7 raw_startDayOfYear  <NA>                   fields
8 namePublishedInYear Name Published In Year fieldsWe can do the same kind of search to find fields with information of australian states/territories and data resource names.
search_all(fields, "states")# A tibble: 5 × 3
  id       description                            type  
  <chr>    <chr>                                  <chr> 
1 cl2013   ASGS Australian States and Territories fields
2 cl22     Australian States and Territories      fields
3 cl927    States including coastal waters        fields
4 cl10925  PSMA States (2016)                     fields
5 cl110925 PSMA States - Abbreviated (2016)       fieldssearch_all(fields, "resource")# A tibble: 8 × 3
  id                     description              type  
  <chr>                  <chr>                    <chr> 
1 resourceID             Resource ID              fields
2 dataResourceUid        Dataset                  fields
3 dataResourceName       Dataset                  fields
4 relatedResourceID      Related Resource ID      fields
5 raw_dataResourceUid    Dataset (unprocessed)    fields
6 raw_dataResourceName   Dataset (unprocessed)    fields
7 relationshipOfResource Relationship Of Resource fields
8 resourceRelationshipID Resource Relationship ID fieldsIf you are ever uncertain which field ID to choose, you can use show_values() to see what possible values are within a field. For example, let’s see what values are in field ID cl22.
search_all(fields, "cl22") |>
  show_values()• Showing values for 'cl22'.# A tibble: 11 × 1
   cl22                        
   <chr>                       
 1 New South Wales             
 2 Victoria                    
 3 Queensland                  
 4 South Australia             
 5 Western Australia           
 6 Northern Territory          
 7 Australian Capital Territory
 8 Tasmania                    
 9 Unknown1                    
10 Coral Sea Islands           
11 Ashmore and Cartier Islands We can also search for a value using search_values(), which might be handy to check that “FrogID” is a value in dataResourceName.
search_all(fields, "dataResourceName") |>
  search_values("FrogID")! Search returned 2 matched fields.
• Showing values for 'dataResourceName'.# A tibble: 1 × 1
  dataResourceName
  <chr>           
1 FrogID          Download observations
Now we are ready to build our query to download observations of Peron’s tree frog in New South Wales since 2018 recorded by FrogID.
For those unfamiliar with Australian geography, New South Wales is this one:
First, let’s find the number of records that match our query. This is good practice before downloading occurrence records because you can check exactly how many records you are intending to download (and avoid an accidental massive download)!
We’ll use atlas_counts() to download record counts, specifying the taxon using galah_identify(), and narrowing the year range, state/territory and data resource using galah_filter().
galah_call() |>                                 # Begin a query
  galah_identify("Litoria peronii") |>          # Peron's tree frog
  galah_filter(year >= 2018,                    # since 2018
               cl22 == "New South Wales",       # in New South Wales
               dataResourceName == "FrogID") |> # by FrogID
  atlas_counts()                                # Download record counts# A tibble: 1 × 1
  count
  <int>
1 27647Now we can use atlas_occurrences() to download occurrence records, which returns each observation’s location coordinates and event date.
You will need to first provide a registered email with the ALA using galah_config() before retrieving records.
galah_config(email = "your-email-here")frogs <- galah_call() |>
  galah_identify("Litoria peronii") |>
  galah_filter(year >= 2018,
               cl22 == "New South Wales",
               dataResourceName == "FrogID") |>
  atlas_occurrences()
frogs# A tibble: 27,647 × 8
   recordID       scientificName taxonConceptID decimalLatitude decimalLongitude
   <chr>          <chr>          <chr>                    <dbl>            <dbl>
 1 0008c41d-fd35… Litoria peron… https://biodi…           -33.2             152.
 2 000c1c20-bec3… Litoria peron… https://biodi…           -33.0             150.
 3 000e46ad-9ace… Litoria peron… https://biodi…           -31.3             149.
 4 001229c0-4c48… Litoria peron… https://biodi…           -33.7             150.
 5 0014b8ff-ddd5… Litoria peron… https://biodi…           -33.4             151.
 6 00183dd1-c9cd… Litoria peron… https://biodi…           -36.6             150.
 7 002088e5-3a73… Litoria peron… https://biodi…           -32.1             150.
 8 002108e1-9933… Litoria peron… https://biodi…           -33.7             151.
 9 00251c30-9dfd… Litoria peron… https://biodi…           -34.3             151.
10 00259fce-87db… Litoria peron… https://biodi…           -30.5             153.
# ℹ 27,637 more rows
# ℹ 3 more variables: eventDate <dttm>, occurrenceStatus <chr>,
#   dataResourceName <chr>Make a map
It’s time to make our map!
In order to draw our map of New South Wales, we’ll download a shapefile of the latest state and territory boundaries from the Australian Bureau of Statistics. Download the “States and Territories - 2021 - Shapefile” zip file, and save the zip file in the same folder you are coding in.
Let’s load our States and Territories shapefile with read_file() and save it as states. Then, we will filter the shapefile to New South Wales and quickly plot it to check it plots correctly (specifying that the border is grey, and the inside is white).
# Load Australian state and territory boundaries
states <- st_read(here("STE_2021_AUST_GDA94.shp"),
                  quiet = TRUE)
# Filter to New South Wales
nsw <- states |> 
  filter(STE_NAME21 == "New South Wales")
# Plot
ggplot() +
  geom_sf(data = nsw,
          colour = "grey60",
          fill = "white")Our shapefile has plotted nicely, but there are many different ways to display our shape of NSW, which exists on a spherical globe (the Earth), onto a flat surface (our map). Our shapefile already has a projection, determined by its Coordinate Reference System (CRS) of GDA94.
# see the details of our shape's geometry
nsw |> st_geometry()Geometry set for 1 feature 
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 140.9993 ymin: -37.50516 xmax: 159.1092 ymax: -28.15689
Geodetic CRS:  GDA94MULTIPOLYGON (((159.0623 -31.50887, 159.0622 -3...To make it clearer how the CRS changes the projection of a map, here are 3 maps of NSW projected with 3 different CRS:
- GDA94 (the current projection of our NSW shapefile)
- EPSG:8058 (a state/territory-specific projection for NSW)
- EPSG:2955 (intended for Canadian territories)
Code
p1 <- nsw |> 
  ggplot() +
  geom_sf(colour = "grey60",
          fill = "white")
p2 <- nsw |> 
  st_transform(crs = st_crs(8058)) |>
  ggplot() +
  geom_sf(colour = "grey60",
          fill = "white")
p3 <- nsw |> 
  st_transform(crs = st_crs(2955)) |>
  ggplot() +
  geom_sf(colour = "grey60",
          fill = "white")Data from the ALA use CRS EPSG:4326 (also known as “WGS84”). Reprojecting our shapefile to the same CRS allows us to make sure the points of our data align correctly with our shapefile.
nsw <- nsw |>
  st_transform(crs = st_crs(4326))Now, we will add species observations to our map. First, we will plot our reprojected shapefile. Then, we will overlay a scatter plot using decimalLongitude as your x axis and decimalLatitude as your y axis. We’ll set the colour and adjust the alpha to make our points partially transparent.
ggplot() +
  geom_sf(data = nsw,
          colour = "grey60",
          fill = "white") +
  geom_point(data = frogs,
             aes(x = decimalLongitude,
                 y = decimalLatitude),
             colour = "#6fab3f",
             alpha = 0.5,
             size = 1.1)For some final touches (to make the map prettier), we can add a centred title and add a minimal theme.
We’ll add a custom Google font, Roboto, using the showtext package, loading it into R with font_add_google().
# Add font
font_add_google("Roboto")
showtext_auto(enable = TRUE)
ggplot() +
  geom_sf(data = nsw,
          colour = "grey60",
          fill = "white") +
  geom_point(data = frogs,
             mapping = aes(x = decimalLongitude,
                           y = decimalLatitude),
             colour = "#6fab3f",
             alpha = 0.5,
             size = 1.1) +
  labs(title = "Peron's tree frog",
       subtitle = "FrogID observations in New South Wales since 2018") +
  theme_void() + 
  theme(
    plot.title = element_text(hjust = 0.5,          # horizontally centre
                              family = "Roboto",    # add font
                              size = 31),           # change font size
    plot.subtitle = element_text(hjust = 0.5,       # horizontally centre
                                 family = "Roboto", # add font
                                 size = 25)         # change font size
  )To save your plot in your current folder, you can use:
# set dpi for text
showtext_opts(dpi = 320)
# save
ggsave(here("map_perons-tree-frog.png"),
       height = 9, width = 10,
       unit = "in",
       dpi = 320)Final thoughts
We hope this post has helped make the basic steps of making a map simple and easy to understand. For more advanced mapping in R, check out our ALA Labs articles on exploring dingo observations and how to make a choropleth map with multiple colour scales.
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-13
 pandoc   2.12 @ C:/Users/KEL329/ANACON~1/Scripts/ (via rmarkdown)
─ Packages ───────────────────────────────────────────────────────────────────
 package     * version date (UTC) lib source
 dplyr       * 1.1.4   2023-11-17 [1] CRAN (R 4.3.2)
 galah       * 2.0.1   2024-02-06 [1] CRAN (R 4.3.2)
 ggplot2     * 3.4.4   2023-10-12 [1] CRAN (R 4.3.1)
 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)
 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)
 showtext    * 0.9-6   2023-05-03 [1] CRAN (R 4.3.2)
 showtextdb  * 3.0     2020-06-04 [1] CRAN (R 4.3.2)
 sysfonts    * 0.8.8   2022-03-13 [1] CRAN (R 4.3.2)
 [1] C:/Users/KEL329/R-packages
 [2] C:/Users/KEL329/AppData/Local/Programs/R/R-4.3.2/library
─ Python configuration ───────────────────────────────────────────────────────
 python:         C:/Users/KEL329/OneDrive - CSIRO/Documents/ALA/Github/ala-labs/.venv/Scripts/python.exe
 libpython:      C:/Users/KEL329/AppData/Local/Programs/Python/Python312/python312.dll
 pythonhome:     C:/Users/KEL329/OneDrive - CSIRO/Documents/ALA/Github/ala-labs/.venv
 version:        3.12.0 (tags/v3.12.0:0fb18b0, Oct  2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)]
 Architecture:   64bit
 numpy:          C:/Users/KEL329/OneDrive - CSIRO/Documents/ALA/Github/ala-labs/.venv/Lib/site-packages/numpy
 numpy_version:  1.26.2
 
 NOTE: Python version was forced by VIRTUAL_ENV
──────────────────────────────────────────────────────────────────────────────