Downloads files from URLs and converts each into a layer within a GeoPackage
using the ogr2ogr command-line tool from GDAL.
Usage
spk_source_url(
path_gpkg,
urls,
query = NULL,
layer = NULL,
open_options = NULL,
a_srs = NULL,
t_srs = NULL,
encoding = NULL,
vsi = "curl"
)Arguments
- path_gpkg
character Path to the output GeoPackage. Created if it does not exist; its parent directory must exist.
- urls
character URLs of the files to download and convert.
- query
character or NULL Optional SQL filter applied during conversion via
-where. IfNULL(default), no filter is applied.- layer
character or NULL Optional output layer name(s). If
NULL(default), each layer is named for the file name of its URL. If supplied, must be the same length asurls.- open_options
character or NULL Optional GDAL open options as
"KEY=VALUE"strings, each passed as a separate-oo.- a_srs
character or NULL Optional CRS to assign to a source that carries none, e.g.
"EPSG:4326".- t_srs
- encoding
character or NULL Optional source encoding, e.g.
"UTF-16LE". When supplied the file is fetched and re-encoded to UTF-8 before conversion. Cannot be combined with a non-defaultvsi.- vsi
character GDAL virtual filesystem used to read each URL.
"curl"(default) reads through/vsicurl/, which probes withHEADand reads with range requests."curl_streaming"reads through/vsicurl_streaming/, a single sequentialGET— required for a service endpoint that supports neither, such as a WFSGetFeatureURL. See Reading a service endpoint below.
Details
Sources are read through a GDAL virtual filesystem, so anything ogr2ogr can open
over HTTP works — FlatGeobuf, GeoJSON, GeoPackage, shapefile archives and plain CSV
among them. Which filesystem is used is chosen with vsi.
A non-spatial CSV carrying coordinate columns becomes a point layer by passing the relevant GDAL open options and assigning a CRS:
open_options = c("X_POSSIBLE_NAMES=Longitude",
"Y_POSSIBLE_NAMES=Latitude",
"KEEP_GEOM_COLUMNS=NO"),
a_srs = "EPSG:4326"Without them the file still imports, as an attribute table with no geometry.
a_srs assigns a CRS to a source that carries none; t_srs reprojects a source
that already has one. They are not interchangeable.
GDAL's CSV driver has no general encoding open option, so a source that is not UTF-8
cannot be fixed with open_options. Pass encoding instead and the file is fetched
and re-encoded before ogr2ogr sees it, rather than streamed through a virtual
filesystem. Because that path never reaches one, encoding and a non-default vsi
are refused together.
Canadian federal open data often ships bilingual, slash-separated column headers —
Site/Site, Year/Année. Verified against GDAL 3.x: both the slash and the accent
survive into the GeoPackage exactly, so ogrinfo reports the fields as Site/Site
and Year/Année.
They do not survive the trip back into R unchanged, though. sf::st_read() returns a
data frame, and R makes column names syntactic — the slash becomes a dot
(Site.Site) while the accent is kept (Year.Année). So the name in the file and the
name in your session differ, and a downstream SQL query against this layer must use
the file's name, quoted.
Reading a service endpoint
/vsicurl/ — the default — probes a source with a HEAD request and reads it with
HTTP range requests. A service endpoint addressed by a query string typically
supports neither, and the open fails before any driver is tried:
Measured against a BC Geographic Warehouse WFS on GDAL 3.13.0: HEAD returns 404, and
a Range request is answered with a full-body 200 rather than a 206. Forcing the
driver (-if GeoJSON, GeoJSON:/vsicurl/...) does not help, because the failure is in
the probe rather than in driver detection.
vsi = "curl_streaming" reads the source with a single sequential GET and needs
neither capability. Reach for it when a URL carries a query string, has no file
extension, or names an OGC service.
"curl_streaming" is not strictly better, so it is not the default. It reads
sequentially and does not cache, so a format that needs to seek — a GeoPackage or
zipped shapefile over HTTP, a FlatGeobuf spatial index — will re-request or degrade
badly under it. Use it for a sequentially-readable payload (GeoJSON, CSV) from an
endpoint /vsicurl/ cannot probe, and leave the default in place otherwise.
A query-string URL also has no usable file name — basename() of a GetFeature
request returns the whole query string — so layer is required in that case. That
guard covers the query-string shape only; other URLs still derive a name that may be
poor (https://example.com yields example), so pass layer whenever the name
matters.
See also
spk_geoserv_dlv() for pulling a layer from a GeoServer WFS endpoint. It
builds the request from parts and writes a GeoJSON file to a directory; this
function takes an already-built URL straight into a GeoPackage layer.
Other download:
spk_source_bcdata()
Examples
if (FALSE) { # \dontrun{
path_gpkg <- fs::path(tempdir(), "layers.gpkg")
# a coordinate CSV published as UTF-16, landing as a point layer
spk_source_url(
path_gpkg = path_gpkg,
urls = "https://example.org/opendata/sites.csv",
layer = "sites",
open_options = c(
"X_POSSIBLE_NAMES=Longitude",
"Y_POSSIBLE_NAMES=Latitude",
"KEEP_GEOM_COLUMNS=NO"
),
a_srs = "EPSG:4326",
encoding = "UTF-16LE"
)
# a WFS GetFeature endpoint: /vsicurl/ cannot open this, and the query string leaves
# no usable name to derive, so `layer` is required
lyr <- "WHSE_BASEMAPPING.FWA_WATERSHED_GROUPS_POLY"
spk_source_url(
path_gpkg = path_gpkg,
urls = paste0(
"https://openmaps.gov.bc.ca/geo/pub/", lyr, "/ows",
"?service=WFS&version=2.0.0&request=GetFeature",
"&typeName=pub%3A", lyr,
"&outputFormat=application%2Fjson&srsName=EPSG%3A3005&count=1"
),
layer = "watershed_groups",
vsi = "curl_streaming"
)
} # }
