This vignette shows how to work with a new service based on data from the 3D Hydrography Program (3DHP) released in 2024. 3DHP data uses “mainstem” identifiers as the primary river ID. We’ll work with one for the Baraboo River in Wisconsin.

We can find one to start with at a url like:

https://reference.geoconnex.us/collections/mainstems/items?filter=name_at_outlet ILIKE '%Baraboo%'

This url searches the reference mainstem collection for rivers with names like “Baraboo”. Using that url query, we can find that the Baraboo mainstem ID is https://geoconnex.us/ref/mainstems/359842 and that it flows to the Wisconsin and Mississippi https://geoconnex.us/ref/mainstems/323742 and https://geoconnex.us/ref/mainstems/312091 respectively. At the reference mainstem page, we can also find that the outlet NHDPlusV2 COMID is https://geoconnex.us/nhdplusv2/comid/937070225.

The code block just below generates starting locations based on information derived from the reference mainstem for the Baraboo and a point location near the Baraboo’s outlet. It queries the 3DHP service for a flowline within a 10m buffer of the point specified just to get us started.


  comid <- "937070225"

  point <- c(-89.441, 43.487) |>
    sf::st_point() |>
    sf::st_sfc(crs = 4326) |> sf::st_sf()

  dm <- c('https://geoconnex.us/ref/mainstems/323742',
          'https://geoconnex.us/ref/mainstems/312091')

  flowline <- get_3dhp(point, type = "flowline", buffer = 10)
  
  pc <- function(x) sf::st_geometry(sf::st_transform(x, 3857))

  plot_nhdplus(bbox = sf::st_bbox(flowline), plot_config = list(flowline = list(col = NULL)), zoom = 14)
  plot(pc(flowline), add = TRUE, col = "darkblue", lwd = 2)
  plot(pc(point), pch = "O", add = TRUE)

Now, we can use dataRetrieval to retrieve a basin upstream of this outlet location. We specify the NHDPlusV2 comid here since we know it, but could have used the point location just as well.

With the basin pulled from the findNLDI() function, we can pass it to get_3dhp() as a Area of Interest for flowlines, waterbodies, and hydrolocations. We can then use the mainstem ids of our two downstream mainstem rivers found above to get the downtream mainstem rivers.

  basin <- dataRetrieval::findNLDI(comid = comid, find = "basin")

  network <- get_3dhp(basin$basin, type = "flowline")
  water <- get_3dhp(basin$basin, type = "waterbody")
  hydrolocation <- get_3dhp(basin$basin, type = "hydrolocation")
#> Warning in (function (AOI = NULL, ids = NULL, type = NULL, where = NULL, :
#> sink, spring, waterbody outlet features found but were outside area of interest
#> polygon.
#> Warning: No sink, spring, waterbody outlet features found
#> Warning: No headwater, terminus, divergence, confluence, catchment outlet
#> features found in area of interest.
  
  down_mains <- get_3dhp(ids = dm, type = "flowline")
  
  old_par <- par(mar = c(0, 0, 0, 0))
  plot_nhdplus(bbox = sf::st_bbox(basin$basin), flowline_only = TRUE,
               plot_config = list(flowline = list(col = NULL)), zoom = 10)
  plot(pc(basin$basin), lwd = 2, add = TRUE)
  plot(pc(network), lwd = 0.5, add = TRUE)
  plot(pc(water), lwd = 0.5, border = "skyblue", col = "lightblue", add = TRUE)
  plot(pc(hydrolocation), pch = "o", col = "#80808026", add = TRUE)
  plot(pc(down_mains), lwd = 3, col = "blue", add = TRUE)

  par(old_par)

  old_par <- par(mar = c(0, 0, 0, 0))
  plot(pc(down_mains))
  plot(pc(basin$basin), add = TRUE)

  par(old_par)

Neat. Now we have flowlines, waterbodies, hydrologic locations, a basin boundary, and major rivers downstream to work with. These could be saved to a local file for use in a GIS or used in some further analysis.

Say you want to know how to relate NHD data to 3DHP data. 3DHP hydrolocations include “reachcode” tops and bottoms to hel with that. Here, we lookup the hydrolocation with reachcode 07070004002889then grab the rest of the reachcode hydrolocations along the mainstem that the reachcode is part of.


  reachcode <- "07070004002889"

  hydrolocation <- get_3dhp(universalreferenceid = reachcode,
                            type = "reach code, external connection")

  hydrolocation
#> Simple feature collection with 2 features and 10 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: -89.44179 ymin: 43.48568 xmax: -89.43904 ymax: 43.48829
#> Geodetic CRS:  WGS 84
#>    id3dhp   featuredate                                mainstemid
#> 1 01HAJW7 1694649600000 https://geoconnex.us/ref/mainstems/359842
#> 2 01SVVYQ 1694649600000 https://geoconnex.us/ref/mainstems/359842
#>   universalreferenceid gnisid gnisidlabel featuretype featuretypelabel
#> 1       07070004002889   <NA>        <NA>          10  Reachcode Start
#> 2       07070004002889   <NA>        <NA>          11    Reachcode End
#>   edhuniqueid workunitid                   geometry
#> 1        <NA>       <NA> POINT (-89.44179 43.48829)
#> 2        <NA>       <NA> POINT (-89.43904 43.48568)

  mainstem_points <- get_3dhp(ids = hydrolocation$mainstemid, type = "reach code, external connection")
  mainstem_lines <- get_3dhp(ids = hydrolocation$mainstemid, type = "flowline")

  old_par <- par(mar = c(0, 0, 0, 0))
  plot(pc(hydrolocation), pch = "o", col = "#808080BF")
  plot(pc(mainstem_lines), lwd = 2, col = "blue", add = TRUE)

  par(old_par)
  
  old_par <- par(mar = c(0, 0, 0, 0))
  plot_nhdplus(bbox = sf::st_bbox(basin$basin), flowline_only = TRUE,
               plot_config = list(flowline = list(col = NULL)), zoom = 10)
#> Zoom set to: 10
  plot(pc(mainstem_lines), lwd = 2, col = "blue", add = TRUE)
  plot(pc(mainstem_points), pch = "o", col = "#808080BF", add = TRUE)

  par(old_par)

With this functionality, we have what we need to inter operate between older NHD data and newly released 3DHP data. This service is brand new and nhdplusTools is just getting going with functionality to support what’s shown here. Please submit issues if you find problems or have questions!!