Skip to contents

The share is the catalogue

There is no manifest file to maintain. build_catalogue() walks \\ABMI-DATA2\science\spatial_data, parses the readme stored beside each dataset, and assembles a table from what it finds. Documenting a dataset means editing its readme; nothing has to be registered anywhere, and the catalogue cannot drift from the data.

Folders follow the ISO 19115 topic categories and readmes follow the ABMI spatial metadata template, both documented in the geospatial catalog and management guide.

A folder is catalogued as a layer when it holds a readme written to the product or variant template. Theme folders carry a shorter Category/Description/Examples readme and are reported by list_themes() instead. Products sit directly under their topic category, so each layer has both a short name (fab_dem) and a full id (elevation/fab_dem). A product may split its record across a product readme and one readme per variant subfolder, in which case the variant is the layer and its id carries the variant folder too (geoscientificInformation/soilgrids_250_v2_ab/abmi1km).

Two top-level folders no scan looks at are _temp, the share’s scratch area, and _deprecated, where superseded products are parked. Work in progress, staging copies, and exports live in the first; products that have been replaced live in the second. The contents of neither are catalogued as layers or counted against the share by check_metadata() — a layer only becomes discoverable once it is filed under a theme, and it stops being discoverable once it is moved to _deprecated.

Pointing at the share

spatial_root() reports where the catalogue reads from and errors if the share is unreachable.

spatial_root()
#> [1] "//ABMI-DATA2/science/spatial_data"

To work from a mirror, a mapped drive, or a local copy, set an option or an environment variable:

options(sciSpatialR.spatial_root = "D:/spatial_data")
Sys.setenv(SCISPATIALR_SPATIAL_ROOT = "D:/spatial_data")

Browsing

Start with the themes, which show the topic categories and how much sits under each:

14 themes, 20 layers catalogued

 theme                            description                                        n_layers
 elevation                        Height above or below sea level.                   5       
 inlandWaters                     Inland water features, drainage systems, and thei… 4       
 imageryBaseMapsEarthCover        Base maps.                                         3       
 biota                            Flora and/or fauna in natural environments.        2       
 geoscientificInformation         Information pertaining to earth sciences.          2       
 location                         Positional information and services.               2       
 boundaries                       Legal land descriptions.                           1       
 transportation                   Means and aids for conveying persons and/or goods. 1       
 .git                             <NA>                                               0       
 climatologyMeteorologyAtmosphere Processes and phenomena of the atmosphere.         0       
 economy                          Economic activities, conditions, and employment.   0       
 environment                      Environmental resources, protection, and conserva… 0       
 farming                          Rearing of animals and/or cultivation of plants.   0       
 figures                          <NA>                                               0       

Every ISO 19115 topic category appears, including the ones nothing is filed under yet, so the empty folders read as available rather than missing. A theme folder with no readme of its own would still appear, with no description, so an undocumented theme stays visible rather than dropping out of the listing. _temp and _deprecated are the exceptions, and are absent: they are scratch and retirement space rather than topic categories.

The returned table carries an examples column as well, too long to tabulate but useful when deciding where a new dataset belongs.

Then list the layers:

20 layers in 8 themes

 id                                                              title                                         year resolution_m data_type
 biota/grassland_inventory                                       Grassland Inventory for Alberta, Manitoba, a… 2024   30.00      raster   
 biota/natural_regions_subregions_of_alberta                     Natural Regions and Subregions of Alberta     2022      NA      vector   
 boundaries/alberta                                              AB2020_provincial_boundary                    2023      NA      vector   
 elevation/fab_dem                                               FABDEM – Forest And Buildings Removed Copern… 2018  100.00      raster   
 elevation/geomorpho90                                           Alberta Geomorphometric Layers (Geomorpho90m) 2023   90.00      raster   
 elevation/nrcan_mrdem_dsm                                       Medium Resolution Digital Elevation Model (M… 2006   30.00      raster   
 elevation/nrcan_mrdem_dtm                                       Medium Resolution Digital Elevation Model (M… 2006   30.00      raster   
 elevation/nrcan_mrdem_dtm_hillshade                             Medium Resolution Digital Elevation Model (M… 2006   30.00      raster   
 geoscientificInformation/hihydrosoil_v2_ab/abmi1km              HiHydroSoil v2.0                              2020 1000.00      raster   
 geoscientificInformation/soilgrids_250_v2_ab/abmi1km            SoilGrids 2.0 global soil property predictio… 2020 1000.00      raster   
 imageryBaseMapsEarthCover/landsat_summer_mean_indices_2000_2024 Landsat Time Series - Alberta Mean Spectral … 2024   30.00      raster   
 imageryBaseMapsEarthCover/modis_land_cover_dynamics_2001_2023   MODIS Annual Land Cover Dynamics (MCD12Q2) -… 2023  500.00      raster   
 imageryBaseMapsEarthCover/scanfi_v1.2                           SCANFI: Spatialized Canadian National Forest… 2020   30.00      raster   
 inlandWaters/archydro2                                          Alberta ArcHydro Phase 2 Data                 1996  100.00      vector   
 inlandWaters/dynamicSurfaceWaterMaps                            Dynamic Surface Water Maps of Canada from 19… 2023      NA      raster   
 inlandWaters/hydrologically adjusted elevations                 Height Above Nearest Drainage (HAND) - Hydro… 2024   92.77      raster   
 inlandWaters/topographic_wetness_index                          Topographic Wetness Index (TWI)               2024   92.77      raster   
 location/GRID1SQKM_AB2020_gdb                                   GRID1SQKM_AB2020                                NA 1000.00      vector   
 location/GRID1SQKM_AB2020_raster                                GRID1SQKM_AB2020_raster                       2026 1000.00      raster   
 transportation/government_of_alberta_access_layers              Access and Facility Roads - Alberta           2023      NA      vector   

Nothing under _temp or _deprecated is listed, documented or not — the Sentinel-2 composites staged in the first are invisible to the catalogue until they are filed under a theme, and a product moved to the second disappears from it.

Or narrow the listing to one theme:

list_layers(theme = "elevation")
5 layers in 1 theme

 id                                  title                                         year resolution_m data_type
 elevation/fab_dem                   FABDEM – Forest And Buildings Removed Copern… 2018 100          raster   
 elevation/geomorpho90               Alberta Geomorphometric Layers (Geomorpho90m) 2023  90          raster   
 elevation/nrcan_mrdem_dsm           Medium Resolution Digital Elevation Model (M… 2006  30          raster   
 elevation/nrcan_mrdem_dtm           Medium Resolution Digital Elevation Model (M… 2006  30          raster   
 elevation/nrcan_mrdem_dtm_hillshade Medium Resolution Digital Elevation Model (M… 2006  30          raster   

list_layers() prints a summary and returns the manifest invisibly. Assign it, or pass verbose = FALSE, to work with the full table — one row per layer, carrying the parsed template fields alongside n_files, size_mb, data_type, path, and readme.

cat_df <- list_layers(verbose = FALSE)
dim(cat_df)
#> [1] 18 40

Forty columns is too wide to print whole; a few of them give the shape of the table:

cat_df[1:5, c("name", "theme", "year", "resolution_m",
              "n_files", "size_mb", "data_type")]
                                   name      theme year resolution_m n_files size_mb data_type
1 natural_regions_subregions_of_alberta      biota 2022           NA       1    11.2    vector
2                   grassland_inventory      biota 2024           30       3 30110.4    raster
3                               alberta boundaries 2023           NA       1     0.4    vector
4                               fab_dem  elevation 2018          100       1 11272.9    raster
5                           geomorpho90  elevation 2023           90       1 11197.9    raster

The full set of fields — the parsed template alongside the columns the file scan adds:

names(cat_df)
 [1] "id"                 "name"               "theme"              "product_id"
 [5] "variant"            "title"              "topic_category"     "keywords"
 [9] "year"               "resolution"         "resolution_m"       "xmin"
[13] "xmax"               "ymin"               "ymax"               "crs"
[17] "crs_name"           "datum"              "vertical_crs"       "publication_date"
[21] "start_date"         "end_date"           "format"             "size"
[25] "use_constraints"    "access_constraints" "contact_name"       "contact_email"
[29] "doi"                "online_resource"    "metadata_date"      "abstract"
[33] "purpose"            "credits"            "lineage"            "citation"
[37] "n_files"            "size_mb"            "data_type"          "path"
[41] "readme"             "product_readme"

product_id and variant are what a split record adds. A product documented in one readme has variant NA and a product_id equal to its id. A product whose data sits in variant subfolders contributes one row per variant instead of a row of its own: id names the variant folder, product_id the product above it, and readme and product_readme point at the two halves of the record.

cat_df[!is.na(cat_df$variant),
       c("id", "product_id", "variant", "resolution_m", "crs")]
                                                   id               product_id variant resolution_m       crs
   geoscientificInformation/hihydrosoil_v2_ab/abmi1km  …/hihydrosoil_v2_ab      abmi1km         1000 EPSG:3400
 geoscientificInformation/soilgrids_250_v2_ab/abmi1km  …/soilgrids_250_v2_ab    abmi1km         1000 EPSG:3400

The resolution and CRS above come from the variant records; the titles and licences those rows also carry come from the product records. layer_meta() returns the two read together, so nothing is lost by the split.

The catalogue as it stands

The table below is a frozen scan of the share, rendered when this vignette was built. It is what list_layers(verbose = FALSE) returns, narrowed to the columns worth tabulating — run the call yourself for the live version and the other thirty-odd columns.

The spatial data catalogue, scanned 2026-08-27.
theme name title year res (m) type size (MB)
biota grassland_inventory Grassland Inventory for Alberta, Manitoba, and Saskatchewan (2023) 2024 30.00 raster 30,110.4
biota natural_regions_subregions_of_alberta Natural Regions and Subregions of Alberta 2022 vector 11.2
boundaries alberta AB2020_provincial_boundary 2023 vector 0.4
elevation fab_dem FABDEM – Forest And Buildings Removed Copernicus Global DEM (30 m) 2018 100.00 raster 11,272.9
elevation geomorpho90 Alberta Geomorphometric Layers (Geomorpho90m) 2023 90.00 raster 11,197.9
elevation nrcan_mrdem_dsm Medium Resolution Digital Elevation Model (MRDEM) - DSM Cloud Optimized GeoTIFF (COG) 2006 30.00 raster 56,895.8
elevation nrcan_mrdem_dtm Medium Resolution Digital Elevation Model (MRDEM) - DTM Cloud Optimized GeoTIFF (COG) 2006 30.00 raster 57,503.7
elevation nrcan_mrdem_dtm_hillshade Medium Resolution Digital Elevation Model (MRDEM) - DTM Hillshade Cloud Optimized GeoTIFF (COG) 2006 30.00 raster 13,648.1
geoscientificInformation hihydrosoil_v2_ab HiHydroSoil v2.0 2020 1,000.00 raster 220.5
geoscientificInformation soilgrids_250_v2_ab SoilGrids 2.0 global soil property predictions 2020 1,000.00 raster 174.0
imageryBaseMapsEarthCover landsat_summer_mean_indices_2000_2024 Landsat Time Series - Alberta Mean Spectral Indices (2000-2024) 2024 30.00 raster 2,772,122.1
imageryBaseMapsEarthCover modis_land_cover_dynamics_2001_2023 MODIS Annual Land Cover Dynamics (MCD12Q2) - 500m Phenology 2023 500.00 raster 2,529.0
imageryBaseMapsEarthCover scanfi_v1.2 SCANFI: Spatialized Canadian National Forest Inventory 2020 30.00 raster 34,349.8
inlandWaters archydro2 Alberta ArcHydro Phase 2 Data 1996 100.00 vector 680.3
inlandWaters dynamicSurfaceWaterMaps Dynamic Surface Water Maps of Canada from 1984-2023 Landsat Satellite Imagery 2023 raster 21,796.5
inlandWaters hydrologically adjusted elevations Height Above Nearest Drainage (HAND) - Hydrologically Adjusted Elevations 2024 92.77 raster 239.0
inlandWaters topographic_wetness_index Topographic Wetness Index (TWI) 2024 92.77 raster 1,193.0
location GRID1SQKM_AB2020_gdb GRID1SQKM_AB2020 1,000.00 vector 322.8
location GRID1SQKM_AB2020_raster GRID1SQKM_AB2020_raster 2026 1,000.00 raster 0.0
transportation government_of_alberta_access_layers Access and Facility Roads - Alberta 2023 vector 1,399.7

Blank cells are fields the readme leaves unfilled, which is what keeps a layer out of the matching find_layer() filter.

What variables a product holds

list_layers() says which layers exist; list_variables() says what is inside them. It reads the band blocks out of each product readme, so the variables available for extraction can be browsed without opening a readme or a raster.

list_variables(product = "soilgrids_250_v2_ab")
61 variables across 1 product

 product             band                    measure                                  units         
 soilgrids_250_v2_ab bdod_0-5cm_mean         Bulk density of the fine earth fraction  kg/dm3        
 soilgrids_250_v2_ab bdod_5-15cm_mean        Bulk density of the fine earth fraction  kg/dm3        
 soilgrids_250_v2_ab bdod_15-30cm_mean       Bulk density of the fine earth fraction  kg/dm3        
 soilgrids_250_v2_ab bdod_30-60cm_mean       Bulk density of the fine earth fraction  kg/dm3        
 soilgrids_250_v2_ab bdod_60-100cm_mean      Bulk density of the fine earth fraction  kg/dm3        
 soilgrids_250_v2_ab bdod_100-200cm_mean     Bulk density of the fine earth fraction  kg/dm3        
 soilgrids_250_v2_ab cec_0-5cm_mean          Cation exchange capacity of the soil     cmol(c)/kg    
 soilgrids_250_v2_ab cec_5-15cm_mean         Cation exchange capacity of the soil     cmol(c)/kg    
 ...

Bands belong to the product rather than to a variant — resampling a layer does not change what its bands measure — so a product with several variants reports one set of rows, not one set per variant.

The compact view above shows two of the columns; the returned table also carries scale, valid_range, and description. Called with no arguments it covers the whole catalogue, which makes it searchable — every band measuring pH, wherever it lives:

v <- list_variables(verbose = FALSE)
v[grepl("pH", v$measure, fixed = TRUE),
  c("name", "band", "units")]
                  name                 band units
71 soilgrids_250_v2_ab     phh2o_0-5cm_mean    pH
72 soilgrids_250_v2_ab    phh2o_5-15cm_mean    pH
73 soilgrids_250_v2_ab   phh2o_15-30cm_mean    pH
74 soilgrids_250_v2_ab   phh2o_30-60cm_mean    pH
75 soilgrids_250_v2_ab  phh2o_60-100cm_mean    pH
76 soilgrids_250_v2_ab phh2o_100-200cm_mean    pH

theme narrows the listing the same way it does elsewhere. A product whose readme documents no bands is listed with band NA rather than dropped, so the gap stays visible the way check_metadata() keeps missing template fields visible.

Finding a layer

find_layer() searches by what a layer is rather than where it lives. keyword matches the folder name, title, keywords, topic category, and abstract:

find_layer(keyword = "elevation")
10 layers in 4 themes

 id                                                            title                                         year resolution_m data_type
 biota/natural_regions_subregions_of_alberta                   Natural Regions and Subregions of Alberta     2022     NA       vector   
 elevation/fab_dem                                             FABDEM – Forest And Buildings Removed Copern… 2018 100.00       raster   
 elevation/geomorpho90                                         Alberta Geomorphometric Layers (Geomorpho90m) 2023  90.00       raster   
 elevation/nrcan_mrdem_dsm                                     Medium Resolution Digital Elevation Model (M… 2006  30.00       raster   
 elevation/nrcan_mrdem_dtm                                     Medium Resolution Digital Elevation Model (M… 2006  30.00       raster   
 elevation/nrcan_mrdem_dtm_hillshade                           Medium Resolution Digital Elevation Model (M… 2006  30.00       raster   
 imageryBaseMapsEarthCover/modis_land_cover_dynamics_2001_2023 MODIS Annual Land Cover Dynamics (MCD12Q2) -… 2023 500.00       raster   
 inlandWaters/archydro2                                        Alberta ArcHydro Phase 2 Data                 1996 100.00       vector   
 inlandWaters/hydrologically adjusted elevations               Height Above Nearest Drainage (HAND) - Hydro… 2024  92.77       raster   
 inlandWaters/topographic_wetness_index                        Topographic Wetness Index (TWI)               2024  92.77       raster   

Note the first hit: a layer filed under biota matched on its abstract, which is the point of searching metadata rather than folder names.

Filters combine, and each takes either a single value or a c(min, max) range:

find_layer(year = c(2020, 2024))
find_layer(resolution = c(0, 120))
find_layer(extent = c(-120, -110, 49, 60))   # decimal degrees
find_layer(theme = "elevation", resolution = 30)

A filter narrows to layers known to match. Resolution, year, and extent come from the readme, so a layer whose readme leaves the field blank is excluded rather than guessed at — check_metadata() will show you which readmes are keeping a layer out.

Loading a layer

get_layer() reads the data with terra, returning a SpatRaster or a SpatVector depending on the format:

twi <- get_layer("topographic_wetness_index")
nsr <- get_layer("natural_regions_subregions_of_alberta")

Pass return_path = TRUE when you want the file rather than the object — to hand it to another tool, or to open it with your own arguments:

get_layer("fab_dem", return_path = TRUE)
#> [1] "//ABMI-DATA2/science/spatial_data/elevation/fab_dem/fab_dem_us_canada.tif"

Some folders hold several datasets. layer_files() shows what is there, and file picks one:

layer_files("grassland_inventory")
#> [1] ".../alberta_grassland_classification_2023.tif"
#> [2] ".../manitoba_grassland_classification_2023.tif"
#> [3] ".../saskatchewan_grassland_classification_2023.tif.tif"

get_layer("grassland_inventory", file = "alberta")

Asking for a multi-file layer without file is an error that lists the candidates, so you never silently get the wrong raster. Shapefile sidecars (.dbf, .shx, .prj) are hidden; pass all = TRUE to see every file in the folder.

Short names work when unambiguous. If two themes hold a folder of the same name, use the full id:

get_layer("elevation/fab_dem")

Taking a local copy

get_layer() reads a layer where it sits, over the network. To work offline or to avoid server latency, download_layer() copies one to a folder on your local machine:

download_layer("fab_dem", "D:/local_spatial")
#> Copying elevation/fab_dem: 3 files, 11.0 GB to
#> D:/local_spatial/fab_dem ...
#> Copied 3 files (11.0 GB) in 9m 12s; 0 already up to date.
#> Verified 3 files against the source on size and md5 checksum.
#> Copy log written to D:/local_spatial/fab_dem/_download_log.txt

Everything in the layer folder comes down, not just the data: the readme that documents it, and any ancillary files. The shapefile sidecars layer_files() hides come too, so the copy opens.

The arguments:

Argument Does what
name Layer name or catalogue id, as for get_layer()
dest Local folder to copy into; created if it does not exist
dry_run Report what would be copied, and what it comes to, writing nothing
verify Read each copy back and check size and md5 against the source. TRUE by default
overwrite Copy every file again rather than skipping unchanged ones
keep_theme Reproduce the share’s full path, ISO topic category included
quiet Suppress the progress messages

Test before copying

A copy from the share can be large, so start with dry_run = TRUE. It does the whole search and sizing and prints summary to console:

download_layer("scanfi_v1.2", "D:/local_spatial", dry_run = TRUE)
#> Dry run — nothing was written.
#> 14 files (22.6 GB) would be copied to D:/local_spatial; 0 already
#> up to date.
#>  file                          size    part  status
#>  scanfi_v1.2/_readme.txt       4.1 KB  layer would copy
#>  scanfi_v1.2/scanfi_index.tif  1.8 GB  layer would copy
#>  ...

The same table comes back as a data.frame, so you can total it or filter it yourself before committing to the copy.

Where the files land

The copy is rooted at the product folder, which lands directly in dest. The ISO topic category is the share’s filing system rather than part of the dataset, so it is left behind — elevation/fab_dem on the share arrives as D:/local_spatial/fab_dem.

Everything from the product folder down is kept, so where a product splits its record the product readme still lands one level up from the variant, exactly as the two sit on the share:

p <- download_layer(
  "geoscientificInformation/soilgrids_250_v2_ab/abmi1km",
  "D:/local_spatial"
)
p[, c("part", "destination", "status")]
#>     part                                             destination status
#>    layer  D:/local_spatial/soilgrids_250_v2_ab/abmi1km/readme.txt copied
#>    layer D:/local_spatial/soilgrids_250_v2_ab/abmi1km/soil.tif    copied
#>  product          D:/local_spatial/soilgrids_250_v2_ab/readme.txt copied

The variant readme carries the resolution, CRS, and derivation, while the product readme carries the title, licence, and citation. A variant copied on its own would arrive with half its provenance. Sibling variants are separate layers and are not swept up; ask for one by its own id, since the name they share is the product’s.

geomorpho90 publishes two: the provider’s native 90 m grid, and a copy resampled to the ABMI 1 km grid. Downloading both puts them side by side under the one product folder:

download_layer("elevation/geomorpho90/abmi1km", "D:/local_spatial")
#> Copying elevation/geomorpho90/abmi1km: 3 files, ... ...
#> Copied 3 files (...); 0 already up to date.

p <- download_layer("elevation/geomorpho90/native", "D:/local_spatial")
p[, c("part", "status")]
#>     part  status
#>    layer  copied
#>    layer  copied
#>  product skipped

The product row comes back skipped on the second download. Both variants share one product record, so the second finds it already there rather than writing it twice, and the copy ends up with the single shared readme the share has:

D:/local_spatial/geomorpho90/
|-- readme.txt              <- the product record, shared
|-- abmi1km/
|   |-- readme.txt          <- this variant's own record
|   |-- geomorpho90_abmi1km.tif
|   `-- _download_log.txt
`-- native/
    |-- readme.txt
    |-- geomorpho90_native.tif
    `-- _download_log.txt

Re-running is cheap

A file already present and matching its source on size and modification time is skipped, so an interrupted copy resumes rather than restarts:

download_layer("fab_dem", "D:/local_spatial")
#> elevation/fab_dem is already up to date in D:/local_spatial (3 files).

overwrite = TRUE forces a fresh copy regardless, which is what to reach for when you suspect the local copy rather than the transfer:

download_layer("fab_dem", "D:/local_spatial", overwrite = TRUE)
#> Copying elevation/fab_dem: 3 files, 11.0 GB to
#> D:/local_spatial/fab_dem ...
#> Copied 3 files (11.0 GB) in 9m 08s; 0 already up to date.
#> Verified 3 files against the source on size and md5 checksum.

Keeping a local mirror

Everything above copied into D:/local_spatial in the default layout (keep_theme = TRUE), which is fine for copying a single dataset to work on. To produce a mirror (a folder the catalogue itself can read) needs keep_theme = TRUE, so the topic category folder is kept.

download_layer("elevation/fab_dem", "D:/local_mirror",
               keep_theme = TRUE)
download_layer(
  "geoscientificInformation/soilgrids_250_v2_ab/abmi1km",
  "D:/local_mirror",
  keep_theme = TRUE
)

Each lands at the path its id names, the product readme one level above the variant as on the share:

D:/local_mirror/
|-- elevation/
|   `-- fab_dem/
|       |-- readme.txt
|       |-- fab_dem_us_canada.tif
|       `-- _download_log.txt
`-- geoscientificInformation/
    `-- soilgrids_250_v2_ab/
        |-- readme.txt              <- the product record
        `-- abmi1km/
            |-- readme.txt          <- this variant's own record
            |-- soilgrids_abmi1km.tif
            `-- _download_log.txt

Point spatial_root() at the mirror and the package works offline against it. The split records survive, so a variant is still catalogued as its own layer and still inherits the title and licence from the product readme above it:

options(sciSpatialR.spatial_root = "D:/local_mirror")

list_layers()
#> 2 layers in 2 themes
#>
#>  id                                                   title
#>  elevation/fab_dem                                    FABDEM – Forest…
#>  geoscientificInformation/soilgrids_250_v2_ab/abmi1km SoilGrids 2.0 …

get_layer("soilgrids_250_v2_ab")

Switch back to default directory for below code chunks

options(sciSpatialR.spatial_root = NULL)
spatial_root()
#> [1] "//ABMI-DATA2/science/spatial_data"

Copy info saved beside the data

Every copy writes _download_log.txt into the layer’s folder, next to the readme:

cat(readLines("D:/local_spatial/fab_dem/_download_log.txt"),
    sep = "\n")
#> ================================================================
#> sciSpatialR download log
#> ================================================================
#>
#> Layer:        elevation/fab_dem
#> Title:        Forest and Buildings Removed DEM
#> Source:       //ABMI-DATA2/science/spatial_data/elevation/fab_dem
#> Destination:  D:/local_spatial/fab_dem
#> Downloaded:   2026-09-24 09:14:02
#> By:           bcasey
#> Package:      sciSpatialR 0.1.0
#> Elapsed:      9m 12s
#> Verified:     size and md5 checksum against the source
#>
#> FILES
#> ----------------------------------------------------------------
#> status   verified        bytes  md5                    file
#> copied   ok        11820476416  d41d8cd9...  fab_dem/fab_dem.tif
#> ...
#>
#> SUMMARY
#> ----------------------------------------------------------------
#> 3 copied, 0 already up to date, 0 failed
#> 11.0 GB in 9m 12s (20.4 MB/s)
#> All 3 files match their source on size and md5 checksum.

By default each copied file is read back and compared to its source on size and md5 checksum before the log records it as sound, and the same verdict comes back in the verified and md5 columns of the manifest. Only files copied in that run are checked. Verification re-reads each copied file from the share, so it roughly doubles the time a large copy takes; pass verify = FALSE to skip it.

Runs append to the log rather than replacing it, so a layer re-copied over several sessions keeps its whole history. A run that copies nothing leaves it alone, so the file records real copy events rather than filling up with no-ops.

Reading the metadata

layer_meta() prints a layer’s provenance, licence, and contact details, and returns the parsed fields invisibly:

layer_meta("fab_dem")
<sciSpatialR metadata>
  file: //ABMI-DATA2/science/spatial_data/elevation/fab_dem/readme.txt
  Title       FABDEM – Forest And Buildings Removed Copernicus Global DEM…
  Abstract    FABDEM is a global, bare-earth digital elevation model deri…
  Purpose     To provide a globally consistent, near–bare-earth digital e…
  Topic       geoscientificInformation
  Keywords    digital elevation model, DEM, bare-earth, terrain, topograp…
  Resolution  3.23 arc-seconds (~100 m at the equator)
  Published   2022-01-01
  Start       2010-01-01
  End         2018-12-31
  Lineage     FABDEM was derived from the Copernicus GLO-30 Digital Eleva…
  Format      GeoTIFF
  Size        Global dataset; size varies by tile (1° × 1° tiles grouped …
  Use         Creative Commons Attribution–NonCommercial–ShareAlike 4.0 I…
  Access      None. Data is publicly available subject to license terms.
  Contact     Laurence Hawker
  DOI         10.1088/1748-9326/ac4d4f
  Source      https://doi.org/10.1088/1748-9326/ac4d4f
  Updated     2026-01-23

Values are truncated for display only; the returned object holds them in full. Fields the readme left blank are omitted from the printout — here Credits and Email, which check_metadata() reports below.

Every Label: value line in the readme is captured, not just the template fields, so anything a readme records is reachable. Labels become snake_case keys, and sub-fields indented under a parent are prefixed with it:

md <- layer_meta("fab_dem", print = FALSE)
md$use_constraints
md$extent_west_bounding_coordinate
md$temporal_extent_start_date

read_metadata() does the same for a readme you name by path, and as_metadata_row() flattens the result to a one-row data.frame with the numeric fields coerced — resolution to metres, bounding coordinates to decimal degrees, dates to a year:

md <- read_metadata("path/to/readme.txt")
as_metadata_row(md)

Unfilled template placeholders ([Data Title]) and non-values ("Not Specified") are returned as NA rather than as text, so a copied-but-unedited readme reads as missing rather than documented.

Auditing the metadata

check_metadata() scores every layer against the required template fields and lists the data folders that have no readme at all:

28 layers audited, 8 with no readme

 id                                                      n_missing complete missing                                      
 …here/climate_na/fab_dem_us_canada_int/Normal_1991_2020 NA        0.000    readme                                       
 …yAtmosphere/climate_na/fab_dem_us_canada_int/Year_2000 NA        0.000    readme                                       
 …yAtmosphere/climate_na/fab_dem_us_canada_int/Year_2005 NA        0.000    readme                                       
 …yAtmosphere/climate_na/fab_dem_us_canada_int/Year_2010 NA        0.000    readme                                       
 …yAtmosphere/climate_na/fab_dem_us_canada_int/Year_2015 NA        0.000    readme                                       
 …yAtmosphere/climate_na/fab_dem_us_canada_int/Year_2020 NA        0.000    readme                                       
 …yAtmosphere/climate_na/fab_dem_us_canada_int/Year_2024 NA        0.000    readme                                       
 inlandWaters/archydro2_merged                           NA        0.000    readme                                       
 biota/grassland_inventory                                8        0.579    purpose, xmin, xmax, ymin, ymax, use_constra…
 boundaries/alberta                                       7        0.632    abstract, credits, resolution, use_constrain…
 location/GRID1SQKM_AB2020_gdb                            7        0.632    abstract, credits, publication_date, use_con…
 imageryBaseMapsEarthCover/scanfi_v1.2                    6        0.684    purpose, xmin, xmax, ymin, ymax, lineage     
 biota/natural_regions_subregions_of_alberta              4        0.789    xmin, xmax, ymin, ymax                       
 elevation/fab_dem                                        2        0.895    credits, contact_email                       
 geoscientificInformation/soilgrids_250_v2_ab/abmi1km     2        0.895    access_constraints, contact_email            
 location/GRID1SQKM_AB2020_raster                         2        0.895    credits, access_constraints                  
 transportation/government_of_alberta_access_layers       2        0.895    resolution, contact_name                     
 elevation/nrcan_mrdem_dsm                                1        0.947    access_constraints                           
 elevation/nrcan_mrdem_dtm                                1        0.947    access_constraints                           
 elevation/nrcan_mrdem_dtm_hillshade                      1        0.947    access_constraints                           
 geoscientificInformation/hihydrosoil_v2_ab/abmi1km       1        0.947    contact_email                                
 inlandWaters/dynamicSurfaceWaterMaps                     1        0.947    resolution                                   
 elevation/geomorpho90                                    0        1.000                                                 
 …seMapsEarthCover/landsat_summer_mean_indices_2000_2024  0        1.000                                                 
 …BaseMapsEarthCover/modis_land_cover_dynamics_2001_2023  0        1.000                                                 
 inlandWaters/archydro2                                   0        1.000                                                 
 inlandWaters/hydrologically adjusted elevations          0        1.000                                                 
 inlandWaters/topographic_wetness_index                   0        1.000                                                 

(Abridged; folders with no readme sort first, then the least complete readmes.) Rows with missing = "readme" are folders holding spatial data that the catalogue cannot see at all — they are the first thing to fix. Deep ids are trimmed from the front when printed, since the last segment is what distinguishes one from another; the returned table holds them in full.

Nothing under _temp or _deprecated is audited either, so staging exports and retired products are not counted as undocumented data. The audit stays a list of real gaps.

Use detail = TRUE for one row per missing field, which is easier to tabulate:

check_metadata(detail = TRUE)
check_metadata(theme = "elevation", detail = TRUE)
5 missing fields across 4 layers

 id                                  theme     field             
 elevation/fab_dem                   elevation credits           
 elevation/fab_dem                   elevation contact_email     
 elevation/nrcan_mrdem_dsm           elevation access_constraints
 elevation/nrcan_mrdem_dtm           elevation access_constraints
 elevation/nrcan_mrdem_dtm_hillshade elevation access_constraints

Both forms print the compact view above. Pass verbose = FALSE for the plain data.frame instead — every column, and the missing list in full rather than truncated to the width of the printout:

gaps <- check_metadata(verbose = FALSE)
gaps$missing[gaps$id == "biota/grassland_inventory"]
#> [1] "purpose, xmin, xmax, ymin, ymax, use_constraints, access_constraints, contact_email"

Caching

Scanning a network share is the slow step, so the manifest is built once per session and reused. After editing a readme or adding data, rescan:

build_catalogue(refresh = TRUE)

build_catalogue(files = FALSE) skips the file inventory for a faster, metadata-only scan; n_files, size_mb, and data_type are then NA. Any argument accepted by build_catalogue() can be passed through the query functions:

list_layers(root = "D:/spatial_data", refresh = TRUE)

Adding a field to the template

Readmes are parsed generically rather than against a fixed schema, so a field you add to the template is readable immediately, with no change to the package. Every Label: value line is captured, and a sub-field indented under a parent label is namespaced by it. Given this block:

Coordinate Reference System:
    Name: NAD83 / Alberta 10-TM (Forest)
    Authority Code: EPSG:3400
    Datum: North American Datum 1983
    Projection: Transverse Mercator
    Vertical CRS: CGVD2013

every part is reachable at once:

md <- layer_meta("some_layer", print = FALSE)
md$coordinate_reference_system_authority_code
#> [1] "EPSG:3400"
md$coordinate_reference_system_projection
#> [1] "Transverse Mercator"

What is not automatic is becoming a column of the manifest, and so a filter in find_layer(). That takes one edit: add the parsed key to .meta_field_map in R/metadata.R. Adding it to .meta_required in the same file makes check_metadata() count it towards completeness. R/catalogue.R builds its columns from that map and needs no change.