| Title: | Shared Loader Utilities for ecospg |
|---|---|
| Description: | Extracts duplicated R code from ecospg data loaders into a reusable package. Provides database connection, catalog registration, partitioned table management, staging/ingest, S3 upload, and CLI argument parsing utilities. |
| Authors: | Lars Dalby [aut, cre] |
| Maintainer: | Lars Dalby <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.6.0 |
| Built: | 2026-10-20 10:22:06 UTC |
| Source: | https://gitlab.au.dk/ecos/tools/r-pkgs/ecospgr |
source_url is stored in meta.dataset_version.source_meta, which
warehouse_reader can read, so any query string is stripped here — a
download URL that carries an API key must not become catalog metadata.
epg_build_source_meta(source_url, metadata_url = NULL, extra = list())epg_build_source_meta(source_url, metadata_url = NULL, extra = list())
source_url |
The source URL for the download. |
metadata_url |
Optional metadata URL. |
extra |
Named list of additional metadata fields to merge. |
A named list of source metadata.
Upstream services silently cap responses (WFS at count, ArcGIS at
maxRecordCount), so a load can succeed while holding only part of the
data. Call this with the count the source reported before publishing.
epg_check_feature_count(n_loaded, n_expected, tolerance = 0, what = "dataset")epg_check_feature_count(n_loaded, n_expected, tolerance = 0, what = "dataset")
n_loaded |
Features actually loaded. |
n_expected |
Features the source reported, or |
tolerance |
Fraction of |
what |
Label used in messages. |
TRUE invisibly when the count is acceptable.
Reads optional JSON from a file path and/or an inline JSON string and merges them (inline takes precedence over file).
epg_collect_source_meta_overrides(json_file_path = NULL, json_inline = NULL)epg_collect_source_meta_overrides(json_file_path = NULL, json_inline = NULL)
json_file_path |
Path to a JSON file (or NULL). |
json_inline |
Inline JSON string or |
A named list of override fields (may be empty).
Uses glue_sql() for safe SQL interpolation. Identifier components are
validated to contain only safe characters (alphanumeric + underscore).
epg_create_partition(con, dataset, ver_id, geom_type = "MultiPolygon")epg_create_partition(con, dataset, ver_id, geom_type = "MultiPolygon")
con |
A DBI connection. |
dataset |
Dataset name (used as part of table names in |
ver_id |
The dataset_version.id (integer). |
geom_type |
Geometry type constraint for the partition. Defaults to
|
Looks for schemaLocation hints in the GML header, then falls back to filename-based matching and sibling XSD files.
epg_detect_xsd_for_gml(gml_path)epg_detect_xsd_for_gml(gml_path)
gml_path |
Path to a GML file. |
Path to the XSD file, or NA_character_ if none found.
Returns a named list of credential query parameters for Datafordeler URLs.
Prefers API-key auth (apikey=) when DFD_API_KEY is set; falls back to
legacy username/password (username=…&password=…). Errors if neither is
available.
epg_dfd_auth_params( api_key = Sys.getenv("DFD_API_KEY", ""), username = Sys.getenv("DFD_USERNAME", ""), password = Sys.getenv("DFD_PASSWORD", "") )epg_dfd_auth_params( api_key = Sys.getenv("DFD_API_KEY", ""), username = Sys.getenv("DFD_USERNAME", ""), password = Sys.getenv("DFD_PASSWORD", "") )
api_key |
API key (default |
username |
Legacy service-user name (default |
password |
Legacy service-user password (default |
Named list with either apikey OR username + password.
Returns a URL-encoded query-string fragment for the credentials returned by
epg_dfd_auth_params(), e.g. "apikey=abc123" or
"username=u&password=p". Does not include a leading ? or &.
epg_dfd_auth_qs(...)epg_dfd_auth_qs(...)
... |
Passed to |
Character scalar.
Drops the version's partition (vector.<dataset>_v<id>), any staging table
a failed run left behind, and its meta.dataset_version row, all in one
transaction. Use it to take a bad load — truncated, duplicated, loaded with
a stale loader — out of the warehouse before re-loading the same tag.
epg_drop_version( con, topic, dataset, version_tag, on_latest = c("error", "previous", "clear") )epg_drop_version( con, topic, dataset, version_tag, on_latest = c("error", "previous", "clear") )
con |
A DBI connection. |
topic |
Topic name. |
dataset |
Dataset name. |
version_tag |
Tag of the version to remove. |
on_latest |
What to do when the version is the latest one:
|
If rows for the version survive anywhere in vector.<dataset> (a partition
not named by the pipeline's convention, say), the foreign key from the data
to meta.dataset_version refuses the delete and the whole transaction rolls
back, so a version is never removed from the catalog while its rows remain.
Removing the version that vector.<dataset>_latest serves would leave that
view empty, so on_latest must say what happens instead.
COGs in raster.cog_asset are kept: that table records its source version
without a foreign key, so a COG stays a valid historical artefact. They are
reported, so you know to rebuild them.
A list with version_id, rows (rows removed), was_latest, and
latest (the tag the latest flag was moved to, or NA if it was not
moved), invisibly.
Errors if topic does not exist, rather than silently inserting nothing
and letting a later step fail with an opaque message.
epg_ensure_dataset(con, topic, dataset)epg_ensure_dataset(con, topic, dataset)
con |
A DBI connection. |
topic |
Topic name (must already exist). |
dataset |
Dataset name. |
Ensure a topic row exists in meta.topic
epg_ensure_topic(con, topic, description = NULL)epg_ensure_topic(con, topic, description = NULL)
con |
A DBI connection. |
topic |
Topic name. |
description |
Optional topic description. |
Finalize a dataset version: mark latest and drop staging
epg_finalize_version( con, topic, dataset, version_tag, stage_tbl, set_latest = TRUE )epg_finalize_version( con, topic, dataset, version_tag, stage_tbl, set_latest = TRUE )
con |
A DBI connection. |
topic |
Topic name. |
dataset |
Dataset name. |
version_tag |
The version tag string. |
stage_tbl |
The qualified staging table name to drop. |
set_latest |
Whether to mark this version as latest. Default |
Applies iconv() only to strings that are not already valid UTF-8.
Converting unconditionally double-encodes correct UTF-8 — ø (C3 B8)
becomes ø — and because Latin-1 has no invalid bytes, iconv() never
errors to warn you. It just corrupts.
epg_fix_encoding(x, from = "latin1")epg_fix_encoding(x, from = "latin1")
x |
A character vector. |
from |
Source encoding assumed for invalid strings. |
x with invalid entries re-decoded.
Insert normalized geometries from staging into a vector partition
epg_insert_normalized( con, dataset, ver_id, stage_tbl, geom_transform = "ST_Multi(ST_CollectionExtract(ST_MakeValid(s.geom), 3))::geometry(MultiPolygon,25832)", delete_before_insert = FALSE )epg_insert_normalized( con, dataset, ver_id, stage_tbl, geom_transform = "ST_Multi(ST_CollectionExtract(ST_MakeValid(s.geom), 3))::geometry(MultiPolygon,25832)", delete_before_insert = FALSE )
con |
A DBI connection. |
dataset |
Dataset name (parent table is |
ver_id |
The dataset_version.id. |
stage_tbl |
The qualified staging table name (e.g., |
geom_transform |
SQL expression for geometry normalization. Defaults to
the standard MultiPolygon transform. Use
|
delete_before_insert |
If |
The number of rows inserted (invisible).
Returns the value following flag. A flag that is absent yields default.
A flag that is present but malformed — repeated, or given without a value —
is an error rather than a silent fallback to default, because these
loaders run unattended and a typo must not quietly load with defaults.
epg_parse_arg(flag, default = NULL, args = commandArgs(trailingOnly = TRUE))epg_parse_arg(flag, default = NULL, args = commandArgs(trailingOnly = TRUE))
flag |
The flag string (e.g., "–src-url"). |
default |
Default value if flag is not found. |
args |
Character vector of arguments. Defaults to
|
The value following the flag, or default when the flag is absent.
If value starts with @, the remainder is treated as a file path whose
contents are read and parsed as JSON.
epg_parse_json_arg(value, label)epg_parse_json_arg(value, label)
value |
A JSON string or |
label |
A human-readable label used in error messages. |
Parsed JSON as an R list, or NULL if value is empty/NULL.
Parse a JSON string
epg_parse_json_payload(payload, label)epg_parse_json_payload(payload, label)
payload |
A JSON string. |
label |
A human-readable label used in error messages. |
Parsed JSON as an R list.
Persist source metadata on a dataset version
epg_persist_source_meta(con, ver_id, source_meta)epg_persist_source_meta(con, ver_id, source_meta)
con |
A DBI connection. |
ver_id |
The dataset_version.id. |
source_meta |
A named list of metadata to store as JSONB. |
Reads PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD from the
environment. PGDATABASE, PGUSER and PGPASSWORD are required: this
package is shared across warehouses, so guessing a database or falling back
to a default superuser password risks connecting somewhere unintended.
epg_pg_connect(application_name = NULL)epg_pg_connect(application_name = NULL)
application_name |
Optional application name tag for the connection
(visible in |
A PqConnection object.
Year-vintage datasets have several compose services sharing one dataset
name and differing only by version tag (2024, 2025, 2026). Loading an
older vintage must not leave it holding is_latest.
epg_promote_latest(con, topic, dataset, year = format(Sys.Date(), "%Y"))epg_promote_latest(con, topic, dataset, year = format(Sys.Date(), "%Y"))
con |
A DBI connection. |
topic |
Topic name. |
dataset |
Dataset name. |
year |
The eligible vintage, as a 4-digit string. A version qualifies
when its tag is exactly |
The rule is "the newest eligible vintage wins". Crucially, when no eligible
version exists — which is the situation every 1 January, before the new
year's data is published — the current flag is left alone. Clearing it
instead would leave vector.<dataset>_latest returning zero rows, silently,
until someone published a new vintage.
The flag itself is moved by meta.set_latest(), so the database keeps a
single implementation of what "latest" means.
The version tag now flagged latest, or NA_character_ if the flag
was left as it was.
Read and parse a JSON file
epg_read_json_file(path, label)epg_read_json_file(path, label)
path |
Path to a JSON file. |
label |
A human-readable label used in error messages. |
Parsed JSON as an R list, or NULL if path is empty/NULL.
Replaces the value of any known credential query parameter with
REDACTED, and strips userinfo from the authority. Use this on every URL
that reaches a log message, an error, or meta.dataset_version.
epg_redact_url(url)epg_redact_url(url)
url |
A character vector of URLs. |
Unparseable input is reduced to its path, never returned verbatim, so a malformed URL cannot leak a secret through this function.
url with credential values replaced by REDACTED.
Register a new dataset version
epg_register_version( con, topic, dataset, version_tag, on_conflict = c("error", "reuse") )epg_register_version( con, topic, dataset, version_tag, on_conflict = c("error", "reuse") )
con |
A DBI connection. |
topic |
Topic name. |
dataset |
Dataset name. |
version_tag |
Version tag string. |
on_conflict |
Reusing a version id means the caller is re-loading into a partition that
may already hold rows. Callers that pass |
The dataset_version.id (integer).
Reads S3_ENDPOINT, S3_REGION, S3_BUCKET, S3_ACCESS_KEY and
S3_SECRET_KEY, the ECOS secret key names, and RAW_PREFIX for the key
prefix of raw files.
epg_resolve_s3_config(default_prefix = "")epg_resolve_s3_config(default_prefix = "")
default_prefix |
Default S3 key prefix if |
The older SCW_* and RAW_* storage names are rejected rather than read,
so a stale env file fails instead of being half-applied.
A named list with components endpoint, region, bucket, prefix,
access_key, secret_key, and configured (logical).
Convenience wrapper that calls epg_ensure_topic(), epg_ensure_dataset(),
epg_register_version(), epg_set_raw_file_url(), epg_persist_source_meta(),
epg_create_partition(), epg_write_staging(), epg_insert_normalized(),
and epg_finalize_version() inside dbWithTransaction().
epg_run_pipeline( con, sf_obj, topic, dataset, version_tag, topic_description = NULL, source_meta = NULL, raw_url = NA_character_, geom_type = "MultiPolygon", geom_transform = "ST_Multi(ST_CollectionExtract(ST_MakeValid(s.geom), 3))::geometry(MultiPolygon,25832)", delete_before_insert = FALSE, set_latest = TRUE, on_conflict_version = "error", expected_features = NA_integer_, count_tolerance = 0, allow_empty = FALSE )epg_run_pipeline( con, sf_obj, topic, dataset, version_tag, topic_description = NULL, source_meta = NULL, raw_url = NA_character_, geom_type = "MultiPolygon", geom_transform = "ST_Multi(ST_CollectionExtract(ST_MakeValid(s.geom), 3))::geometry(MultiPolygon,25832)", delete_before_insert = FALSE, set_latest = TRUE, on_conflict_version = "error", expected_features = NA_integer_, count_tolerance = 0, allow_empty = FALSE )
con |
A DBI connection. |
sf_obj |
An sf object with the features to load. |
topic |
Topic name. |
dataset |
Dataset name. |
version_tag |
Version tag string. |
topic_description |
Optional topic description. |
source_meta |
Named list of source metadata (or NULL). Any URL it contains is redacted before it is stored. |
raw_url |
Optional S3 URI of the raw file. |
geom_type |
Geometry type for the partition. Default |
geom_transform |
SQL expression for geometry normalization. |
delete_before_insert |
If |
set_latest |
Whether to mark this version as latest. |
on_conflict_version |
Passed as |
expected_features |
Feature count reported by the source, checked
against the number of rows inserted before the version is published.
|
count_tolerance |
Fraction of |
allow_empty |
If |
Everything that touches the database happens inside the transaction, so a failure at any step leaves no registered version, no partition and no half-loaded data. Do the download before calling this.
The dataset_version.id (invisible).
Lowercases, replaces non-alphanumeric characters with underscores, and strips leading/trailing underscores.
epg_sanitize_name(x)epg_sanitize_name(x)
x |
A character string. |
A sanitized character string.
Credentials are stripped before the URL is stored: meta is readable by
warehouse_reader, so a query string must never reach this column.
epg_set_raw_file_url(con, ver_id, raw_url)epg_set_raw_file_url(con, ver_id, raw_url)
con |
A DBI connection. |
ver_id |
The dataset_version.id. |
raw_url |
The S3 URI of the raw file. A length > 1 vector is an error; pass the single URI the version should record. |
Danish sources ship shapefiles both as Latin-1 and as UTF-8. Forcing
ENCODING=ISO-8859-1 overrides a .cpg sidecar that says otherwise, and
silently mojibakes every æ, ø and å into the attrs jsonb — where
nothing downstream can tell it from real data.
epg_shapefile_encoding_opts(shp_path, fallback = "ISO-8859-1")epg_shapefile_encoding_opts(shp_path, fallback = "ISO-8859-1")
shp_path |
Path to a |
fallback |
Encoding assumed when no |
So: trust an explicit .cpg declaration when there is one, and only assume
Latin-1 when the shapefile declares nothing.
A character vector of GDAL open options for sf::st_read().
Upload raw files to S3-compatible object storage
epg_upload_raw_files(files, s3_config, on_error = c("stop", "warn"))epg_upload_raw_files(files, s3_config, on_error = c("stop", "warn"))
files |
Character vector of local file paths to upload. |
s3_config |
A list as returned by |
on_error |
|
The S3 URI(s) of uploaded files (character vector), or
NA_character_ for files that failed when on_error = "warn".
Returns the scheme, authority and path only. Use for the source_url
recorded in meta.dataset_version.source_meta when the query string
carries credentials.
epg_url_without_query(url)epg_url_without_query(url)
url |
A character vector of URLs. |
url without its query string or fragment.
Creates the stage schema if needed, writes the sf object via st_write,
verifies creation, normalizes the geometry column name to geom, and
returns a row count.
epg_write_staging(con, sf_obj, dataset, ver_id)epg_write_staging(con, sf_obj, dataset, ver_id)
con |
A DBI connection. |
sf_obj |
An sf object to stage. |
dataset |
Dataset name. |
ver_id |
The dataset_version.id (integer). |
A list with stage_tbl (qualified name like "stage.foo_s_42")
and n_rows (count of rows with non-NULL geometry).