This page was generated from nhd_navigation.ipynb. Interactive online version:
NHD Navigation#
[1]:
import folium
import networkx as nx
import pynhd
from pynhd import GeoConnex, WaterData
In this example, we explore navigating the National Hydrography Dataset (NHD) using the networkx
package. First, we use pynhd.enhd_flowlines_nx
to obtain the CONUS drainage network based on the ENHD. This function returns a networkx.DiGraph
object, a mapping from the node IDs of the generated network to ComIDs of ENHD, and a list of topologically sorted ComIDs. Note that for performance reasons, the node IDs of
the generated network are different than the ComIDs of ENHD.
[2]:
graph, node2comid, _ = pynhd.enhd_flowlines_nx()
comid2node = {v: k for k, v in node2comid.items()}
We validate our navigation approach with the Mainstems’ dataset that we can retrieve from the GeoConnex web service. Moreover, for visualization purposes, we obtain the flowline geometries from the WaterData web service.
[3]:
gcx = GeoConnex("mainstems")
nhd_flw = WaterData("nhdflowline_network")
We pick Wisconsin River for this tutorial. Let’s start by getting the mainstem of Wisconsin River from GeoConnex.
[4]:
ms = gcx.byid("id", "323742")
ms.explore()
[4]:
GeoConnex returns a GeoDataFrame containing the requested mainstem(s) and their headwater and outlet NHDPlus v2 ComIDs. We use these two ComIDs to the navigate Wisconsin River in the ENHD network.
[5]:
outlet_comid = int(ms["outlet_nhdpv2_comid"].str.rsplit("/", n=1).iloc[0][-1])
head_comid = int(ms["head_nhdpv2_comid"].str.rsplit("/", n=1).iloc[0][-1])
Navigating from a headwater to an outlet is straightforward. We use networkx.shortest_path
that returns a list of nodes that we can use to get the ComIDs of the flowlines. Finding the shortest path between two nodes in a network is a well-known problem in graph theory. For a weighted graph with positive weight values, the Dijkstra’s algorithm is the most efficient approach. Note that in graph theory, the shortest path is a path that
has the minimum sum of its edge weights, i.e., the path with the least resistance (minimum navigation cost).
So, for navigating a NHDPlus from upstream to downstream, we need to consider using an attribute as edge weights that favors the flowlines without divergence. Thus, using the divergence
attribute is recommended for cases that divergence is a concern. However, ENHD provides a dendritic drainage network for CONUS, i.e., no divergence. So, we can simply navigate between two nodes in the ENHD network without using any weights.
[6]:
main_comids = [
node2comid[n] for n in nx.shortest_path(graph, comid2node[head_comid], comid2node[outlet_comid])
]
flw_main = nhd_flw.byid("comid", main_comids)
For visualization purposes and validating our approach, we overlay the flowlines that we obtained through navigation, on the mainstem of the Wisconsin River.
[7]:
m = ms.explore(style_kwds={"color": "blue", "weight": 4})
folium.GeoJson(flw_main, style_function=lambda f: {"color": "red", "weight": 1}).add_to(m)
m
[7]: