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

exception watershed_workflow.split_hucs.GeometryError[source]
class watershed_workflow.split_hucs.HandledCollection(objs=None)[source]

A collection of of objects and handles for those objects.

add(value)[source]

Adds a object, returning a handle to that object

add_many(values)[source]

Add many objects, returning a list of handles.

pop(key)[source]

Removes a handle and its object.

handles()[source]

Generator for handles

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.

polygon(i)[source]

Construct polygon i and return a copy.

polygons()[source]

Iterate over the polygons.

spines()[source]

Iterate over spines.

exterior()[source]

Construct boundary polygon and return a copy.

deep_copy()[source]

Return a deep copy

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

addChild(segment)[source]

Append a child (upstream) reach to this reach.

getSegment()[source]

Used if one needs segments with properties.

prune(preserve_catchments=False)[source]

Removes a reach all the way to the leaf node.

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

merge(to='parent')[source]

Merges this to its parent or to its one and only child.

moveCoordinate(i, xy)[source]

Moves the ith coordinate of self.segment to a new location.

insertCoordinate(i, xy)[source]

Inserts a new coordinate before the ith coordinate.

appendCoordinate(xy)[source]

Appends a coordinate at the end (downstream) of the segment.

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.

popCoordinate(i)[source]

Removes the ith coordinate and returns its value.

leaf_nodes()[source]

Generator for all leaves of the tree.

leaves()[source]

Generator for all leaf reaches of the tree.

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.

iter_stream_children()[source]

Find all roots of next-level streams that flow into this reach.

iter_stream_roots()[source]

Generator to iterate over all roots of streamlevels.

accumulate(to_accumulate, to_save=None, op=<built-in function sum>)[source]

Accumulates a property across the river tree.

getNode(id)[source]

return node for a given ID

accumulateCatchments(name=None)[source]

Form a polygon of all contributing areas based on the ‘catchment’ property.

depthFirst()[source]

Iterates of reaches in the river in an “depth-first” ordering.

breadthFirst()[source]

Iterates of reaches in the river in an “breadth-first” ordering.

is_locally_continuous(tol=1e-07)[source]

Is this node continuous with its parent and children?

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

is_hydroseq_consistent()[source]

Confirms that hydrosequence is valid.

is_consistent(tol=1e-07)[source]

Validity checking of the tree.

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.

classmethod construct_rivers_by_hydroseq(segments)[source]

Given a list of segments, create a list of rivers using the HydroSeq maps provided in NHDPlus datasets.

deepcopy()[source]

Creates a copy of self including properties

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:

  1. If it crosses an external boundary, cut the reach in two and discard the portion outside of the domain.

  2. 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:

  1. simplifying to tol

  2. pruning all leaf nodes of length < prune_tol

  3. 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.

watershed_workflow.hydrography.merge(river, tol=0.1)[source]

Remove inner branches that are short, combining branchpoints as needed.

This function merges the “short” segment into the child segment if it is a junction tributary with one child or into the parent segment otherwise

watershed_workflow.hydrography.simplify(river, tol=0.1)[source]

Simplify, IN PLACE, all reaches.