Client for OGC API - Environmental Data Retrieval (EDR)

A tidy R client for services implementing the OGC API - Environmental Data Retrieval ('EDR') standard with JSON discovery and 'GeoJSON' or 'CoverageJSON' query responses. General purpose, but most of its real-world use is against in-situ monitoring networks (stream gauges, weather stations, snow and reservoir telemetry) that expose their stations and time series as EDR collections. Known working endpoints include the USGS waterdata OGC API and the Western Water Datahub. Provides discovery, query, and parsing helpers for the locations, items, position, area, cube, radius, trajectory, and corridor query types. Returns 'CoverageJSON' as tidy 'tibble' rows and 'GeoJSON' as 'sf' objects.


edr4r

CRAN status R-CMD-check Codecov test coverage Lifecycle: experimental License: MIT

An R client for any service that speaks OGC API - Environmental Data Retrieval (EDR). The spec is general, but in practice this package gets the most use against in-situ monitoring networks — stream gauges, weather stations, snow telemetry, reservoir telemetry — that expose their stations and time series through EDR.

Two known-good places to point it:

For cross-server experiments, the Met Office Labs EDR demonstrator is another useful endpoint. It is a technical demonstrator, not an operational service: availability, collections, and response details can change without notice, so do not build production workflows around it.

The goal is to take the tedious parts of EDR off your hands — URL construction, comma-separated parameter lists, WKT coordinate encoding, retries, content negotiation — and hand back something you can actually do data analysis with:

  • CoverageJSON → a long tibble (one row per coverage × parameter × time step), via covjson_to_tibble().
  • GeoJSON → an sf object, via geojson_to_sf().

Installation

Install the released version from CRAN:

install.packages("edr4r")

Or the development version from GitHub:

# install.packages("pak")
pak::pak("ksonda/edr4r")

# or
# install.packages("remotes")
remotes::install_github("ksonda/edr4r")

For local development:

git clone https://github.com/ksonda/edr4r.git
cd edr4r
R -e 'devtools::install()'

Requires R >= 4.1. The sf package is optional but recommended (used to turn location lists and GeoJSON into spatial objects).

Quick start

Start by pointing a client at a server. The base URL is the only thing it really needs:

library(edr4r)

client <- edr_client("https://api.waterdata.usgs.gov/ogcapi/beta")
# or "https://api.wwdh.internetofwater.app"
# or "http://localhost:5005" if you're running pygeoapi locally

edr_collections(client)
#> # A tibble: N × 7
#>   id                   title                description  extent_bbox crs   data_queries links
#>   <chr>                <chr>                <chr>        <list>      <chr> <list>       <list>
#> 1 monitoring-locations Monitoring locations ...          <dbl [4]>   ...   <chr [3]>    ...
#> 2 daily-values         Daily values         ...          <dbl [4]>   ...   <chr [3]>    ...
#> ...

The collection IDs above (monitoring-locations, daily-values) are the ones I used as placeholders — every server advertises its own. The first thing to do against a new service is run edr_collections() and read the data_queries column to see which EDR endpoints each collection supports.

To try the non-operational Met Office demonstrator with a deliberately small request, query one terrain point rather than a forecast collection:

met <- edr_client(
  "https://labs.metoffice.gov.uk/edr",
  timeout = 10,
  max_tries = 1
)

terrain <- edr_position(
  met,
  "terrain_tiles",
  coords = c(-0.1276, 51.5072),
  parameter_name = "Height"
)
covjson_to_tibble(terrain)

This example is also exercised by a scheduled, non-blocking live smoke check; it is never run as part of CRAN checks or the regular test suite.

Find stations

edr_locations() with no filters returns the full station list as GeoJSON. If you have sf installed, it gets promoted to an sf object automatically:

locs <- edr_locations(client, "monitoring-locations")
locs                            # sf POINTs with station attributes
plot(sf::st_geometry(locs))

Pull a time series for one station

Once you know a station ID, ask for its values. The server returns CoverageJSON; covjson_to_tibble() flattens it into one row per (coverage × parameter × timestamp):

resp <- edr_location(
  client, "daily-values",
  location_id    = "08313000",
  datetime       = "2020-01-01/2020-12-31",
  parameter_name = c("discharge", "gage_height")
)

df <- covjson_to_tibble(resp)
df
#> # A tibble: 732 × 9
#>   coverage_id parameter   parameter_label  unit  datetime                x     y     z value
#>   <chr>       <chr>       <chr>            <chr> <dttm>              <dbl> <dbl> <dbl> <dbl>
#> 1 08313000    discharge   Discharge        ft3/s 2020-01-01 00:00:00 -109.  37.0    NA   240
#> ...

Spatial filters — bbox and polygon

To grab everything inside a rectangle, use edr_cube():

cube <- edr_cube(
  client, "daily-values",
  bbox           = c(-120, 39, -118, 41),
  datetime       = "2023-01-01/2023-03-31",
  parameter_name = "discharge"
)
covjson_to_tibble(cube)

For an arbitrary polygon, edr_area() takes WKT, an sf polygon, or a matrix of (lon, lat) rows (it'll close the ring for you):

ring <- matrix(
  c(-109, 47, -104, 47, -104, 49, -109, 49),
  ncol = 2, byrow = TRUE
)
area <- edr_area(client, "monitoring-locations", coords = ring,
                 datetime = "2022-01-01/..")
covjson_to_tibble(area)

Plot a time series

edr_plot() is a small ggplot2 wrapper over the tidy tibble:

edr_plot(resp)            # accepts an edr_response directly

Facets by parameter (so different units don't share a y-axis) and colours by station. Add layers or themes like any other ggplot.

It also auto-detects common non-station shapes:

edr_plot(cube)            # x/y grid -> tile map
edr_plot(profile)         # varying z -> vertical profile

# or force the layout
edr_plot(profile, view = "profile")
edr_plot(cube, view = "grid")

Map stations with per-station popups

edr_map() puts the stations on a leaflet basemap. Pass data = as a named list keyed by station id (the shape [edr_explore()] produces) and each marker gets a popup with an inline plot and a "Download CSV" link for that station's data — embedded as a data: URI so the saved HTML is selfcontained:

stations <- edr_locations(client, "monitoring-locations",
                          bbox = c(-116, 35.5, -114, 36.5))
data_list <- list("3514" = covjson_to_tibble(resp))
m <- edr_map(stations, data = data_list, popup = "plot+csv")
edr_save_html(m, "stations.html")

For a quick exploratory pass over a whole collection, edr_explore() does the fetch + plot + map in one call:

edr_explore(
  client, "daily-values",
  bbox           = c(-116, 35.5, -114, 36.5),
  datetime       = "2024-01-01/2024-03-31",
  parameter_name = "discharge",
  limit          = 25,
  file           = "snapshot.html"
)

Gridded coverages and vertical profiles can be mapped too. edr_map() detects tidy CoverageJSON grids/profiles and puts slice selectors inside the leaflet widget when there are multiple parameters or datetimes; grids also get a z selector when multiple vertical levels are present:

grid <- covjson_to_tibble(cube)
edr_map(grid)

profile <- covjson_to_tibble(profile_resp)
edr_map(profile)

edr_explore() uses the same behavior for bulk coverage queries. Use output = "plot" when you want a ggplot instead of the interactive map:

edr_explore(client, "gridded-collection",
            bbox = c(-120, 39, -118, 41),
            method = "cube")

edr_explore(client, "profile-collection",
            coords = c(-119, 40),
            method = "position")

edr_explore(client, "profile-collection",
            coords = c(-119, 40),
            method = "position", output = "plot")

Weird IDs, CSV, and an escape hatch

Some monitoring networks use compound station IDs — colon-separated triplets are a common pattern. The client URL-encodes reserved characters for you:

edr_location(client, "station-network", "1185:CO:SNTL",
             datetime = "2024-01-01/..")

If the server advertises CSV, you can ask for it instead of CovJSON:

edr_location(client, "daily-values", "08313000",
             datetime = "2010-01-01/..", format = "csv")

And if you need to hit an endpoint the package doesn't wrap (instances, custom queryables, anything weird), edr_request() is the raw escape hatch:

edr_request(client, "collections/daily-values/instances", format = "json")

API at a glance

Function EDR endpoint
edr_client() construct a client
edr_landing() / edr_conformance() /, /conformance
edr_collections() / edr_collection() /collections
edr_queryables() /collections/{id}/queryables
edr_locations() / edr_location() /collections/{id}/locations[/{loc}]
edr_items() / edr_item() /collections/{id}/items[/{item}]
edr_position() /collections/{id}/position
edr_area() /collections/{id}/area
edr_cube() /collections/{id}/cube
edr_radius() /collections/{id}/radius
edr_trajectory() /collections/{id}/trajectory
edr_corridor() /collections/{id}/corridor
edr_request() low-level escape hatch
covjson_to_tibble() / geojson_to_sf() response parsers

What a server actually supports varies. Every query verb above is in the EDR spec and supported by the client, but most servers implement only a subset. On in-situ monitoring deployments, locations, position, cube, and area are common; radius, trajectory, and corridor less so. Hitting a verb the server doesn't implement gives you an HTTP error. Check the data_queries column from edr_collections() before you assume a query will work.

Common parameters

Every query verb accepts the standard EDR filters:

  • datetime — an ISO-8601 instant or interval. Accepts "2020-01-01/2020-12-31", an open interval "2020-01-01/..", or a length-2 character vector c("2020-01-01", "2020-12-31").
  • parameter_name — a character vector of parameter names; sent as a comma-separated parameter-name= query. Use edr_parameters() to discover valid names.
  • bbox — numeric length-4 (minx, miny, maxx, maxy) or length-6 (with z).
  • coords — for position/area/radius/trajectory/corridor: a WKT string, a numeric vector / 2-column matrix of lon-lat, or an sf/sfc geometry.
  • z, crs, limit — passed through when supplied.
  • ... — any extra query parameter is forwarded verbatim.

License

MIT

Reference manual

It appears you don't have a PDF plugin for this browser. You can click here to download the reference manual.

install.packages("edr4r")

0.1.1 by Kyle Onda, 3 months ago


https://github.com/ksonda/edr4r


Report a bug at https://github.com/ksonda/edr4r/issues


Browse source code at https://github.com/cran/edr4r


Authors: Kyle Onda [aut, cre, cph]


Documentation:   PDF Manual  


MIT + file LICENSE license


Imports cli, httr2, jsonlite, purrr, rlang, tibble, vctrs

Suggests base64enc, ggplot2, htmlwidgets, knitr, leaflet, rmarkdown, sf, svglite, testthat, webfakes


See at CRAN