### Install swmmio Source: https://github.com/pyswmm/swmmio/blob/master/docs/index.ipynb Install the swmmio library using pip. ```bash pip install swmmio ``` -------------------------------- ### SWMMIO Basemap Setup with Leaflet Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/index.html Initializes a Leaflet map with a dark Mapbox tile layer and adds GeoJSON data for new conduits. Includes functions for feature popups and styling. ```javascript // Set up the map var mymap = L.map('mapid').setView([39.921685, -75.148946], 15); var tile_url = 'https://api.mapbox.com/styles/v1/mapbox/dark-v9/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiYWVyaXNwYWhhIiwiYSI6ImNpdWp3ZTUwbDAxMHoyeXNjdDlmcG0zbDcifQ.mjQG7vHfOacyFDzMgxawzw' L.tileLayer(tile_url).addTo(mymap); // POPUPS function onEachFeature(feature, layer) { // does this feature have a property named popupContent? console.log(feature.properties.geom1) // console.log(layer) if (feature.properties && feature.properties.geom1) { content = feature.properties.HoursFloodedProposed + 'hrs x ' + feature.properties.geom2 + 'ft' // layer.bindPopup('Geom1 % ' + feature.properties.MaxQPercent); layer.bindPopup(content); }; }; function mystylz (feature){ console.log(feature.properties.fill) return {color: feature.properties.fill}; }; // ADD THE DATA TO MAP // convert_coords(data) L.geoJSON(newconduits, {onEachFeature:onEachFeature}).addTo(mymap); // L.geoJSON(newconduits, {onEachFeature:onEachFeature}).addTo(mymap); ``` -------------------------------- ### Load SWMM Model and Get Links DataFrame Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/getting_started.ipynb Instantiate a swmmio Model object and retrieve the links data as a pandas DataFrame. This is useful for inspecting link properties. ```python from swmmio.tests.data import MODEL_FULL_FEATURES_PATH import swmmio # instantiate a model object model = swmmio.Model(MODEL_FULL_FEATURES_PATH) # get the data related to links model.links.dataframe ``` -------------------------------- ### Run Multiple SWMM Models in Parallel Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/getting_started.ipynb Start a multiprocessing pool to run multiple SWMM models concurrently. Specify directories containing SWMM input files and optionally control the number of unused cores. ```shell # run all models in models in directories Model_Dir1 Model_Dir2 python -m swmmio -sp Model_Dir1 Model_Dir2 # leave 1 core unused python -m swmmio -sp Model_Dir1 Model_Dir2 -cores_left=1 ``` -------------------------------- ### Instantiate swmmio.Model Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/working_with_pyswmm.ipynb Instantiate a swmmio.Model by providing the path to a SWMM input file. This model object can then be used to access model properties and run simulations. ```python from IPython.display import HTML import swmmio import pyswmm # path to a SWMM model from swmm-nrtestsuite model_path = 'https://raw.githubusercontent.com/USEPA/swmm-nrtestsuite/refs/heads/dev/public/examples/Example1.inp' model = swmmio.Model(model_path) model.summary ``` -------------------------------- ### Run a Single SWMM Model from Command Line Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/getting_started.ipynb Execute a single SWMM model simulation using the swmmio command-line interface. Provide the path to the SWMM input file. ```shell python -m swmmio --run path/to/mymodel.inp ``` -------------------------------- ### Initialize Mapbox Map Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/mapbox_base_comparison.html Sets up a Mapbox map with a specified style, center coordinates, zoom level, and container. Ensure the mapboxgl library is loaded and an access token is set before using this snippet. ```javascript mapboxgl.accessToken = 'pk.eyJ1IjoiYWVyaXNwYWhhIiwiYSI6ImNpdWp3ZTUwbDAxMHoyeXNjdDlmcG0zbDcifQ.mjQG7vHfOacyFDzMgxawzw'; var map = new mapboxgl.Map({ style:'mapbox://styles/mapbox/dark-v9', center: [-75.148946, 39.921685], zoom: 15, //pitch: 20, //bearing: -17.6, container: 'map' }); ``` -------------------------------- ### Tag and Push Release Source: https://github.com/pyswmm/swmmio/blob/master/RELEASE.md Create an annotated tag for the release version and push it to the origin repository, including all tags. ```bash git tag -a vX.X.X -m 'Release version' git push --follow-tags ``` -------------------------------- ### Initialize Mapbox Compare Control Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/compare.html Sets up two Mapbox maps (before and after) and initializes the Compare control to enable side-by-side visualization. Requires Mapbox GL JS library. ```javascript mapboxgl.accessToken = 'pk.eyJ1IjoiYWVyaXNwYWhhIiwiYSI6ImNpdWp3ZTUwbDAxMHoyeXNjdDlmcG0zbDcifQ.mjQG7vHfOacyFDzMgxawzw'; var beforeMap = new mapboxgl.Map({ container: 'before', style: 'mapbox://styles/mapbox/dark-v9', center: [-75.148946, 39.921685], zoom: 0 }); var afterMap = new mapboxgl.Map({ container: 'after', style: 'mapbox://styles/mapbox/dark-v9', center: [-75.148946, 39.921685], zoom: 0 }); var map = new mapboxgl.Compare(beforeMap, afterMap, { // Set this to enable comparing two maps by mouse movement: // mousemove: true center: [-75.148946, 39.921685], }); ``` -------------------------------- ### Initialize Nodes Object with Custom Settings Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/getting_started.ipynb Create a Nodes object with specific input and report sections, and selected columns. This allows for targeted data extraction from SWMM model files. ```python from swmmio import Model, Nodes m = Model(model_path) nodes = Nodes( model=m, inp_sections=['junctions', 'storage', 'outfalls'], rpt_sections=['Node Depth Summary', 'Node Inflow Summary'], columns=[ 'InvertElev', 'MaxDepth', 'InitDepth', 'SurchargeDepth', 'MaxTotalInflow', 'coords'] ) nodes.geodataframe.head() ``` -------------------------------- ### Basic Network Visualization with swmmio Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/visualizing_models.ipynb Generates a quick visualization of the SWMM network using the swmmio.draw_model function. This provides a simple overview of the model's structure. ```python swmmio.draw_model(model, title='NCIMM Black and White Box Model') ``` -------------------------------- ### Instantiate SWMM Model from URL Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/visualizing_models.ipynb Instantiates a SWMM model object by passing a URL to the inp file. This is useful for accessing models hosted remotely. ```python import swmmio # url to the inp file hosted on GitHub URL = 'https://raw.githubusercontent.com/SWMMEnablement/1729-SWMM5-Models/dd846c1466c1219019492cc7895f049d298f6d2f/SWMM5_NCIMM/10070_H_Elements.inp' # instatiate a model object by passing in a URL model = swmmio.Model(URL) ``` -------------------------------- ### Restore Development Version Source: https://github.com/pyswmm/swmmio/blob/master/RELEASE.md Add 'dev0' to the version and increment the minor version number in __init__.py to prepare for the next development cycle. ```bash git add . git commit -m "Restore dev version" ``` -------------------------------- ### Stage and Commit Release Version Source: https://github.com/pyswmm/swmmio/blob/master/RELEASE.md Add all changes to the staging area and commit them with a message indicating the release version has been set. ```bash git add . git commit -m "Set release version" ``` -------------------------------- ### swmmio.core.rpt Source: https://github.com/pyswmm/swmmio/blob/master/docs/reference/swmmio_rpt.rst The `rpt` class in `swmmio.core` provides methods to access and parse data from SWMM report files (.rpt). It allows users to read various simulation results and summary statistics. ```APIDOC ## Class: swmmio.core.rpt ### Description Provides an interface for reading and parsing SWMM report files (.rpt). ### Methods #### `__init__(self, rpt_path)` Initializes the `rpt` object with the path to the SWMM report file. - **rpt_path** (str) - Required - The file path to the SWMM report file. #### `read(self)` Reads and parses the entire SWMM report file. Returns: - A dictionary-like object containing all parsed data from the report file. #### `get(self, section_name)` Retrieves data for a specific section from the report file. - **section_name** (str) - Required - The name of the section to retrieve (e.g., 'SUMMARY', 'ELEMENT COUNTS'). Returns: - A pandas DataFrame or Series containing the data for the specified section, or None if the section is not found. #### `sections(self)` Returns a list of all available section names in the report file. Returns: - list - A list of strings, where each string is a section name. #### `summary(self)` Retrieves the summary section of the report file. Returns: - A pandas DataFrame containing the summary statistics. #### `element_counts(self)` Retrieves the element counts section of the report file. Returns: - A pandas DataFrame containing the counts of different SWMM elements. #### `timing(self)` Retrieves the timing information from the report file. Returns: - A pandas DataFrame containing simulation timing details. ``` -------------------------------- ### swmmio.core.inp Source: https://github.com/pyswmm/swmmio/blob/master/docs/reference/swmmio_inp.rst The `inp` class provides methods for reading, writing, and manipulating SWMM input files (.inp). It allows users to access and modify various sections of the input file programmatically. ```APIDOC ## swmmio.core.inp ### Description Provides methods for reading, writing, and manipulating SWMM input files (.inp). This class allows programmatic access and modification of different sections within a SWMM input file. ### Methods - **__init__(self, filepath=None, **kwargs)**: Initializes the Inp object. Can be initialized with a filepath or keyword arguments for sections. - **read_inp(self, filepath)**: Reads an SWMM input file from the specified filepath. - **write_inp(self, filepath)**: Writes the current Inp object to a SWMM input file at the specified filepath. - **get_section(self, section_name)**: Retrieves a specific section from the input file. - **add_section(self, section_name, data)**: Adds a new section to the input file. - **remove_section(self, section_name)**: Removes a section from the input file. - **get_value(self, section_name, item_name, column_name=None)**: Gets a specific value from a section. - **set_value(self, section_name, item_name, column_name, value)**: Sets a specific value in a section. - **get_items(self, section_name)**: Retrieves all items within a specified section. - **add_item(self, section_name, item_data)**: Adds a new item to a section. - **remove_item(self, section_name, item_name)**: Removes an item from a section. - **get_columns(self, section_name)**: Retrieves the column headers for a section. - **add_column(self, section_name, column_name)**: Adds a new column to a section. - **remove_column(self, section_name, column_name)**: Removes a column from a section. ### Parameters - **filepath** (str, optional): The path to the SWMM input file to read. Defaults to None. - ****kwargs**: Arbitrary keyword arguments, where each key is a section name and the value is the data for that section. ### Example ```python from swmmio.core import inp # Read an existing INP file inp_obj = inp.Inp('model.inp') # Access a section controls = inp_obj.get_section('CONTROLS') print(controls) # Set a value inp_obj.set_value('OPTIONS', 'FLOW_UNITS', 'CFS') # Write to a new INP file inp_obj.write_inp('new_model.inp') ``` ``` -------------------------------- ### Access Model Network Graph Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Initializes a SWMM model and accesses its network representation as a graph using networkx. This is a prerequisite for graph-based analysis. ```python import networkx as nx jersey = swmmio.Model(jersey.inp.path) G = jersey.network # find nodes with zero in-degree zero_indegree = [n for n in G.nodes() if G.in_degree(n) == 0] ``` -------------------------------- ### Initialize Mapbox GL JS Map Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/mapbox_base.html Initializes a new Mapbox GL JS map instance, setting the access token, style URL, initial zoom level, and the HTML container ID. Placeholder comments indicate where to insert map center, bounding box, and GeoJSON data. ```javascript mapboxgl.accessToken = 'pk.eyJ1IjoiZW1uZXQiLCJhIjoiY2pscGFpZjRlMjJmdDNsbjNycDN6a3J0OCJ9.wYmEb0AnxVVMKVhs2ns89A'; var map = new mapboxgl.Map({ style:'mapbox://styles/emnet/ckjt4ym0q00cz19ob1y62adi5', // center: [-75.148946, 39.921685], // INSERT MAP CENTER HERE zoom: 15, container: 'map', }); // INSERT BBOX HERE // INSERT GEOJSON HERE ``` -------------------------------- ### Initialize Model for Scaling and Plotting Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Resets the SWMM model to its original state and calculates its centroid and bounding box before plotting the links. This is typically done before applying scaling or fractal generation techniques. ```python # let's see if that worked jersey = swmmio.Model(jersey.inp.path) (xc, yc), [x1, y1, x2, y2] = centroid_and_bbox_from_coords(jersey.inp.coordinates) # start the plot as usual ax = jersey.links.geodataframe.plot(color='green', capstyle='round') ``` -------------------------------- ### Plotting SWMM Links with Customization Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Plots SWMM model links with specified color, linewidth, and capstyle. This is a foundational step for visualizing the network. ```python ax = jersey.links.geodataframe.plot(color='green', linewidth=jersey.links.geodataframe['Geom1'], capstyle='round') ``` -------------------------------- ### Prepare Model DataFrames Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/visualizing_models.ipynb Copies the geodataframes for links and nodes from the model object. This is often a preliminary step before further analysis or manipulation. ```python links = model.links.geodataframe.copy() nodes = model.nodes.geodataframe.copy() ``` -------------------------------- ### Access SWMM Model as Networkx Graph Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/getting_started.ipynb Load a SWMM model and access its network representation as a Networkx MultiDiGraph. This enables graph-based analysis and traversal of the model's structure. ```python # access the model as a Networkx MutliDiGraph model = Model(model_path) G = model.network # iterate through links for u, v, key, data in model.network.edges(data=True, keys=True): # do stuff with the network print(u, v, key, data) break ``` -------------------------------- ### Scale and Plot Model Coordinates Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Scales model coordinates and plots the links with varying transparency. Requires numpy for arange. ```python for scale in np.arange(0.05, 1, 0.1): jersey = swmmio.Model(jersey.inp.path) jersey = scale_model_coords(jersey, scale, (xc, yc)) jersey.links.geodataframe.plot(ax=ax, alpha=0.5) ax.set_axis_off() ``` -------------------------------- ### Styled Network Links and Outfall Visualization Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/visualizing_models.ipynb Visualizes model links with linewidth based on 'Geom1' and highlights outfalls with their invert elevation. The axis is turned off for a cleaner map view. ```python # grab dataframe of outfalls outfalls = model.inp.outfalls # draw the model links and outfalls and turn off the axis because it doesn't really help here ax = model.links.geodataframe.plot(linewidth=model.links.dataframe['Geom1'], figsize=(10,10)) ax.set_axis_off() model.nodes.geodataframe.loc[outfalls.index].plot( 'InvertElev', ax=ax, legend=True, legend_kwds={"label": "Invert Elevation"} ) ``` -------------------------------- ### Update Local Repository Source: https://github.com/pyswmm/swmmio/blob/master/RELEASE.md Fetch the latest changes from the upstream repository and push them to your origin master branch. ```bash git pull upstream master git push origin master ``` -------------------------------- ### Initiating Recursive Plotting for a SWMM Model Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Initializes the recursive plotting process for a SWMM model. It sets up the initial plot axes, identifies nodes with zero in-degree, and calls the recursive plotting function. ```python jersey = swmmio.Model(jersey.inp.path) # Start the plot as usual ax = jersey.links.geodataframe.plot(color='green', linewidth=jersey.links.geodataframe['Geom1'], capstyle='round', figsize=(10, 10)) # Find nodes with zero in-degree G = jersey.network zero_indegree = [n for n in G.nodes() if G.in_degree(n) == 0] # Run the recursive plotting function with a maximum depth of 3 plot_recursive(jersey, ax, zero_indegree, depth=1, max_depth=3) ax.set_axis_off() ``` -------------------------------- ### Push Development Changes Source: https://github.com/pyswmm/swmmio/blob/master/RELEASE.md Push the restored development version changes to both the upstream and origin master branches. ```bash git push upstream master git push origin master ``` -------------------------------- ### Visualize SWMM Model Graph Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/getting_started.ipynb Visualize the SWMM model's network graph using matplotlib and networkx. This helps in understanding the connectivity and structure of the model. ```python # visualize the graph import matplotlib.pyplot as plt import networkx as nx # Draw the graph pos = nx.spring_layout(G, k=30) plt.figure(figsize=(5, 2)) nx.draw(G, node_size=10, node_color='blue', with_labels=False) plt.show() ``` -------------------------------- ### Load SWMM Model and Access Outfalls Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/getting_started.ipynb Load a SWMM model from a file path and access its outfall data. This is useful for initial model inspection. ```python model_slr_5 = swmmio.Model(newfilepath) model_slr_5.inp.outfalls ``` -------------------------------- ### Highlight Sub-Watershed and Add Annotations Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/visualizing_models.ipynb Identifies all elements within a sub-watershed, plots them with distinct styling, and adds annotations for the outfall and summary statistics. Requires networkx and swmmio to be imported. ```python outfall = 'OT055333' water_shed_junctions = nx.ancestors(G, outfall) ws_edges = [k for _, _, k in G.edges(keys=True, nbunch=water_shed_junctions)] links['outfall'] = links.index.isin(ws_edges) ax = links.plot('outfall', linewidth=links['Geom1'], figsize=(10,10), capstyle='round') model.nodes.geodataframe.loc[[outfall]].plot(markersize=220, ax=ax, color='green', zorder=10) # Coordinates of the point of interest (example coordinates) point_of_interest = model.nodes.geodataframe.loc[outfall].geometry # Add title and hide axes ax.set_title(f'{outfall} Watershed', loc='left', y=0.95, fontsize=18) ax.set_axis_off() # Add annotation with arrow highlighting location of outfall ax.annotate( outfall, xy=(point_of_interest.x, point_of_interest.y), xytext=(-20, 15), textcoords='offset points' ) # Add annotation in the bottom right part of the plot with summary stats # Get the data limits xlim = ax.get_xlim() ylim = ax.get_ylim() # Calculate the annotation coordinates x_annotate = xlim[0] + 0.93 * (xlim[1] - xlim[0]) y_annotate = ylim[0] + 0.07 * (ylim[1] - ylim[0]) sub_links = links.loc[links['outfall']] # build the summary text summary = f"""Total Sewer Length: {sub_links['Length'].sum() / 5280:.0f} miles Maximum Diameter: {sub_links['Geom1'].max() * 12} inches Number of Links: {len(sub_links)} Number of Nodes: {len(water_shed_junctions)} """ ax.annotate( summary, xy=(x_annotate, y_annotate), # Coordinates for the text box xytext=(x_annotate, y_annotate), # Same coordinates for text textcoords='data', horizontalalignment='right', verticalalignment='bottom' ) ``` -------------------------------- ### Run SWMM Simulation and Extract Link Flows Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/working_with_pyswmm.ipynb Run a SWMM simulation using pyswmm.Simulation and extract the flow for each link at every simulation step. The results are stored in a dictionary and then converted to a pandas DataFrame for plotting. ```python import pandas as pd link_flows = dict() # Run simulation PySWMM with pyswmm.Simulation(model.inp.path) as sim: # get link ids link_ids = model.inp.conduits.index for step in sim: # store each link's flow in a dictionary link_flows[sim.current_time] = { link_id: pyswmm.Links(sim)[link_id].flow for link_id in link_ids } pd.DataFrame(link_flows).T.plot(title='Link Flows') ``` -------------------------------- ### Generate Model Variations with Sea Level Rise Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/getting_started.ipynb Create multiple SWMM model files with varying outfall stage elevations to simulate climate change impacts. This involves looping through desired sea level rise increments and saving modified INP files. ```python import os # path to a SWMM model from swmm-nrtestsuite model_path = 'https://raw.githubusercontent.com/USEPA/swmm-nrtestsuite/refs/heads/dev/public/examples/Example3.inp' # initialize a model object model = swmmio.Model(model_path) sea_level_rise = 0.0 # set the starting sea level rise condition rise_increment = 0.25 # set the increment of sea level rise for each iteration # set the outfall type and initial outfall stage elevation model.inp.outfalls.loc[:, 'OutfallType'] = 'FIXED' model.inp.outfalls.loc[:, 'StageOrTimeseries'] = 576 # create models up to 5ft of sea level rise. while sea_level_rise < 5: # create a dataframe of the model's outfalls outfalls = model.inp.outfalls # add the current rise to the outfalls' stage elevation outfalls.loc[:, 'StageOrTimeseries'] = outfalls.loc[:, 'StageOrTimeseries'] + rise_increment model.inp.outfalls = outfalls # create the filename for the new model newfilepath = os.path.join(model.inp.dir, f'{model.inp.name}_{sea_level_rise}_SLR.inp') # Overwrite the OUTFALLS section of the new model with the adjusted data model.inp.save(newfilepath) # increase sea level rise for the next loop sea_level_rise += rise_increment # check the outfalls of the last sea level rise ``` -------------------------------- ### Load GeoJSON Data and Add Map Layers Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/mapbox_base.html This code block executes once the map is loaded, adding two GeoJSON sources ('sewer-data' and 'node-data') and three layers: 'sewer' (styled lines with interpolated color/width), 'sewer-hover' (for highlighting), and 'node' (styled circles). ```javascript var sewer_source, node_source; map.on('load', function() { // load the data into the map sewer_source = map.addSource('sewer-data', {'type': 'geojson', 'data': conduits}); node_source = map.addSource('node-data', {'type': 'geojson', 'data': nodes}); map.addLayer({ "id": "sewer", 'type': 'line', "source": "sewer-data", 'paint': { 'line-color': [ "interpolate-hcl", [ "linear" ], ["number", [ "get", "MaxQPerc" ], 0], 0, '#5cd66e' , 1, '#dde639' , 2, '#e64839', ], 'line-opacity': 0.85, 'line-width': [ 'interpolate', [ "exponential", 10 ], [ 'zoom' ], 10, [ "ln", ["+", [ "get", "MaxQ" ], 1]], 15, [ "ln", ["+", [ "get", "MaxQ" ], 1]], ], } }); map.addLayer({ "id": "sewer-hover", 'type': 'line', "source": "sewer-data", 'paint': { 'line-color': "#8384a4", 'line-opacity': 1, 'line-width':4, }, 'filter':["==", "'InletNode'", ""], }); map.addLayer({ "id": "node", 'type': 'circle', "source": "node-data", 'paint': { 'circle-color': '#1182d9', 'circle-opacity': 0.75, 'circle-radius':3, } }); ``` -------------------------------- ### Animate Link Flows Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/working_with_pyswmm.ipynb Animate the link flows extracted from the simulation. This involves creating a GeoDataFrame of links, joining the flow data, and using matplotlib.animation.FuncAnimation to create the animation. The animation is then rendered as HTML. ```python import matplotlib.pyplot as plt import matplotlib.animation as animation # Create a links geodataframe and join the flow data links_gdf = model.links.geodataframe links_gdf = links_gdf.join(pd.DataFrame(link_flows)) # create a figure and axis fig, ax = plt.subplots() # Function to update the plot for each frame def update(frame): ax.clear() links_gdf.plot(linewidth=links_gdf[frame]+0.2, ax=ax, capstyle='round') ax.set_axis_off() ax.set_title(f'{frame}') # Create the animation ani = animation.FuncAnimation(fig, update, frames=list(link_flows)[30:400][::5], repeat=True) # Close the figure to prevent it from being displayed plt.close(fig) # render the animation in the notebook HTML(ani.to_jshtml(fps=30)) ``` -------------------------------- ### Advanced Model Rotation with Shifting Origin Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Generates a dense visual pattern by plotting multiple rotated versions of the SWMM model with a shifting origin for rotation in each iteration. Uses a lower alpha for increased density. ```python # refresh the model jersey = swmmio.Model(jersey.inp.path) # start the plot as usual ax = jersey.links.geodataframe.plot(linewidth=jersey.links.dataframe['Geom1'], capstyle='round') # jersey.nodes.geodataframe.plot('MaxDepth', ax=ax, markersize='InvertElev', zorder=2) # spin that thing around for rads in np.arange(0, 6*3.14, 0.05): jersey = rotate_model(jersey, rads=rads, origin=(xc+rads*50, yc)) jersey.links.geodataframe.plot(linewidth=jersey.links.dataframe['Geom1'], capstyle='round', ax=ax, alpha=0.1) ax.set_axis_off() ``` -------------------------------- ### Generate Changelog Entries Source: https://github.com/pyswmm/swmmio/blob/master/RELEASE.md Use loghub to generate changelog entries based on milestones, users, and issue labels. Specify categories for features, enhancements, and bugs. ```bash loghub aerispaha/swmmio -m -u -ilr "complete" -ilg "feature" "New Features" -ilg "enhancement" "Enhancements" -ilg "bug" "Bugs fixed" ``` -------------------------------- ### Basic Network Links Plot with Geopandas Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/visualizing_models.ipynb Creates a basic plot of the model's links using the .geodataframe.plot() method. This leverages geopandas and matplotlib for visualization. ```python # basic plot of the model's links model.links.geodataframe.plot() ``` -------------------------------- ### Handle Map Click Events for Feature Popups Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/mapbox_base.html Attaches a click event listener to the map to query rendered features within a small clickbox. If a feature is found, it highlights the feature and displays a popup with its properties. ```javascript map.on('click', function (e) { //create a buffer to forgive those clumsy clicks var x = e.point.x; var y = e.point.y; var clickbox = [[x-10, y-10], [x+10, y+10]] var features = map.queryRenderedFeatures(clickbox, { layers: ['sewer', 'node'] }); if (!features.length) { map.setFilter("sewer-hover", ["==", "Name", ""]); return; } var feature = features[0]; console.log(feature) // highlight whats been clicked map.setFilter("sewer-hover", ["==", "Name", feature.properties.Name]); html_message_start = '' + feature.properties.Name + '
'; html_message = []; Object.keys(feature.properties).forEach(function(key) { html_message.push(key + ': ' + feature.properties[key]); }); html_message = html_message_start + html_message.join('
'); var popup = new mapboxgl.Popup() .setLngLat(map.unproject(e.point)) .setHTML(html_message) .addTo(map); }); ``` -------------------------------- ### Spirograph Effect with Model Rotation Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Creates a Spirograph-like effect by repeatedly rotating the SWMM model around its centroid and plotting it with transparency. Requires calculating the model's centroid and bounding box first. ```python # first we'll get the centroid and the bounding box of the model # this is useful for rotating the model about its centroid (xc, yc), [x1, y1, x2, y2] = centroid_and_bbox_from_coords(jersey.inp.coordinates) # start the plot as usual ax = jersey.links.geodataframe.plot(linewidth=jersey.links.dataframe['Geom1'], capstyle='round') jersey.nodes.geodataframe.plot('MaxDepth', ax=ax, markersize='InvertElev', zorder=2) # spin that thing around for rads in np.arange(0, 3*3.14, 0.1): jersey = rotate_model(jersey,rads=rads, origin=(xc, yc)) jersey.links.geodataframe.plot(linewidth=1, capstyle='round', ax=ax, alpha=0.5) jersey.nodes.geodataframe.plot('MaxDepth', ax=ax, markersize='InvertElev', zorder=2, alpha=0.5) ax.set_axis_off() ``` -------------------------------- ### Basic SWMM Model Plotting Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Plots SWMM links and nodes using their geometric data and attributes. Axes are turned off for a cleaner visualization. ```python import numpy as np import pandas as pd import swmmio from swmmio.examples import jersey from swmmio.utils.functions import rotate_model from swmmio.utils.spatial import centroid_and_bbox_from_coords ax = jersey.links.geodataframe.plot(linewidth=jersey.links.dataframe['Geom1'], capstyle='round') jersey.nodes.geodataframe.plot('MaxDepth', ax=ax, markersize='InvertElev', zorder=2) # hide the axes since we're making art ax.set_axis_off() ``` -------------------------------- ### Plotting a Different SWMM Model Recursively Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Applies the recursive plotting logic to a different SWMM model ('walnut'). This demonstrates the reusability of the plotting function across different model files. ```python from swmmio.examples import walnut walnut = swmmio.Model(walnut.inp.path) # Start the plot as usual ax = walnut.links.geodataframe.plot(color='green', linewidth=walnut.links.geodataframe['Geom1'], capstyle='round', figsize=(20, 10)) # Find nodes with zero in-degree G = walnut.network zero_indegree = [n for n in G.nodes() if G.in_degree(n) == 0] # Run the recursive plotting function with a maximum depth of 3 plot_recursive(walnut, ax, zero_indegree, depth=1, max_depth=3) ax.set_axis_off() ``` -------------------------------- ### Add Parcel Extrusion Layer (Before Map) Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/compare.html Loads GeoJSON data for parcels and adds a fill-extrusion layer to the 'before' map. The layer's color and height are dynamically styled based on the 'HoursFloodedBaseline' property. ```javascript beforeMap.on('load', function() { //load the data into the map beforeMap.addSource('sewer-data', {'type': 'geojson', 'data': conduits}); beforeMap.addSource('parcel-data', {'type': 'geojson','data': parcels}); beforeMap.addLayer({ "id": "parcel-extrusion-baseline", 'type': 'fill-extrusion', "source": "parcel-data", 'paint': { 'fill-extrusion-color': { property: 'HoursFloodedBaseline', stops: [ [0.0833, '#343332'], [2.0, '#f91313'] ] }, 'fill-extrusion-height': { property: 'HoursFloodedBaseline', stops: [ [0.0833, 5], [2.0, 200] ] }, 'fill-extrusion-opacity': 0.8, } }); }); ``` -------------------------------- ### Iterative Coordinate Shifting and Plotting Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Iterates through nodes with zero in-degree, shifts and scales the model coordinates, and plots the links. This is useful for visualizing sub-networks or specific parts of the model relative to a target point. ```python for node, row in jersey.inp.coordinates.loc[zero_indegree].iterrows(): jersey = swmmio.Model(jersey.inp.path) anchor_point = jersey.inp.coordinates.loc[jersey.inp.outfalls.index[0]][['X', 'Y']].values jersey = scale_model_coords(jersey, 0.3, center_point=anchor_point) jersey = shift_model_coords(jersey, (row.X, row.Y), anchor_point=anchor_point) jersey.links.geodataframe.plot(ax=ax, alpha=0.5) ``` -------------------------------- ### Scale Model Coordinates Function Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Defines a utility function to scale all coordinates within an SWMM model about a specified center point. This is a prerequisite for generating fractal patterns. ```python def scale_model_coords(model, scale_factor, center_point): """ Apply a scaling factor to all coordinates in the model, such that the coordinates appear to shrink or expand about a given center point. Parameters ---------- model : swmmio.Model The SWMM model whose coordinates are to be scaled. scale_factor : float The factor by which to scale the coordinates. center_point : tuple The (x, y) point about which to scale the coordinates. Returns ------- swmmio.Model The model with scaled coordinates. """ def scale_coordinates(coords, scale_factor, center_point): coords = np.array(coords) center_point = np.array(center_point) scaled_coords = center_point + scale_factor * (coords - center_point) return scaled_coords.tolist() model.inp.coordinates = pd.DataFrame( data=scale_coordinates(model.inp.coordinates.values, scale_factor, center_point), columns=model.inp.coordinates.columns, index=model.inp.coordinates.index ) model.inp.vertices = pd.DataFrame( data=scale_coordinates(model.inp.vertices.values, scale_factor, center_point), columns=model.inp.vertices.columns, index=model.inp.vertices.index ) model.inp.polygons = pd.DataFrame( data=scale_coordinates(model.inp.polygons.values, scale_factor, center_point), columns=model.inp.polygons.columns, index=model.inp.polygons.index ) return model ``` -------------------------------- ### Visualize Outfalls by Upstream Drainage Importance Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/visualizing_models.ipynb Plots the model links and highlights outfalls with marker size proportional to the number of upstream nodes. This visually emphasizes the 'importance' of each outfall in the network. ```python ax = model.links.geodataframe.plot(linewidth=model.links.dataframe['Geom1'], figsize=(10,10)) ax.set_axis_off() ax = model.nodes.geodataframe.loc[outfalls.index].plot( markersize=outfalls['CountUpstreamNodes'] / 10, ax=ax, color='green', zorder=2 ) ``` -------------------------------- ### Load and Add SWMM Data Layers to Mapbox Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/mapbox_base_comparison.html Loads conduit and parcel data as GeoJSON sources and adds them to the Mapbox map. This is typically done within the map's 'load' event handler. Ensure 'conduits' and 'parcels' variables contain valid GeoJSON data. ```javascript map.on('load', function() { //load the data into the map map.addSource('sewer-data', {'type': 'geojson', 'data': conduits}); map.addSource('parcel-data', {'type': 'geojson','data': parcels}); // map.addSource('node-data', {'type': 'geojson', 'data': nodes}); map.addLayer({ "id": "sewer", 'type': 'line', "source": "sewer-data", 'paint': { 'line-color': { property: 'Geom1', stops: [ [1, '#343332'], [6, '#64bab4'] ] }, 'line-opacity': 0.75, 'line-width':2, } }); map.addLayer({ "id": "sewer-hover", 'type': 'line', "source": "sewer-data", 'paint': { 'line-color': "#8384a4", 'line-opacity': 1, 'line-width':4, }, 'filter':["==", "'InletNode'", ""] }); // map.addLayer({ // "id": "parcel", // 'type': 'fill', // "source": "parcel-data", // 'paint': { // 'fill-color': { // property: 'HoursFloodedBaseline', // stops: [ // [0.0833, '#343332'], // [2.0, '#f91313'] // ] // }, // 'fill-opacity': 0.8, // } // }); map.addLayer({ "id": "parcel-extrusion-proposed", 'type': 'fill-extrusion', "source": "parcel-data", 'paint': { 'fill-extrusion-color': { property: 'HoursFloodedProposed', stops: [ [0.0833, '#343332'], [2.0, '#f91313'] ] }, 'fill-extrusion-height': { property: 'HoursFloodedProposed', stops: [ [0.0833, 5], [2.0, 200] ] }, 'fill-extrusion-opacity': 1, } }); map.addLayer({ "id": "parcel-extrusion-baseline", 'type': 'fill-extrusion', "source": "parcel-data", 'paint': { 'fill-extrusion-color': { property: 'HoursFloodedBaseline', stops: [ [0.0833, '#343332'], [2.0, '#f91313'] ] }, 'fill-extrusion-height': { property: 'HoursFloodedBaseline', stops: [ [0.0833, 5], [2.0, 200] ] }, 'fill-extrusion-opacity': 0.8, } }); map.addLayer({ "id": "parcel-extrusion-delta", 'type': 'fill-extrusion', "source": "parcel-data", 'paint': { 'fill-extrusion-color': { property: 'DeltaHoursAbs', stops: [ [0.0833, '#5858e2'], [2.0, '#3d3dff'] ] }, 'fill-extrusion-height': { property: 'DeltaHoursAbs', stops: [ [0.0833, 5], [2.0, 200] ] }, // 'fill-extrusion-base': { // property: 'HoursFloodedProposed', // stops: [ // [0.0833, 5], // [2.0, 200] // ] // }, 'fill-extrusion-opacity': 0.8, }, 'filter':["==", "Category", "decreased_flooding"], }); // map.addLayer({ // "id": "nodescool", // 'type': 'circle', // "source": "node-data", // 'paint': { // 'circle-color': '#e55e5e', // 'circle-radius': 5, // 'circle-opacity':1 // }, // }); // map.addLayer({ // "id": "nodes", // 'type': 'circle', // "source": "node-data", // 'paint': { // 'circle-color': { // property: 'HoursFlooded', // stops: [ // [0.0833, '#f1f075'], // [5.0, '#e55e5e'] // ] // }, // 'circle-radius': { // property: 'HoursFlooded', // stops: [ // [{zoom: 8, value: 0.0833}, 0], // [{zoom: 8, value: 5.0}, 1], // [{zoom: 11, value: 0.0833}, 0], // [{zoom: 11, value: 5.0}, 6], // [{zoom: 16, value: 0.0833}, 0], // [{zoom: 16, value: 5}, 40] // ] // } // }, // }); }); ``` -------------------------------- ### Mapbox Click Event for Sewer Layer Source: https://github.com/pyswmm/swmmio/blob/master/swmmio/reporting/basemaps/mapbox_base_comparison.html Handles click events on the map to query features from the 'sewer' layer. It creates a small buffer around the click point for better accuracy and displays a popup with feature properties. It also applies a filter to highlight the clicked sewer line. ```javascript // When a click event occurs near a polygon, open a popup at the location of // the feature, with description HTML from its properties. map.on('click', function (e) { //create a buffer to forgive those clumsy clicks var x = e.point.x; var y = e.point.y; var clickbox = [[x-10, y-10], [x+10, y+10]]; var features = map.queryRenderedFeatures(clickbox, { layers: ['sewer'] }); if (!features.length) { map.setFilter("sewer-hover", ["==", "Name", ""]); return; } var feature = features[0] console.log(feature) //highlight whats been clicked map.setFilter("sewer-hover", ["==", "Name", feature.properties.Name]); var props = feature.properties var cap = props.MaxQ / props.MaxQPerc //capacity var html_message = [props.InletNode, 'Peak Flow: ' + props.MaxQ + 'cfs', 'Capacity: '+ Math.round(cap * 100) / 100 + 'cfs', 'Geom1: ' + props.Geom1, 'Shape: '+ props.Shape, ]; var popup = new mapboxgl.Popup() .setLngLat(map.unproject(e.point)) .setHTML(html_message.join('
')) .addTo(map); }); ``` -------------------------------- ### Shift Model Coordinates and Plot Source: https://github.com/pyswmm/swmmio/blob/master/docs/usage/making_art_with_swmm.ipynb Shifts model coordinates relative to a specified anchor point and plots the links. This is useful for aligning model components. ```python jersey = swmmio.Model(jersey.inp.path) (xc, yc), [x1, y1, x2, y2] = centroid_and_bbox_from_coords(jersey.inp.coordinates) # start the plot as usual ax = jersey.links.geodataframe.plot(color='green', linewidth=jersey.links.geodataframe['Geom1'], capstyle='round') for outfall, _ in jersey.inp.outfalls.iterrows(): jersey = shift_model_coords(jersey, (jersey.inp.coordinates.loc[outfall].X, jersey.inp.coordinates.loc[outfall].Y), anchor_point=(x1, y2)) jersey.links.geodataframe.plot(ax=ax, alpha=0.5) ax.set_axis_off() ```