Giter Club home page Giter Club logo

stormcatchments's Introduction

stormcatchments

PyPi docs

Stormwater network aware catchment delineation

Converts existing stormwater infrastucture GIS feature data (points and lines) into a networkx directed graph (DiGraph) object, then utilizes the DiGraph to incorporate subsurface flows into urban stormwater catchment delineation.

Dependencies of stormcatchments include:

Similar libraries/projects:

Installation

To install from PyPI:

pip install stormcatchments

Input data requirements

To utilize this package, you need both point and line spatial data, which could represent a network of catchbasins and stormlines. The file format does not matter as long as it can be successfully read into a geopandas.GeoDataFrame. The line data must connect to the points, and lines must have verticies snapped to the points.

This was initially developed for Vermont Agency of Natural Resources stormwater infrastructure dataset. However, the package is intended to generalize to any infrastructure dataset that meets these basic requirements.

Example Usage

Imports

import geopandas as gpd
import stormcatchments as sc

Read infrastructure data

storm_lines = gpd.read_file('tests/test_data/johnson_vt/storm_lines.shp')
storm_lines.set_index('OBJECTID', inplace=True)
storm_pts = gpd.read_file('tests/test_data/johnson_vt/storm_pts.shp')
storm_pts.set_index('OBJECTID', inplace=True)

Initialize Network object and resolve directions

# storm_pts contains a column "Type" with integer values describing what type of 
# structure each point represents
sinks = [2, 8] # Corresponds to catchbasins and culvert inlets
sources = [5, 9] # Corresponds to outfalls and culvert outlets

net = sc.Network(
  storm_lines, storm_pts, type_column='Type', sink_types=sinks, source_types=sources
)

Refer to Mapping flow sinks and sources below for more information on initializing a Network

Resolve flow directions of the Network

net.resolve_directions(method='from_sources', verbose=True)

Output:

Adding edges...
Succesfully resolved direction for 202 edges

Refer to Determining subsurface flow direction below for more information of resolving Network directions

Preprocess terrain data

grid, fdir, acc = sc.terrain.preprocess_dem('tests/test_data/johnson_vt/dem.tif')

Note that sc.terrain.preprocess_dem() uses default settings for pysheds. It's worth experimenting with this step to try and improve results with your DEM.

Initialize Delineate object and get a stormcatchment

grid_epsg = 6589
delin = sc.Delineate(net, grid, fdir, acc, grid_epsg)

# (x, y) coordinates in same CRS as grid
pour_pt = (484636, 237170)
# get stormcatchment using the default accumulation threshold
stormcatchment = delin.get_stormcatchment(pour_pt, acc_thresh=1000)

Also get the original catchment (network unaware) to compare results

catchment = sc.delineate.get_catchment(
  pour_pt, grid, fdir, acc, grid_epsg, acc_thresh=1000
)

Plot original catchment in blue and stormcatchment in orange

This uses the built-in net.draw() method, which adds a contextily basemap when add_basemap=True. Note that the orange stormcatchment incorporates a large hillside that pipes to the pour point.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(12, 12))
stormcatchment.plot(ax=ax, ec='orange', fc='orange', alpha=0.5, linewidth=3)
catchment.plot(ax=ax, ec='blue', fc='blue', alpha=0.5, linewidth=3)
net.draw(ax=ax, add_basemap=True)

Plot of catchment and stormcatchment

Mapping flow sinks and sources

Flow sinks are where flow can enter a subsurface system (such as a catchbasin). Flow sources are where flow can exit a subsurface system (such as an outfall). Initializing the network.Network requires either:

  • Manually setting two bool columns in the point GeoDataFrame, named IS_SINK and IS_SOURCE that are set to True if a point falls into either category.
  • Defining a type_column in the point data, then supplying a list of sink_types and a list of source_types to lookup in the type_column. This will then be mapped onto two bool columns in the point data named IS_SINK and IS_SOURCE.

Determining subsurface flow direction

Resolving the flow direction of subsurface stormwater networks, which is done during network.Network.resolve_directions(), can be done in three ways:

  1. from_sources: This is the default. This method traces networks upstream from their discharge points. This assumes that subnetworks are comprised of one or more flow sinks that flow to a single flow source. If multiple flow sources are connected to a given flow sink, this method will run into issues since determining which flow source is the terminal node in the graph would need to incorporate pipe elevation data somehow.
  2. vertex_order: This defines the subsurface flow direction using the order of verticies in the line data (flowing from the first to last vertex).
  3. vertex_order_r: This is the same as above, but in reverse (flowing from last to first vertex).

Two other potential methods that are not yet implemented are:

  • Using surface elevation data as an analog for for subsurface pipe elevations. In flat urban settings this would likely have a lot of issues/inaccuracies.
  • Using pipe invert data from the attributes of point or line data. This would require manual preparation by the user but would be the most accurate method.

Validating network topology

Several functions are available in stormcatchments.topology to assist in validating the topology of your input data. These include:

  • find_floating_points: Returns points that aren't snapped to a line vertex. Floating points cannot be incorporated into the Network.
  • snap_points: Returns an altered copy of a stormcatchments.Network in which any floating points are snapped to the nearest vertex, within a specified snapping tolerance.
  • find_multi_outlet: Returns a GeoDataFrame containing geometry for subgraphs within a Network that have multiple flow sources (outlets). Delineation results may not be optimal in mutli-outlet subgraphs depending on how the directions are resolved within them. Having a single outlet ensures predictable delineation results.

stormcatchments's People

Contributors

ewouth avatar t-ott avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

ewouth

stormcatchments's Issues

Better to resolve edge directions as-needed or during initialization?

One option is to initialize Network.G with bidirectional edges, then have an additional user method call such as net.resolve_directions() that modifies the object and does that work once, then switch a boolean attribute to mark that as complete. Then when delineating stormcatchments, check to ensure directions have been resolved first.

Generate reporting for network direction resolution

Reports could be printed to std out or to separate file? Could include info related to:

  • How many outfalls/source points were resolved
  • How many edges were successfully resolved and how many edges failed to resolve
  • If any sections of the network do not have a outfall/source point

Move constants to a ```constants.py``` file

Such as SINK_TYPES_VT and SOURCE_TYPES_VT, then can access those constants when initializing a Network with something like:

...
from . import constants
...
net = network.Network(lines, points, sink_types=constants.SINK_TYPES_VT, source_types=constants.SOURCE_TYPES_VT)

And change the parameters sink_types and source_types to optional params. If default None, then check to make sure there's appropriate bool columns in the input data already.

Allow for line-only network initialization

In the case of line (pipe) data that has vertex order that corresponds to flow direction, you may not need to any point data. You would have to make one of two assumptions about the line data without points, though. Either:

  1. Every first vertex / root node on each line is a flow sink. For example, typical culverts where flow enters on one end, and travels out the flow source on the other end.
  2. Every vertex on each line is a flow sink, except leaf nodes. This would be applicable to line data that has some sort of catchbasin/drop inlet on each vertex.
  3. If neither of these two assumptions are correct for a given directioned line dataset, the user would have to provide some sort of point data to represent where flow can (or cannot) enter subsurface networks.

One relatively easy way to implement this is to initialize a Network with lines only, then write some function to create points at every vertex in the line data, using one of the two methods described above.

Change initialization of flow sinks and sources

Force user to supply bool columns for IS_SOURCE and IS_SINK. Could also allow user to change these default columns names to something else too, if desired. This should make Network initialization clearer. If the user hasn't mapped out two bool columns yet, they can do so with a quick .apply(), e.g., what's currently done here:

self.pts['IS_SINK'] = self.pts[type_column].apply(
    lambda x: True if x in sink_types else False
)
self.pts['IS_SOURCE'] = self.pts[type_column].apply(
    lambda x: True if x in source_types else False
)

Maybe these could be included as helper functions, but having multiple ways to initialize sinks/sources is a bit confusing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.