The new National Water Model (NWM) provides continuous hydrologic forecasting for the nearly 2.7 million reaches that make up the National Hydrography Dataset (NHD Plus). As part of the Young Innovators Program at the National Water Center over the past three years several applications to access and use the output have been created using Tethys. The NWM Viewer application can be used to select individual stream reaches and view the short, medium, long range and analysis and assimilation model configurations, while other applications use the API functions to obtain a forecast and compute statistics, compare with other gage data and forecasts, and generate derivative products such as floodplain maps. In this exercise, we will build a simple app that accesses the NWM Viewer API and displays the forecast for the short and medium range configurations for the reach that coincides with Two Mile Creek as it enters the Black Warrior River near the University of Alabama Campus in Tuscaloosa.
The script takes the following as input:
and accessing the API of the NWM Viewer produces the following:
We will create an app that lets the user pick from one of these configurations and then display the current forecast. We will also demonstrate adding the NHD maps as a Webbing Mapping Service.
Source code of this example is stored at:
https://github.com/BYU-Hydroinformatics/tethysapp-nwm_example
which has the following branches:
“master”: the completed app once you finish the last section “Adding a View for the Short Range Forecast”
“step_1_3”: Scaffold a New App Section-- Step 1-3
“step_6_7_8”: Scaffold a New App Section -- Step 6-8
“medium_range_step_1_6”: Adding a View for the Medium Range Forecast Step Section 1-6
“short_range_step_1_5”: Adding a View for the Short Range Forecast Section Step 1-5
If you get stuck on any particular section, you copy/download a solution from any of these branches using the steps outlined in the GitHub unit.
Before completing this tutorial, you should have done the following:
Also, if necessary, open a terminal window and do the following:
1. Start tethys virtual environment.
$ t
2. Start Tethys
(tethys) $ tstartdb (tethys) $ tms
3. Launch your browser and view Tethys Portal http://127.0.0.1:8000 or http://localhost:8000.
4. Sign in with default admin credentials: admin/pass
First we will create a new empty app. For each of the following console commands, enter only the code following the "(tethys) $".
If you do not already have a development folder create one by pathing to your home directory and typing:
(tethys) $ mkdir ~/tethysdev
Make your tethysdev your current directory
(tethys) $ cd ~/tethysdev
Create a new app using the scaffold command. You will be prompted to enter metadata about your app such as, proper name, version, author, and description. All of these metadata are optional. You can accept the default value by pressing Enter, repeatedly.
(tethys) $ tethys scaffold nwm_example
Change to the new app directory and run setup.py.
(tethys) $ cd ~/tethysdev/tethysapp-nwm_example
Install this new app to Tethys:
(tethys) $ python setup.py develop
Start/Restart Tethys:
(tethys) $ tstartdb (tethys) $ tms
Go to the browser at http://127.0.0.1:8000/apps/
You should see this:
The components of this page come from two different templates using the Django paradigm:
Navigate to the folder
tethysdev/tethysapp-nwm_example/tethysapp/nwm_example/templates/nwm_example
and open the base.html file in a text editor.
Remove:
<li class="title">App Navigation</li> <li class="active"><a href="">Home</a></li> <li><a href="">Jobs</a></li> <li><a href="">Results</a></li> <li class="title">Steps</li> <li><a href="">1. The First Step</a></li> <li><a href="">2. The Second Step</a></li> <li><a href="">3. The Third Step</a></li> <li class="separator"></li> <li><a href="">Get Started</a></li>
From block:
{% block app_navigation_items %} … {% endblock %}
Restart Tethys and View your app:
Navigate to the folder:
tethysdev/tethysapp-nwm_example/tethysapp/nwm_example/templates/nwm_example
and open the home.html file in a text editor.
Remove this code:
<h1>Welcome to your Tethys App!</h1> <p>Take advantage of beautiful typography to organize the content of your app:</p> <h1>Heading 1</h1> <h2>Heading 2</h2> <h3>Heading 3</h3> <h4>Heading 4</h4> <h5>Heading 5</h5> <h6>Heading 6</h6>
From block:
{% block app_content %} … {% endblock %}
Remove this code:
{% gizmo save_button %} {% gizmo edit_button %} {% gizmo remove_button %} {% gizmo previous_button %} {% gizmo next_button %}
From block:
{% block app_actions %} … {% endblock %}
Restart Tethys and View your app:
Note: If you encounter errors connecting to the database, use the following command:
(tethys) $ tstartdb
Next we will add a map to our app using the Map View gizmo and populate it using a web mapping service.
Open the base.html file in a text editor and INSERT this code:
{% url 'nwm_example:home' as home_url %} <li class="title">Navigation</li> <li class="{% if request.path == home_url %}active{% endif %}"><a href="{{ home_url }}">Home</a></li>
Into this block:
{% block app_navigation_items %} … {% endblock %}
Navigate to the folder
tethysdev/tethysapp-nwm_example/tethysapp/nwm_example/templates/nwm_example
and open the home.html file in a text editor. Replace the contents with the following:
Insert this code:
{% gizmo nwm_example_map %}
Into this block:
{% block app_content %} ... {% endblock %}
Navigate to the:
tethysdev/tethysapp-nwm_example/tethysapp/nwm_example
folder and open controllers.py. Replace the contents with the following code:
import datetime as dt from django.shortcuts import render from django.contrib.auth.decorators import login_required from django.shortcuts import reverse from tethys_sdk.gizmos import MapView, Button, MVLayer, MVLegendClass import plotly.graph_objs as go from tethys_sdk.gizmos import PlotlyView #from .helpers import get_nwm_forecast @login_required() def home(request): """ Controller for the app home page. """ # Tiled ArcGIS REST Layer arc_gis_layer2 = MVLayer( source='TileArcGISRest', options={'url': 'https://services.nationalmap.gov/arcgis/rest/services/wbd/MapServer'}, legend_title='Watershed Boundaries', legend_extent=[-173, 17, -65, 72] ) nwm_example_map = MapView( height='100%', width='100%', layers=[arc_gis_layer2], basemap='OpenStreetMap', legend = True ) context = { 'nwm_example_map': nwm_example_map } return render(request, 'nwm_example/home.html', context)
Start the Tethys Server
(tethys) $ tms
If you encounter errors connecting to the database, use the following command:
(tethys) $ tstartdb
Your app should look like this:
The objective of this next exercise is to create a second page for our app that displays a forecast for the NWM medium range configuration for Two Mile Creek in Tuscaloosa. The forecast will be accessed using the helper function previously created to call the API from the NWM Viewer. Our start date is July 20 and the forecast will last about 10 days.
In the same folder you are working in, create a new Python file called helpers.py and add the following:
# Use the API from the NWM Viewer app to get the WaterML text import requests def get_nwm_forecast(config, comid, startdate, enddate, forecasttime): url = 'https://apps.hydroshare.org/apps/nwm-forecasts/api/GetWaterML/?config=' + config + '&geom=channel_rt&variable=streamflow&COMID=' + comid + '&lon=-87.5658033081755&lat=33.2279708144365&startDate='+startdate+'&endDate='+enddate+'&time='+forecasttime+'&lag=t00z' res = requests.get(url).content return res
Open controller.py to enable the above Helper function:
Replace:
# from .helpers import get_nwm_forecast
With:
from .helpers import get_nwm_forecast
Open the base.html file and edit the block app navigation items. This will create a new button to navigate to the Medium Range page.
Inside this block:
{% block app_navigation_items %} ... {% endblock %}
Directly after this code:
{% url 'nwm_example:home' as home_url %}
Insert this code:
{% url 'nwm_example:medium_range' as medium_range_url %}
Then, after this code:
<li class="{% if request.path == home_url %}active{% endif %}"><a href="{{ home_url }}">Home</a></li>
Insert this code:
<li class="{% if request.path == medium_range_url %}active{% endif %}"><a href="{{ medium_range_url }}">Medium Range Forecast</a></li>
Create a new html file called medium_range.html by copying another html file and renaming it. Replace the contents with the following code:
{% extends "nwm_example/base.html" %} {% load tethys_gizmos %} {% block app_content %} <h1>National Water Model</h1> <h2>Medium Range Forecast</h2> {% gizmo nwm_plot %} {% endblock %}
Open the controllers.py file and edit the home(request) by editing the context.
Create a new medium_range(request) function by inserting the following code below the home(request) in the controllers.py file. The river “comid” being referenced is Two Mile Creek in Tuscaloosa. This comid could be replaced with any valid comid that is part of the NHD Plus dataset.
@login_required() def medium_range(request): """ Controller for the Medium Range page. """ dateraw = [] date1 = [] value1 = [] comid = '18228725' config = 'medium_range' startdate = '2017-07-20' enddate = '2017-07-21' #not needed for medium range forecasttime = '00' watermlstring = str(get_nwm_forecast(config, comid, startdate, enddate, forecasttime)) waterml = watermlstring.split('dateTimeUTC="') waterml.pop(0) for e in waterml: parser = e.split('" methodCode="1" sourceCode="1" qualityControlLevelCode="1" >') dateraw.append(parser[0]) value1.append(parser[1].split('<')[0]) for e in dateraw: date1.append(dt.datetime.strptime(e, "%Y-%m-%dT%H:%M:%S")) nwm_plot = PlotlyView([go.Scatter(x=date1, y=value1)]) context = { 'nwm_plot': nwm_plot } return render(request, 'nwm_example/medium_range.html', context)
Open the app.py file and make the following edits:
After this code:
url_maps = ( UrlMap( name='home', url='nwm-example', controller='nwm_example.controllers.home' ),
Insert this code:
UrlMap( name='medium_range', url='nwm-example/range/medium', controller='nwm_example.controllers.medium_range' ),
When finished, the complete app.py should be as follows:
from tethys_sdk.base import TethysAppBase, url_map_maker class NwmExample(TethysAppBase): """ Tethys app class for Nwm Example. """ name = 'Nwm Example' index = 'nwm_example:home' icon = 'nwm_example/images/icon.gif' package = 'nwm_example' root_url = 'nwm-example' color = '#8e44ad' description = 'Place a brief description of your app here.' tags = '' enable_feedback = False feedback_emails = [] def url_maps(self): """ Add controllers """ UrlMap = url_map_maker(self.root_url) url_maps = ( UrlMap( name='home', url='nwm-example', controller='nwm_example.controllers.home' ), UrlMap( name='medium_range', url='nwm-example/range/medium', controller='nwm_example.controllers.medium_range' ), ) return url_maps
Start the Tethys Server
(tethys) $ tms
If you encounter errors connecting to the database, use the following command:
(tethys) $ tstartdb
Open a web browser and go to the site http://127.0.0.1:8000/apps to view your app.
Now we will add a second request to access information from the National Water Model to generate a short range forecast.
Once again, we will make a link to a new page by making the following edits in base.html:
After this code:
{% url 'nwm_example:medium_range' as medium_range_url %}
Insert:
{% url 'nwm_example:short_range' as short_range_url %}
After this code:
<li class="{% if request.path == medium_range_url %}active{% endif %}"><a href="{{ medium_range_url }}">Medium Range Forecast</a></li>
Insert:
<li class="{% if request.path == short_range_url %}active{% endif %}"><a href="{{ short_range_url }}">Short Range Forecast</a></li>
Make a copy of medium_range.html and rename it short_range.html. Replace the contents of the file with the following:
{% extends "nwm_example/base.html" %} {% load tethys_gizmos %} {% block app_content %} <h1>National Water Model</h1> <h2>Short Range Forecast</h2> {% gizmo nwm_plot %} {% endblock %}
Add the controller code for the short range forecast in controller.py by making the following edits:
Add this new function to the end of this file:
@login_required() def short_range(request): """ Controller for the Short Range page. """ dateraw = [] date1 = [] value1 = [] comid = '18228725' config = 'short_range' startdate = '2017-07-20' enddate = '2017-07-21' forecasttime = '12' watermlstring = str(get_nwm_forecast(config, comid, startdate, enddate, forecasttime)) waterml = watermlstring.split('dateTimeUTC="') waterml.pop(0) for e in waterml: parser = e.split('" methodCode="1" sourceCode="1" qualityControlLevelCode="1" >') dateraw.append(parser[0]) value1.append(parser[1].split('<')[0]) for e in dateraw: date1.append(dt.datetime.strptime(e, "%Y-%m-%dT%H:%M:%S")) nwm_plot = PlotlyView([go.Scatter(x=date1, y=value1)]) context = { 'nwm_plot': nwm_plot } return render(request, 'nwm_example/short_range.html', context)
Open the app.py file. After this code:
UrlMap( name='medium_range', url='nwm-example/range/medium', controller='nwm_example.controllers.medium_range' ),
Insert:
UrlMap( name='short_range', url='nwm-example/range/short', controller='nwm_example.controllers.short_range' ),
Start the Tethys Server:
(tethys) $ tms
If you encounter errors connecting to the database, use the following command:
(tethys) $ tstartdb
Open a web browser and go to the site http://127.0.0.1:8000/apps to view your app.
At this point, your app is complete!