Data Structures and Shape Manipulation
Several custom data structures are used in the manipulation of geometry to form a consistent mesh. Two are the most important: the SplitHUC object defines a set of polygons that partition the domain into sub-catchments of the full domain (e.g. HUC 12s in a a HUC8 domain, or differential contributing areas to a series of gages). The RiverTree object defines a tree data structure defined by a child-parent relationship where children of a reach are all reaches that flow into that reach.
SplitHUCs
A module for working with multi-polys, a MultiLine that together forms a Polygon
- class watershed_workflow.split_hucs.HandledCollection(objs=None)[source]
A collection of of objects and handles for those objects.
- class watershed_workflow.split_hucs.SplitHUCs(shapes, abs_tol=1, rel_tol=1e-05, exterior_outlet=None, polygon_outlets=None)[source]
Class for dealing with the multiple interacting views of HUCs
- Parameters:
shapes (list[Polygon]) – The shapes to be split, one per subcatchment to be delineated.
abs_tol (float) – Distance used in defining small holes at intersections to ignore.
rel_tol (float) – Relative to the shapes area, a tolerance for defining small holes on intersections.
exterior_outlet (np.array((2,))) – Location of the outlet of the entire domain.
polygon_outlets (np.array((len(shapes), 2))) – Location of the outlets of each polygon.
The resulting class instance includes the following views into data:
- segmentsHandledCollection[LineString]
unique list of all segments, a HandledCollection of LineStrings
- boundariesHandledCollection[int]
A HandledCollection of handles into segments, identifying those segments on the outer boundary of the collection.
- intersectionsHandledCollection[int]
A HandledCollection of handles into segments, identifying those segments on the shared, inner boundaries.
- gonslist[HandledCollection[int], HandledCollection[int]]
One per polygon provided, a pair of HandledCollections, identifying the collection of handles into intersetctions and boudaries that make up thos polygon.
- watershed_workflow.split_hucs.simplify(hucs, tol=0.1)[source]
Simplify, IN PLACE, all segments in the polygon representation.
- watershed_workflow.split_hucs.removeHoles(polygons, abs_tol=1, rel_tol=1e-05, remove_all_interior=True)[source]
Removes interior small holes between the boundaries of polygons.
Note this assumes the polygons are mostly disjoint.
- watershed_workflow.split_hucs.partition(list_of_shapes, abs_tol=1, rel_tol=1e-05)[source]
Given a list of shapes which mostly share boundaries, make sure they partition the space. Often HUC boundaries have minor overlaps and underlaps – here we try to account for wiggles.
Modifies the list.
- watershed_workflow.split_hucs.intersectAndSplit(list_of_shapes)[source]
Given a list of shapes which share boundaries (i.e. they partition some space), return a compilation of their segments.
- Parameters:
list_of_shapes (list[shapely.geometry.Polygon]) – The polygons to intersect and split, of length N.
- Returns:
uniques (list[None | shapely.geometry.LineString | shapely.geometry.MultiLineString]) – An N-length-list of the entities describing the exterior boundary.
intersections (list[list[None | shapely.geometry.LineString | shapely.geometry.MultiLineString]]) – An NxN list of lists of the entities describing the interior boundary.
- watershed_workflow.split_hucs.find_outlets_by_crossings(hucs, river, tol=None, debug_plot=False)[source]
For each HUC, find all outlets using a river network’s crossing points.
- watershed_workflow.split_hucs.find_outlets_by_elevation(hucs, crs, elev_raster, elev_raster_profile)[source]
Find outlets by the minimum elevation on the boundary.
- watershed_workflow.split_hucs.find_outlets_by_hydroseq(hucs, river, tol=0)[source]
Find outlets using the HydroSequence VAA of NHDPlus.
Finds the minimum hydroseq reach in each HUC, and intersects that with the boundary to find the outlet.
- watershed_workflow.split_hucs.computeNonOverlappingPolygons(polys, abs_tol=1, rel_tol=1e-05, remove_all_interior=True)[source]
Given a list of overlapping contributing area polygons, compute a nonoverlapping set.
Often we wish to use SplitHucs constructed from the “delta contributing area”, e.g. the full domain is defined by a single polygon, then multiple gages on the river network CAs are used to form the subdomains.
This splits the overlapping contributing areas into the delta contributing area for each gage, returning a non-overlapping set of polygons, one per original polygon.
Properties are presereved.
- Parameters:
polys (list[shapely.geometry.Polygon]) – The overlapping CAs
- Returns:
partition (list[shapely.geometry.Polygon]) – The non-overlapping delta CAs.
holes (list[shapely.geometry.Polygon]) – Holes in the partition that are bigger than the tolerance.
RiverTree
Module for working with tree data structures, built on watershed_workflow.tinytree
- class watershed_workflow.river_tree.River(segment=None, children=None)[source]
A tree node data structure
- split(i)[source]
Split the reach at the ith coordinate of the segment.
Note that this could, but does not, split the catchment!
Returns upstream_node, downstream_node
- extendCoordinates(xys)[source]
Appends multiple coordinates at the end (downstream) of the segment.
- prependCoordinates(xys)[source]
Prepends multiple coordinates at the beginning (upstream) of the segment.
- iter_streamlevel()[source]
Generator to iterate over all reachs of the same streamlevel.
streamlevel is a concept defined by NHDPlus attributes, so this is only valid if the VAA was loaded.
- accumulate(to_accumulate, to_save=None, op=<built-in function sum>)[source]
Accumulates a property across the river tree.
- accumulateCatchments(name=None)[source]
Form a polygon of all contributing areas based on the ‘catchment’ property.
- is_continuous(tol=1e-07)[source]
Checks geometric continuity of the river.
Confirms that all upstream children’s downstream coordinate coincides with self’s upstream coordinate.
- make_continuous(tol=1e-07)[source]
Sometimes there can be small gaps between segments of river tree if river is constructed using HydrologicSequence and Snap option is not used. Here we make them consistent
- classmethod construct_rivers_by_geometry(reaches, tol=1e-07)[source]
Forms a list of River trees from a list of reaches by looking for close endpoints of those reaches.
Note that this expects that endpoints of a reach coincide with beginpoints of their downstream reach, and does not work for cases where the junction is at a midpoint of a reach.
- watershed_workflow.river_tree.getNode(rivers, nid)[source]
Finds the node, by ID, in a list of rivers
- watershed_workflow.river_tree.combineSiblings(n1, n2)[source]
Combines two sibling nodes, merging catchments and metadata.
- watershed_workflow.river_tree.accumulateCatchments(rivers, outlet_IDs, names=None)[source]
Given a list of outlet_IDs, find the reach in rivers and form its contributing area.
Parameters:
- rivers: list(watershed_workflow.river_tree.RiverTree)
Rivers from which outlet reaches are potentially from
- outlet_IDs: list(str)
List of IDs of the outlet reaches
- names: list(str), optional
Names for the catchments
- returns:
list(river_tree.RiverTree) – The trunks of the rivers at outlet_IDs
list(shapely.geometry.Polygon) – The contributing areas to those trunks.
- watershed_workflow.river_tree.accumulateIncrementalCatchments(rivers, outlet_IDs, names=None)[source]
Given a list of outlet_IDs, form the incremental contributing areas.
Parameters:
- rivers: list(watershed_workflow.river_tree.RiverTree)
Rivers from which outlet reaches are potentially from
- outlet_IDs: list(str)
List of IDs of the outlet reaches
- names: list(str), optional
Names for the catchments
- returns:
list(river_tree.RiverTree) – The trunks of the rivers at outlet_IDs
list(shapely.geometry.Polygon) – The contributing areas to those trunks.
Hydrography
Functions for manipulating hydrography and river_tree.River objects
- watershed_workflow.hydrography.createGlobalTree(reaches, method='geometry', tol=0.1)[source]
Constructs River objects from a list of reaches.
- Parameters:
reaches (list[LineString]) – List of reaches
method (str, optional='geometry') –
Provide the method for constructing rivers. Valid are:
’geometry’ looks at coincident coordinates
’hydroseq’ Valid only for NHDPlus data, this uses the NHDPlus VAA tables Hydrologic Sequence. If using this method, get_reaches() must have been called with both ‘HydrologicSequence’ and ‘DownstreamMainPathHydroSeq’ properties requested (or properties=True).
tol (float, optional=0.1) – Defines what close is in the case of method == ‘geometry’
- watershed_workflow.hydrography.snap(hucs, rivers, tol=0.1, triple_junctions_tol=None, reach_endpoints_tol=None, cut_intersections=False)[source]
Snap HUCs to rivers.
Attempts to make rivers that intersect, or are close to intersecting with HUC boundaries intersect discretely, in that they share a common point on the boundary.
This is the highest level function – it calls many of the other functions in this namespace.
- watershed_workflow.hydrography.snap_waterbodies(hucs, waterbodies, tol=0.1, cut_intersections=True)[source]
Snap waterbodies to HUCs.
Attempts to make waterbodies that intersect or nearly intersect hucs intersect discretely, in that they share common point(s).
- watershed_workflow.hydrography.cut_and_snap_crossings(hucs, rivers, tol=0.1)[source]
If any reach crosses a HUC boundary:
If it crosses an external boundary, cut the reach in two and discard the portion outside of the domain.
If it crosses an internal boundary, ensure there is a coordinate the reach that is on the internal boundary.
Either way, also ensure there is a coordinate on the HUC boundary at the crossing point.
- watershed_workflow.hydrography.snap_polygon_endpoints(hucs, rivers, tol=0.1)[source]
Snaps the endpoints of HUC segments to endpoints of rivers.
- watershed_workflow.hydrography.snap_endpoints(tree, hucs, tol=0.1)[source]
Snap river endpoints to huc segments and insert that point into the boundary.
Note this is O(n^2), and could be made more efficient.
- watershed_workflow.hydrography.cleanup(rivers, simp_tol=None, prune_tol=0.1, merge_tol=0.1, preserve_catchments=False)[source]
Cleans rivers in place by:
simplifying to tol
pruning all leaf nodes of length < prune_tol
merging all internal nodes of length < merge_tol
- watershed_workflow.hydrography.pruneBySegmentLength(tree, prune_tol=10, preserve_catchments=False)[source]
Removes any leaf segments that are shorter than prune_tol
- watershed_workflow.hydrography.pruneRiverByArea(river, area, prop='DivergenceRoutedDrainAreaSqKm', preserve_catchments=False)[source]
Removes, IN PLACE, reaches whose total contributing area is less than area km^2.
Note this requires NHDPlus data to have been used and the ‘DivergenceRoutedDrainAreaSqKm’ property (or whatever is selected) to have been set.
- watershed_workflow.hydrography.pruneByArea(rivers, area, prop='DivergenceRoutedDrainAreaSqKm', preserve_catchments=False)[source]
Removes, IN PLACE, reaches whose total contributing area is less than area km^2.
Note this requires NHDPlus data to have been used and the ‘DivergenceRoutedDrainAreaSqKm’ property to have been set.
- watershed_workflow.hydrography.removeDiversions(rivers, preserve_catchments=False)[source]
Removes diversions, but not braids.
- watershed_workflow.hydrography.removeBraids(rivers, preserve_catchments=False)[source]
Remove braids, but not diversions.
- watershed_workflow.hydrography.removeDivergences(rivers, preserve_catchments=False)[source]
Removes both diversions and braids.
Braids are divergences that return to the river network, and so look like branches of a river tree whose upstream entity is in the river (in another branch).
Diversions are divergences that do not return to the stream network, and so their upstream entity is in another river.
- watershed_workflow.hydrography.filterSmallRivers(rivers, count)[source]
Remove any rivers with fewer than count reaches.