尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證SWIG-based GDAL Bindings for R
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗The goal of GDAL7 is to model the GDAL API in R, generated from GDAL's own SWIG interface definitions and bound with cpp11 and S7.
You need GDAL with its development headers (gdal-config on its PATH,
or pkg-config able to find gdal), then:
remotes::install_github("rgdal-dev/GDAL7")
Or from a clone:
git clone https://github.com/rgdal-dev/GDAL7
R CMD INSTALL GDAL7
The cpp11 registration files (src/cpp11.cpp, R/cpp11.R) are
committed, so no generation step is needed to install. You only need
cpp11::cpp_register() after adding or changing a [[cpp11::register]]
function; data-raw/orchestrate.R does that as part of regenerating the
bindings.
The examples below run against a small GeoTIFF that ships with the package, so they need no network.
What an object knows about itself is a property, read from GDAL at the moment it is asked for rather than copied when the object was made, and settable by assignment where GDAL lets it be set. What an object does, and anything that needs an argument, is a function.
library(GDAL7)
dsn <- system.file("extdata/test.tif", package = "GDAL7")
ds <- gdal_open(dsn)
#### What every dataset, band and driver carries
basename(ds@description) # what the object calls itself
#> [1] "test.tif"
ds@metadata_domain_list # the metadata domains it has
#> [1] "IMAGE_STRUCTURE" "DERIVED_SUBDATASETS" ""
get_metadata_list(ds, "") # metadata as KEY=VALUE strings
#> [1] "AREA_OR_POINT=Area"
get_metadata_dict(ds, "") # the same, as a named vector
#> AREA_OR_POINT
#> "Area"
get_metadata_item(ds, "AREA_OR_POINT") # one item, NA when not set
#> [1] "Area"
#### What a dataset knows
substr(ds@crs, 1, 40) # the CRS as WKT2
#> [1] "GEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodeti"
substr(ds@projection, 1, 40) # GDAL's own WKT1 spelling
#> [1] "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROI"
ds@geotransform # pixel and line to x and y
#> origin_x pixel_width row_rotation origin_y column_rotation
#> -180 18 0 90 0
#> pixel_height
#> -18
basename(ds@file_list) # the files it is made of
#> [1] "test.tif"
ds@gcp_count
#> [1] 0
ds@layer_count # vector layers, none here
#> [1] 0
c(ds@raster_xsize, ds@raster_ysize, ds@raster_count)
#> [1] 20 10 2
#### Bands
# A band is taken by number, so that is a function; what it then knows about
# itself is a property.
band <- get_raster_band(ds, 1L)
band
#> <GDALRasterBand>
#> Band: 1
#> Size: 20 x 10
#> Type: Int16
#> Block size: 20 x 10
#> NoData: -32768
#> Color: Gray
band@data_type_name
#> [1] "Int16"
band@block_size
#> x y
#> 20 10
band@nodata_value
#> [1] -32768
A band belongs to its dataset, so closing the dataset retires the band with it. Reaching for one afterwards is an error rather than a crash:
gdal_close(ds)
band@xsize
#> Error:
#> ! This GDALRasterBand cannot be used: the GDALDataset it belongs to has been closed
A read names a window and, separately, the size to return it at. GDAL picks an overview level that can serve the output size, so a large window at a small output size costs only the bytes of that level. The fixture here is a small COG with two overview levels.
ds <- gdal_open(system.file("extdata/overviews.tif", package = "GDAL7"))
band <- get_raster_band(ds, 1)
band@overview_sizes
#> xsize ysize
#> 1 256 128
#> 2 128 64
# 512x256 down to 4x2, averaged.
read_raster(band, out_size = c(4, 2), resample = "average")
#> [1] 318 446 574 702 830 958 1086 1214
# An 8x8 window down to 2x2. The window may be fractional.
read_raster(band, window = c(0, 0, 8, 8), out_size = c(2, 2),
resample = "average")
#> [1] 8 12 24 28
gdal_close(ds)
gdal_info() gathers everything gdalinfo reports that does not need a
pass over the pixels, in a single call rather than one per property:
info <- gdal_info(system.file("extdata/overviews.tif", package = "GDAL7"))
info$size
#> xsize ysize
#> 512 256
info$geotransform
#> [1] -180.000000 0.703125 0.000000 90.000000 0.000000 -0.703125
info$band_info
#> band type block_x block_y nodata scale offset color unit overviews
#> 1 1 Int16 128 128 -32768 NA NA Gray 2
Any DSN GDAL understands works, which is the point of the /vsicurl/
and service drivers: no download step, and only the bytes actually
needed are read.
gebco <- "/vsicurl/https://data.source.coop/alexgleith/gebco-2024/GEBCO_2024.tif"
ds <- gdal_open(gebco)
c(ds@raster_xsize, ds@raster_ysize)
get_raster_band(ds, 1L)@block_size
gdal_close(ds)
wmts <- paste0(
"WMTS:https://services.arcgisonline.com/arcgis/rest/services/",
"World_Imagery/MapServer/WMTS/1.0.0/WMTSCapabilities.xml,layer=World_Imagery"
)
ds <- gdal_open(wmts)
ds@metadata_domain_list
gdal_close(ds)
NetCDF, Zarr and HDF5 hold arrays of any number of dimensions rather than bands of pixels. GDAL7 navigates them and reads them.
ds <- gdal_open(system.file("extdata/multidim.zarr", package = "GDAL7"),
multidim = TRUE)
grp <- get_root_group(ds)
grp
#> <GDALGroup>
#> Name: /
#> Arrays (4): lon, temperature, lat, time
arr <- open_mdarray(grp, "temperature")
arr
#> <GDALMDArray>
#> Name: temperature
#> Type: Float64
#> Dimensions: time=3, lat=4, lon=5
#> Unit: degC
#> NoData: -999
arr@dimensions
#> name size type direction indexed
#> 1 time 3 TEMPORAL TRUE
#> 2 lat 4 HORIZONTAL_Y NORTH TRUE
#> 3 lon 5 HORIZONTAL_X EAST TRUE
A read takes an origin, a count along each dimension and a step. The
dim of what comes back is the reverse of the array's own dimension
order, and carries the dimension names, so there is never a question of
which axis is which.
values <- read_mdarray(arr)
dim(values)
#> lon lat time
#> 5 4 3
# The first time step, every second longitude.
read_mdarray(arr, start = c(1, 1, 1), count = c(1, 4, 3), step = c(1, 1, 2))
#> , , 1
#>
#> [,1] [,2] [,3] [,4]
#> [1,] 1 6 11 16
#> [2,] 3 NA 13 18
#> [3,] 5 10 15 20
Where the values sit, and what the format says about them:
arr@dimension_values
#> $time
#> [1] 0 1 2
#>
#> $lat
#> [1] -40 -41 -42 -43
#>
#> $lon
#> [1] 140 141 142 143 144
arr@attributes
#> $coordinates
#> [1] "lat lon"
#>
#> $long_name
#> [1] "air temperature"
#>
#> $valid_range
#> [1] -50 50
A view slices lazily, in GDAL's own syntax, and a two-dimensional array can be handed to the raster side of the package as an ordinary dataset.
first <- get_view(arr, "[0,:,:]")
first@dimensions
#> name size type direction indexed
#> 1 lat 4 HORIZONTAL_Y NORTH TRUE
#> 2 lon 5 HORIZONTAL_X EAST TRUE
raster <- as_classic_dataset(first)
c(raster@raster_xsize, raster@raster_ysize)
#> [1] 5 4
gdal_close(ds)
With the altarr package
installed, as_altarr() turns an array, a path and an array name, or a
band into an ordinary R array whose values stay in the source until
something reads them. Base R indexing and reductions read it a batch of
chunks at a time, and each batch is one GDAL read, announced first with
advise_read() so that Zarr decodes the batch's chunks on GDAL's
threads at once. The chunks are the source's own, which an array reports
as block_size:
zarr <- system.file("extdata/multidim.zarr", package = "GDAL7")
ds <- gdal_open(zarr, multidim = TRUE)
open_mdarray(get_root_group(ds), "temperature")@block_size
#> time lat lon
#> 1 4 5
gdal_close(ds)
x <- as_altarr(zarr, array = "temperature")
dim(x)
#> [1] 5 4 3
x[2, 3, ] # one cell, every time step
#> [1] 12 32 52
sum(x, na.rm = TRUE)
#> [1] 1822
altarr::altarr_stats(x)[["fetch_calls"]]
#> [1] 3
Built from a path, the array saves with saveRDS() as a recipe of about
a kilobyte, and reopens its source when it is first read in a new
session. A band works the same way, and an overview is a band, so
as_altarr(get_overview(band, 1)) is a lazy view of a reduced level.
A whole layer arrives as a data frame in one call, through the column-oriented Arrow path GDAL added in 3.6, so there is no per-feature work in R at all. Geometry comes back as WKB, in a list column of raw vectors:
gpkg <- system.file("extdata/test.gpkg", package = "GDAL7")
ds <- gdal_open(gpkg)
ds@layers
#> name geometry_type feature_count fast_arrow
#> 1 places Point 5 TRUE
places <- read_vector(ds)
places[c("name", "population")]
#> name population
#> 1 Hobart 247086
#> 2 Melbourne 5031195
#> 3 Sydney 5312163
#> 4 Perth 2141834
#> 5 Darwin 147255
gdal_close(ds)
Filters are set on the layer and apply to everything read afterwards. A spatial filter is what makes a large layer cheap, because a driver with a spatial index uses it rather than reading every feature:
ds <- gdal_open(gpkg)
layer <- get_layer(ds, "places")
set_filter(layer, where = "population > 1e6", bbox = c(140, -45, 155, -30))
read_vector(layer)$name
#> [1] "Melbourne" "Sydney"
read_vector(execute_sql(ds, "SELECT name FROM places ORDER BY name LIMIT 2"))
#> OGC_FID name
#> 1 1 Darwin
#> 2 2 Hobart
gdal_close(ds)
The same path runs the other way, so a data frame with a WKB column writes back out as a layer:
path <- tempfile(fileext = ".gpkg")
write_vector(places, path, layer = "places", crs = "EPSG:4326",
geometry_type = "Point")
identical(read_vector(path), places)
#> [1] TRUE
unlink(path)
A dataset is made with a size, a band count and a type, written into, and closed. Closing is what finishes the file.
path <- tempfile(fileext = ".tif")
ds <- gdal_create(path, 64, 48, bands = 1, type = "Float32")
ds@crs <- "EPSG:4326"
ds@geotransform <- c(-180, 360 / 64, 0, 90, 0, -180 / 48)
write_raster(ds, list(as.double(seq_len(64 * 48))))
gdal_close(ds)
info <- gdal_info(path)
info$size
#> xsize ysize
#> 64 48
info$band_info[c("band", "type", "block_x", "block_y")]
#> band type block_x block_y
#> 1 1 Float32 64 32
substr(info$projection, 1, 30)
#> [1] "GEOGCS[\"WGS 84\",DATUM[\"WGS_198"
Creation options are a table, read out of the driver's own metadata, rather than a string of XML to parse or a list of names to remember:
options <- driver_options("GTiff")
options[options$name %in% c("COMPRESS", "TILED", "BLOCKXSIZE"),
c("name", "type", "default")]
#> name type default
#> 1 COMPRESS string-select <NA>
#> 20 TILED boolean NO
#> 24 BLOCKXSIZE int 256
options$choices[[which(options$name == "COMPRESS")]][1:6]
#> [1] "NONE" "LZW" "PACKBITS" "JPEG" "CCITTRLE" "CCITTFAX3"
They are asked about before anything is made, so a name the driver does not have, or a value it will not take, is an error rather than a file left behind:
validate_creation_options("GTiff", c(COMPRESS = "DEFLATE"))
#> [1] TRUE
validate_creation_options("GTiff", c(COMPRES = "DEFLATE"))
#> [1] FALSE
gdal_create(tempfile(fileext = ".tif"), 4, 4,
options = c(COMPRESS = "NOT_A_CODEC"))
#> Error:
#> ! The GTiff driver does not take these creation options: 'NOT_A_CODEC' is an unexpected value for COMPRESS creation option of type string-select.
gdal_create_copy() puts an existing dataset through a driver, which is
how a COG gets made: the driver builds the overviews and lays the file
out itself.
cog_path <- tempfile(fileext = ".tif")
cog <- gdal_create_copy(path, cog_path, driver = "COG",
options = c(COMPRESS = "DEFLATE", BLOCKSIZE = "128"),
progress = FALSE)
gdal_close(cog)
reopened <- gdal_open(cog_path)
get_metadata_dict(reopened, "IMAGE_STRUCTURE")
#> LAYOUT COMPRESSION INTERLEAVE
#> "COG" "DEFLATE" "BAND"
get_raster_band(reopened, 1)@block_size
#> x y
#> 128 128
gdal_close(reopened)
gdal_drivers() says which drivers can do which of those things, in one
pass over the driver manager:
# Raster drivers that can make a dataset from nothing, rather than only copy
# an existing one.
drivers <- gdal_drivers(c("DCAP_RASTER", "DCAP_CREATE"))
nrow(drivers)
#> [1] 36
head(drivers[c("short_name", "copy", "vsi", "extensions")], 5)
#> short_name copy vsi extensions
#> 1 GTiff TRUE TRUE tif tiff
#> 2 VRT TRUE TRUE vrt
#> 3 NITF TRUE TRUE ntf
#> 4 HFA TRUE TRUE img
#> 5 MEM FALSE FALSE <NA>
Every GDAL path works, not only files on disk, and they compose. A
dataset built at a /vsimem/ path never reaches the disk, and its bytes
come back as raw:
ds <- gdal_create("/vsimem/small.tif", 4, 4, bands = 1, type = "Byte")
write_raster(ds, list(as.double(seq_len(16))))
gdal_close(ds)
vfs_stat("/vsimem/small.tif")$size
#> [1] 162
bytes <- vfs_read_file("/vsimem/small.tif")
rawToChar(bytes[1:2])
#> [1] "II"
vfs_unlink("/vsimem/small.tif")
GDAL reads its configuration options at the moment it needs them, so
setting one changes how the next call behaves. with_gdal_config() sets
them for one expression and puts back exactly what was there:
with_gdal_config(c(GDAL_CACHEMAX = "16"), gdal_config("GDAL_CACHEMAX"))
#> [1] "16"
gdal_config("GDAL_CACHEMAX")
#> NULL
GDAL 3.11 added a registry of the algorithms its own command line is
built from. GDAL7 binds the registry rather than each utility, so what
gdal can do arrives with GDAL rather than with a GDAL7 release.
gdal_has_algorithms() says whether this build has any. Having the API
is not the same as having the algorithms: a GDAL can be built with them
turned off, and before 3.12 they registered themselves as a side effect
of being loaded, which a static link leaves out. The Windows build here
links GDAL statically, and answers FALSE today; Linux and macOS have the
algorithms.
gdal_algorithms()
#> [1] "convert" "dataset" "info" "mdim" "pipeline" "raster" "vector"
#> [8] "vsi"
head(gdal_algorithms("raster"), 12)
#> [1] "as-features" "aspect" "blend" "calc" "clean-collar"
#> [6] "clip" "color-map" "compare" "contour" "convert"
#> [11] "create" "edit"
Every algorithm describes itself, so there is no table of arguments in this package to fall out of date.
info <- gdal_algorithm_info("raster reproject")
info$description
#> [1] "Reproject a raster dataset."
head(info$arguments[c("name", "type", "required")], 8)
#> name type required
#> 1 help boolean FALSE
#> 2 help-doc boolean FALSE
#> 3 json-usage boolean FALSE
#> 4 config string_list FALSE
#> 5 input-format string_list FALSE
#> 6 open-option string_list FALSE
#> 7 input dataset_list TRUE
#> 8 quiet boolean FALSE
Arguments go in by name. An algorithm told to write to memory hands back a dataset and touches no disk; one given a file name writes it, closes it and hands back the path.
ds <- gdal_open(system.file("extdata/test.tif", package = "GDAL7"))
reprojected <- gdal_run("raster reproject", list(
input = ds,
output = "",
"output-format" = "MEM",
"dst-crs" = "EPSG:3857",
bbox = c(-180, -85, 180, 85),
"bbox-crs" = "EPSG:4326"
), progress = FALSE)
c(reprojected@raster_xsize, reprojected@raster_ysize)
#> [1] 19 19
substr(reprojected@projection, 1, 40)
#> [1] "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS"
gdal_close(reprojected)
gdal_close(ds)
Pipelines work the same way, with the steps written as GDAL writes them:
piped <- gdal_run("raster pipeline", list(
pipeline = paste(
"read", system.file("extdata/overviews.tif", package = "GDAL7"),
"! reproject --dst-crs EPSG:3857 --resampling average",
"! write --output-format MEM streamed"
)
), progress = FALSE)
#> Warning: GDAL: Clamping output bounds to (-20037508.342789,-20037508.342789) ->
#> (20037508.342789, 20037508.342789)
c(piped@raster_xsize, piped@raster_ysize)
#> [1] 497 497
gdal_close(piped)
A long algorithm draws a progress bar and stops on Ctrl-C.
The enumerators and metadata keys that GDAL declares come through as two named vectors, rather than two hundred exported names:
gdal_constants("GDT_")[1:6]
#> GDT_Unknown GDT_Byte GDT_Int8 GDT_UInt16 GDT_Int16 GDT_UInt32
#> 0 1 14 2 3 4
gdal_string_constants("DCAP_")[1:3]
#> DCAP_OPEN DCAP_CREATE
#> "DCAP_OPEN" "DCAP_CREATE"
#> DCAP_CREATE_MULTIDIMENSIONAL
#> "DCAP_CREATE_MULTIDIMENSIONAL"
A few bindings call GDAL functions newer than the minimum GDAL7
requires. They always exist and always dispatch; calling one that the
GDAL in use is too old for raises an error naming the release it needs.
gdal7_capabilities() is how to ask first:
gdal_release()[["release"]]
#> [1] "3.12.4"
gdal7_capabilities()
#> binding gdal available
#> 1 dataset_mark_suppress_on_close 3.12.0 TRUE
#> 2 dataset_get_close_reports_progress 3.13.0 FALSE
#> 3 dataset_as_mdarray 3.12.0 TRUE
#> 4 dataset_is_thread_safe 3.10.0 TRUE
#> 5 dataset_get_thread_safe_dataset 3.10.0 TRUE
Please note that the GDAL7 project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.