edr4r targets the
JSON ecosystem around OGC API - Environmental Data Retrieval. The EDR
standard permits more encodings and deployment patterns than this
package promises to parse. This article makes that boundary explicit so
that a server advertising an EDR conformance class is not mistaken for a
guarantee that every edr4r workflow will work
unchanged.
| Capability | Status in edr4r | Notes |
|---|---|---|
| Landing page, conformance, collections, parameters, queryables | Supported | JSON discovery documents |
| Collection instances | Supported | Discovery and instance-scoped query paths |
locations, items, position,
area, cube, radius,
trajectory, corridor |
Supported | Read-only HTTP GET helpers; the server may implement only a subset |
| EDR 1.0/1.1 conformance identifiers | Supported | Version-tolerant capability matching; current verified endpoints advertise 1.0 identifiers |
| EDR 1.1 multi-geometries and custom dimensions | Supported subset | WKT MULTIPOINT, MULTIPOLYGON, and
MULTILINESTRING; custom extent metadata is retained and
named dimension parameters pass through ... |
| CoverageJSON | Supported subset | Inline Coverage/CoverageCollection with
primitive, regular, or tuple axes and inline NdArray
ranges; nonstandard coordinates are retained as .axis_*
columns |
| GeoJSON | Supported | Converted to sf when installed; otherwise retained as
an edr_response |
| CSV | Limited | Parsed by the low-level client and supported directly by location queries |
| NetCDF, GeoTIFF, GRIB and other native encodings | Download only | Use edr_request(..., parse = FALSE) with the server’s
advertised f value; no native R conversion yet |
External CoverageJSON domains/ranges and
TiledNdArray |
Not supported | Rejected explicitly rather than partially or silently parsed |
| Pagination/link following | Supported subset | Opt-in for GeoJSON FeatureCollections from
edr_locations() / edr_items(); bounded by
finite page and feature caps |
| Multi-location retrieval | Supported subset | Explicit IDs through sequential edr_location_batch();
optional bounded calendar windows, custom-axis-aware exact
deduplication, checkpoint/resume, CoverageJSON/CSV, and a finite
expanded-plan cap |
| HTTP 202/308 asynchronous polling | Not yet supported | HTTP 204 empty responses are supported |
| POST queries and EDR Part 2 Pub/Sub | Out of current scope | The package is a synchronous, read-only client |
CRS values are passed to the server as requested. edr4r
retains effective CoverageJSON referencing metadata and does not
silently reproject coverage coordinates. A projected grid remains valid
in edr_plot(), which uses source coordinates.
edr_map() requires WGS 84/CRS84 longitude/latitude: it
rejects a declared projected or other known geographic CRS and
out-of-range coordinates or inferred cell bounds. Missing or custom
references produce a warning only after those range checks pass. Station
sf geometry is transformed to WGS 84 for Leaflet display
after any ID or source-CRS spatial matching has been completed; missing
sf CRS metadata warns before plausible degree-range
coordinates are used as-is.
Current pygeoapi endpoints commonly serve GeoJSON and CoverageJSON
through f=json, which remains the compatibility default.
When a stricter endpoint advertises an encoding-specific token, pass it
explicitly while keeping the desired parser, for example
edr_position(..., format = "covjson", f = "CoverageJSON")
or
edr_request(..., query = list(f = "CoverageJSON"), format = "covjson").
Discovery tables (edr_collections(),
edr_parameters(), and edr_instances()) return
tibbles with stable scalar columns and list columns for metadata that
cannot be flattened without loss. Raw collection/instance documents
remain available in a raw list column. Detailed discovery
calls return the parsed JSON list.
CoverageJSON query verbs return an edr_response; convert
it explicitly with covjson_to_tibble(). GeoJSON collection
verbs return sf when sf is installed and
otherwise retain an edr_response. CSV responses return a
tibble. HTTP 204 produces a typed empty result rather than a parser
failure. The first nine CoverageJSON columns remain
coverage_id, parameter,
parameter_label, unit, datetime,
x, y, z, and value.
Nonstandard coordinates follow as .axis_<coordinate>
columns without changing row cardinality. A versioned
edr_covjson_metadata attribute retains per-coverage domain
type, axis details, and effective referencing, including
collection-level values inherited by child coverages. Package batch,
explore, and checkpoint paths preserve that attribute; arbitrary
third-party row binds are not promised to do so. For numeric
CoverageJSON ranges, values serialized as JSON strings are coerced only
when every non-missing string is finite and safely representable; actual
text, overflow/underflow, and unsafe declared integers remain explicit
parser errors.
edr_location_batch() returns an
edr_location_batch object with three stable tibbles:
requests (one status row per planned location/window
query), data (row-bound CoverageJSON/CSV data with
.request_id and .location_id provenance), and
errors (preserved conditions when
on_error = "collect"). With explicit
include_parameters = TRUE, a fourth parameters
tibble holds the nonduplicated collection or instance parameter catalog;
otherwise that component is NULL and no metadata request is
made. Optional day/week/month/year chunks require a finite closed
interval. Adjacent windows share their boundary; exact rows repeated
across windows for the same location are removed by default, while
requests$n_rows retains raw response counts. Timestamp
bounds are normalized to UTC before calendar arithmetic and may contain
up to six fractional-second digits; values that R cannot represent
without loss are rejected before requests begin. It is sequential in
this release so the normal client retry policy remains
authoritative.
Checkpointing is opt-in and preserves only terminal successful or
empty parsed responses. An interrupted or failed request has no terminal
file and is retried on the next explicit resume = TRUE
call. Each checkpoint is locked while in use and fingerprinted against
the effective endpoint, ordered location/window plan, format, and query
arguments. A mismatch or corrupt result aborts before network activity.
Headers, full query URLs, and error conditions are not stored; the
parsed observation data is stored and should be protected accordingly.
Checkpointed clients require an absolute HTTP(S) base URL without an
embedded query, fragment, username, or password. Rotating credentials
belong in client headers; the caller remains responsible for resuming
under the same logical authorization context.
The checkpoint schema is versioned with parser semantics. Schema 2 records the custom-axis identity and CoverageJSON metadata contract; earlier checkpoints are rejected explicitly and must be recreated rather than combined with newly parsed rows.
Parameter catalogs are not checkpointed or included in the
request-plan fingerprint. A resumed batch with
include_parameters = TRUE obtains current metadata through
the client’s discovery cache even when every data request is restored.
max_requests counts data requests only, and
on_error does not collect a parameter-discovery
failure.
Pagination is deliberately opt-in. The client follows only the
server-advertised body link with rel = "next", treats its
cursor/offset as opaque, and requires the continuation to remain on the
original request’s service origin. Reaching max_pages or
max_features while another page exists is a typed error,
not a silently partial result. edr_explore() does not
enable collection-wide pagination automatically.
Local argument and parser failures are raised before issuing unsafe
follow-up requests. HTTP failures retain their httr2_http_*
status classes and include a useful server error body when one is
available. edr_diagnose() is the exception: after
validating its arguments, it records metadata/network failures as
fail, warn, or skip rows so one
broken discovery endpoint does not hide the rest of the report.
The 0.2 discovery layer retains query-specific formats, units, CRS details, extents, and parameter metadata instead of flattening them away.
client <- edr_client("https://labs.metoffice.gov.uk/edr")
service <- edr_capabilities(client)
terrain <- edr_capabilities(client, "terrain_tiles")
edr_supports(client, "terrain_tiles", query = "position")
edr_supports(
client, "terrain_tiles",
query = "position", format = "CoverageJSON"
)
# Fresh, metadata-only probes; no data query is issued.
edr_diagnose(client, "terrain_tiles")Discovery responses are cached per client for a bounded period. Use
refresh = TRUE on a discovery call when current server
state matters, or clear that client’s metadata with
edr_cache_clear(client).
The table records direct compatibility checks, not a permanent availability promise. Server metadata and pagination behavior were last inspected on 2026-07-10.
| Endpoint | Role | Advertised query types observed | edr4r coverage |
|---|---|---|---|
| USGS waterdata | Operational U.S. streamgage service | locations |
Collection/location discovery, opaque cursor pagination,
latest-record station/batch time series, CoverageJSON parsing,
per-location exploration; location queries currently ignore
datetime |
| Western Water Datahub | Operational multi-network pygeoapi deployment | locations, items, position,
area, cube |
Discovery, parameters, offset pagination for items, station and bulk queries, plotting and mapping |
| Met Office Labs | Non-operational technical demonstrator | locations, items, instances,
position, area, cube,
radius, trajectory |
Cross-implementation metadata, instances, query-specific formats, terrain point coverage, and an explicit-CRS population grid |
The Met Office service describes itself as an example rather than an operational API. It is useful for interoperability testing because its metadata exercises instances and richer query variables, but production workflows must not depend on its availability.
Tests use small, reviewed fixtures so CRAN and pull-request checks are deterministic and offline. Met Office fixtures retain the response shapes needed for capability, instance, and single-Coverage parsing tests while omitting large payloads and verbose WKT definitions.
CoverageJSON axis bounds are not yet used as cell edges. Grid visualization infers cell bounds from coordinate centers, including range validation of the derived edges; external domains/ranges and tiled arrays remain unsupported as listed above.
Frozen cursor- and offset-shaped pages exercise both pagination styles without relying on live service state. Security and edge-case tests also cover relative links, cycles, malformed/multiple next links, cross-origin continuations, and page or feature caps.
A separate scheduled/manual workflow performs tiny, non-blocking live probes: bounded USGS cursor pagination and station batching, bounded WWDH offset pagination, plus the Met Office capability, terrain, and population-grid checks. An endpoint outage does not fail package CI; it is evidence of availability or interoperability drift to investigate.
edr4r can construct every EDR Part 1 query path listed
above, but a particular server may not advertise or implement each path
or encoding. Use capability discovery rather than treating the existence
of an R function as a server guarantee. For an unfamiliar endpoint,
start with edr_diagnose(), inspect collection capabilities,
and then issue the smallest representative query.