Skip to content

Commit

Permalink
Update to new living atlas layer (#1)
Browse files Browse the repository at this point in the history
* ngrok js layer

* correct usages of river and reach

* revise url for new map server dns

* use segmented layers

* clear charts on reload

* correct river to latlon function

* update to new esri endpoint

* fix esri map

* format revisions, show timezone on plots

* linting
  • Loading branch information
rileyhales authored Jul 15, 2024
1 parent 7458ef3 commit 33d38b2
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 97 deletions.
4 changes: 3 additions & 1 deletion install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ requirements:
- cf-staging
- defaults
packages:
- geoglows>=1.1.0
- geoglows>=1.7.0
- natsort
- plotly

pip:

Expand Down
49 changes: 19 additions & 30 deletions tethysapp/hydroviewer/controllers.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import geoglows as gg
import geoglows as g
from django.http import JsonResponse
from django.shortcuts import render
from tethys_sdk.routing import controller


@controller
def home(request):
"""
Controller for the app home page.
"""
dates = gg.data.dates().values.flatten()
dates = [(date, f'{date[0:4]}-{date[4:6]}-{date[6:8]}') for date in dates]
dates = sorted(dates, reverse=True)
context = {
'dates': dates,
'endpoint': gg.data.DEFAULT_REST_ENDPOINT,
'version': gg.data.DEFAULT_REST_ENDPOINT_VERSION,
'endpoint': g.data.DEFAULT_REST_ENDPOINT,
'version': g.data.DEFAULT_REST_ENDPOINT_VERSION,
}
return render(request, 'hydroviewer/home.html', context)

Expand All @@ -31,40 +24,36 @@ def get_forecast(request):
source = 'hydroviewer'
reach_id = int(reach_id)
try:
ens = gg.data.forecast_ensembles(reach_id, date=forecast_date, source=source)
rp = gg.data.return_periods(reach_id)
simple = gg.analyze.simple_forecast(ens)
ens = g.data.forecast_ensembles(reach_id, date=forecast_date, source=source)
rp = g.data.return_periods(reach_id)
except Exception as e:
print(e)
return JsonResponse({'error': str(e)})

return JsonResponse({
'ens': gg.plots.forecast_ensembles(ens, rp_df=rp, plot_type='html'),
'simple': gg.plots.forecast(simple, rp_df=rp, plot_type='html'),
'rpt': gg.tables.flood_probabilities(ens, rp),
})
json_respones = {
'ens': g.plots.forecast_ensembles(ens, rp_df=rp, plot_type='html'),
'simple': g.plots.forecast(g.analyze.simple_forecast(ens), rp_df=rp, plot_type='html'),
'rpt': g.tables.flood_probabilities(ens, rp),
}
return JsonResponse(json_respones)


@controller(name='get-retrospective', url='get-retrospective')
def get_retrospective(request):
reach_id = int(request.GET['reach_id'])

df = gg.data.retrospective(river_id=reach_id)
dayavg_df = gg.analyze.daily_averages(df)
monavg_df = gg.analyze.monthly_averages(df)
annavg_df = gg.analyze.annual_averages(df)
river_id = int(request.GET['reach_id'])
df = g.data.retrospective(river_id=river_id)
rp = g.data.return_periods(river_id=river_id)

json_response = {
'retro': gg.plots.retrospective(df, plot_type='html'),
'dayAvg': gg.plots.daily_averages(dayavg_df, plot_type='html'),
'monAvg': gg.plots.monthly_averages(monavg_df, plot_type='html'),
'annAvg': gg.plots.annual_averages(annavg_df, plot_type='html'),
'retro': g.plots.retrospective(df, plot_type='html', rp_df=rp),
'dayAvg': g.plots.daily_averages(g.analyze.daily_averages(df), plot_type='html'),
'annAvg': g.plots.annual_averages(g.analyze.annual_averages(df), plot_type='html', decade_averages=True),
'fdc': g.plots.flow_duration_curve(df, plot_type='html'),
}

return JsonResponse(json_response)


@controller(name='find-river', url='find-river')
def find_river(request):
lat, lon = gg.streams.reach_to_latlon(int(request.GET['reach_id']))
lat, lon = g.streams.river_to_latlon(int(request.GET['reach_id']))
return JsonResponse({'lat': lat, 'lon': lon})
4 changes: 2 additions & 2 deletions tethysapp/hydroviewer/public/css/main.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* MAP STYLES */
.modal-chart {
height: min(3in, 550px)
height: min(4in, 550px)
}

.load-status {
Expand Down Expand Up @@ -174,7 +174,7 @@

#fcProbTable {
height: auto;
min-height: 250px;
min-height: 300px;
align-items: center;
display: flex;
justify-content: center;
Expand Down
77 changes: 44 additions & 33 deletions tethysapp/hydroviewer/public/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const app = (() => {
// const endpoint = "{{ endpoint }}";
// const URL_getForecastData = "{% url 'hydroviewer:get-forecast' %}";
// const URL_getHistoricalData = "{% url 'hydroviewer:get-retrospective' %}";
// const URL_findReachID = "{% url 'hydroviewer:find-river' %}";
// const URL_findRiverID = "{% url 'hydroviewer:find-river' %}";
// const ESRI_LAYER_URL = = "";

let loadingStatus = {reachid: "clear", forecast: "clear", retro: "clear"}
Expand Down Expand Up @@ -129,7 +129,8 @@ const app = (() => {
to: endDateTime
})
.addTo(mapObj)
L.control
L
.control
.layers(
basemapsJson,
{
Expand All @@ -141,32 +142,38 @@ const app = (() => {
.addTo(mapObj)

mapObj.on("click", event => {
if (mapObj.getZoom() <= 9.5) return mapObj.flyTo(event.latlng, 10)
if (mapObj.getZoom() < 16) {
mapObj.flyTo(event.latlng, 16)
mapObj.fire('zoomend')
return
}
mapObj.flyTo(event.latlng)

if (mapMarker) mapObj.removeLayer(mapMarker)
mapMarker = L.marker(event.latlng).addTo(mapObj)
// updateStatusIcons({reachid: "load", forecast: "clear", retro: "clear"})
// $("#chart_modal").modal("show")

// L.esri
// .identifyFeatures({url: ESRI_LAYER_URL})
// .on(mapObj)
// .at([event.latlng["lat"], event.latlng["lng"]])
// .tolerance(10) // map pixels to buffer search point
// .precision(3) // decimals in the returned coordinate pairs
// .run((error, featureCollection) => {
// if (error) {
// updateStatusIcons({reachid: "fail"})
// alert("Error finding the reach_id")
// return
// }
// updateStatusIcons({reachid: "ready"})
// selectedSegment.clearLayers()
// selectedSegment.addData(featureCollection.features[0].geometry)
// REACHID = featureCollection.features[0].properties["COMID (Stream Identifier)"]
// fetchData(REACHID)
// })
updateStatusIcons({reachid: "load", forecast: "clear", retro: "clear"})
$("#chart_modal").modal("show")

L
.esri
.identifyFeatures({url: ESRI_LAYER_URL})
.on(mapObj)
.at([event.latlng["lat"], event.latlng["lng"]])
.tolerance(10) // map pixels to buffer search point
.precision(6) // decimals in the returned coordinate pairs
.run((error, featureCollection) => {
if (error) {
updateStatusIcons({reachid: "fail"})
alert("Error finding the reach_id")
return
}
updateStatusIcons({reachid: "ready"})
selectedSegment.clearLayers()
selectedSegment.addData(featureCollection.features[0].geometry)
REACHID = featureCollection.features[0].properties["TDX Hydro Link Number"]
fetchData(REACHID)
})
})

//////////////////////////////////////////////////////////////////////// OTHER UTILITIES ON THE LEFT COLUMN
Expand All @@ -182,7 +189,7 @@ const app = (() => {
$.ajax({
type: "GET",
async: true,
url: `${URL_findReachID}${L.Util.getParamString({reach_id: prompt("Please enter a Reach ID to search for")})}`,
url: `${URL_findRiverID}${L.Util.getParamString({reach_id: prompt("Please enter a Reach ID to search for")})}`,
success: response => {
if (mapMarker) mapObj.removeLayer(mapMarker)
mapMarker = L.marker(L.latLng(response.lat, response.lon)).addTo(mapObj)
Expand Down Expand Up @@ -233,8 +240,8 @@ const app = (() => {
$("#fcProbTable"),
$("#retroPlot"),
$("#dayAvgPlot"),
$("#monAvgPlot"),
$("#annAvgPlot"),
$("#annAvgPlot "),
$("#fdcPlot")
]

const getForecastData = reachID => {
Expand All @@ -244,14 +251,17 @@ const app = (() => {
let simpleForecast = chartDivs[0]
let ensembleForecast = chartDivs[1]
let probabilityTable = chartDivs[2]
$("#chart_modal").modal("show")
ftl.tab("show")
simpleForecast.html(`<img alt="loading signal" src=${loading_gif}>`)
ensembleForecast.html('')
probabilityTable.html('')
simpleForecast.css("text-align", "center")
updateStatusIcons({forecast: "load"})
$.ajax({
type: "GET",
async: true,
data: {reach_id: REACHID, forecast_date: $("#forecast_date").val()},
data: {reach_id: REACHID, timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone, forecast_date: $("#forecast_date").val().replaceAll("-", "")},
url: URL_getForecastData,
success: response => {
ftl.tab("show")
Expand All @@ -273,7 +283,6 @@ const app = (() => {
error: () => {
updateStatusIcons({forecast: "fail"})
giveForecastRetryButton(REACHID)
REACHID = null
}
})
}
Expand All @@ -282,8 +291,10 @@ const app = (() => {
if (!REACHID) return
updateStatusIcons({retro: "load"})
updateDownloadLinks("clear")
let tl = $("#historical_tab_link") // select divs with jquery so we can reuse them
let plotdiv = chartDivs[3]
let tl = $("#historical_tab_link") // select divs with jquery so we can reuse them
chartDivs.slice(3).forEach(div => div.html("")) // clear the historical data divs
$("#chart_modal").modal("show")
$("#retroPlot").html(`<img alt="loading signal" src=${loading_gif}>`)
$.ajax({
type: "GET",
async: true,
Expand All @@ -292,10 +303,10 @@ const app = (() => {
success: response => {
tl.tab("show")
tl.click()
plotdiv.html(response.retro)
$("#retroPlot").html(response.retro)
$("#dayAvgPlot").html(response.dayAvg)
$("#monAvgPlot").html(response.monAvg)
$("#annAvgPlot").html(response.annAvg)
$("#fdcPlot").html(response.fdc)
updateDownloadLinks("set")
updateStatusIcons({retro: "ready"})
},
Expand Down Expand Up @@ -390,7 +401,7 @@ const app = (() => {

$("#forecast_tab_link").on("click", () => fix_buttons("forecast"))
$("#historical_tab_link").on("click", () => fix_buttons("historical"))
$("#forecast_date").on("change", getForecastData)
$("#forecast_date").on("change", () => getForecastData())

const clearMarkers = () => {
if (mapMarker) mapObj.removeLayer(mapMarker)
Expand Down
3 changes: 1 addition & 2 deletions tethysapp/hydroviewer/templates/hydroviewer/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@
<a class="btn btn-light mybtn" role="button" onclick="app.findReachID()">Find River by ID</a>
<a class="btn btn-light mybtn" role="button" onclick="app.clearMarkers()">Remove Map Marker</a>
<hr>
{# put a slider stype checkbox with a label inline with eachother #}
<div style="display: flex; justify-content: space-between; align-items: center">
<label class="control-label" for="slider">Auto Load Retrospective</label>
<label class="control-label" for="slider">Show Retrospective</label>
<input type="checkbox" id="auto-load-retrospective" style="height: 1rem; width: 1rem" checked>
</div>
<hr>
Expand Down
53 changes: 24 additions & 29 deletions tethysapp/hydroviewer/templates/hydroviewer/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,14 @@ <h2 class="modal-title">Streamflow Results</h2>
<div class="modal-body">
<!-- Nav tabs -->
<ul class="nav nav-tabs">
<li class="active"><a id="forecast_tab_link" href="#forecast" class="nav-link active" data-bs-toggle="tab">Forecasts</a>
</li>
<li class="nav-item"><a id="historical_tab_link" href="#historical" class="nav-link" data-bs-toggle="tab">Retrospective</a>
</li>
<li class="active"><a id="forecast_tab_link" href="#forecast" class="nav-link active" data-bs-toggle="tab">Forecasts</a></li>
<li class="nav-item"><a id="historical_tab_link" href="#historical" class="nav-link" data-bs-toggle="tab">Retrospective</a></li>
</ul>

<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="forecast">
<div class="panel panel-default">
<div id="forecast-date-selector">
<label for="forecast_date">Forecast Date: </label>
<select class="form-control" id="forecast_date" name="forecast_date">
{% for ymd, display in dates %}
<option value="{{ ymd }}">{{ display }}</option>
{% endfor %}
</select>
</div>
<div id="forecastPlot" style="height: auto"></div>
<div id="fcEnsPlot" style="height: auto"></div>
<div id="fcProbTable" style="height: auto"></div>
Expand All @@ -56,17 +46,22 @@ <h2 class="modal-title">Streamflow Results</h2>
<div class="panel panel-default">
<div id="retroPlot" class="panel-body modal-chart" style="height: auto"></div>
<div id="dayAvgPlot" class="panel-body modal-chart" style="height: auto"></div>
<div id="monAvgPlot" class="panel-body modal-chart" style="height: auto"></div>
<div id="annAvgPlot" class="panel-body modal-chart" style="height: auto"></div>
<div id="fdcPlot" class="panel-body modal-chart" style="height: auto"></div>
</div>
</div><!-- /.tab-pane -->
</div>
</div>
<div class="modal-footer">
<a class="btn btn-success" role="button" id="download-forecast-btn" target="_blank">Download Forecast</a>
<a class="btn btn-success" role="button" id="download-historical-btn" target="_blank" style="display: none">Download
Historical Data</a>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
<div class="modal-footer" style="justify-content: space-between">
<div id="forecast-date-selector">
<label for="forecast_date">Forecast Date: </label>
<input type="date" id="forecast_date" name="forecast_date" min="2024-07-01" max="{% now "Y-m-d" %}" value="{% now "Y-m-d" %}" onkeydown="return false">
</div>
<div>
<a class="btn btn-success" role="button" id="download-forecast-btn" target="_blank">Download Forecast</a>
<a class="btn btn-success" role="button" id="download-historical-btn" target="_blank" style="display: none">Download Historical Data</a>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Expand All @@ -91,9 +86,7 @@ <h2 style="text-align: center">GEOGLOWS Streamflow</h2>
The model provides a few main outputs:
<ul>
<li>Daily 15 day ensemble forecasts at 3 hour time steps</li>
<li>Retrospective simulation of daily average flows covering 1 January 2024 to the present with 5-12 days
of lag
</li>
<li>Retrospective daily average flows from 1 January 2024 to present with 5-12 days of lag</li>
<li>GIS datasets for the streams and catchments</li>
<li>Model configuration files</li>
<li>River context information such as river names and country</li>
Expand All @@ -102,9 +95,11 @@ <h2 style="text-align: center">GEOGLOWS Streamflow</h2>
<h2 style="text-align: center">VIIRS 5-Day Composite Imagery</h2>
<p>VIIRS imagery is a web mapping service layer derived from the VIIRS satellites' optical sensors.</p>
<a href="https://www.star.nesdis.noaa.gov/jpss/images/Harvey/show/VIIRSFloodDetectionMapQuickGuide.pdf" target="_blank">VIIRS
Flood Detection Map Quick Guide (PDF) (noaa.gov)</a><br>
Flood Detection Map Quick Guide (PDF) (noaa.gov)</a>
<br>
<a href="https://www.ssec.wisc.edu/flood-map-demo/" target="_blank">VIIRS Quick Start Guide/Flood Map Demo
(wisc.edu)</a><br>
(wisc.edu)</a>
<br>
<img src="{% static 'hydroviewer/images/VIIRS_legend.png' %}" alt="legend" style="width: 80%; margin: auto">
<ul>
<li>MS: missing data (transparent)</li>
Expand Down Expand Up @@ -145,12 +140,12 @@ <h2 style="text-align: center">VIIRS 5-Day Composite Imagery</h2>
// URLS
const URL_getForecastData = "{% url 'hydroviewer:get-forecast' %}";
const URL_getHistoricalData = "{% url 'hydroviewer:get-retrospective' %}";
const URL_findReachID = "{% url 'hydroviewer:find-river' %}";
const URL_findRiverID = "{% url 'hydroviewer:find-river' %}";

// CONSTANTS
const REST_ENDPOINT = "{{ endpoint }}";
const REST_VERSION = "{{ version }}";
const ESRI_LAYER_URL = "https://livefeeds2.arcgis.com/arcgis/rest/services/GEOGLOWS/GlobalWaterModel_Medium/MapServer";
const ESRI_LAYER_URL = "https://livefeeds3.arcgis.com/arcgis/rest/services/GEOGLOWS/GlobalWaterModel_Medium/MapServer";
const VIIRS_LAYER_URL = "https://floods.ssec.wisc.edu/tiles/RIVER-FLDglobal-composite/{z}/{x}/{y}.png";
</script>
{# Leaflet #}
Expand All @@ -159,12 +154,12 @@ <h2 style="text-align: center">VIIRS 5-Day Composite Imagery</h2>
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<script
src="https://unpkg.com/[email protected].11/dist/esri-leaflet.js"
integrity="sha512-qSE34Lz9JHdaPt3AZsi6L3dcqYHO9nnQEIxfrkgrRqqe+R0DPuAMu+j2vlb92zPka9O+XHZV4+9G5/rHT5ADsQ=="
src="https://unpkg.com/[email protected].12/dist/esri-leaflet.js"
integrity="sha512-G4+fuKc1B96F0sUG7eKtgiJr0JM3iQC4bfc8WtYvmm7msHES0Hst2mLWASPb8zZk91tqMG86AjP0tgXIEFPPUA=="
crossorigin=""></script>
<script
src="https://unpkg.com/[email protected].2/dist/esri-leaflet-vector.js"
integrity="sha512-EBPUr/aLfZBYAFeLJ2WDvTYjjFKAOa6nlySW/EBEalUk+vCWJca51+QW8sa+R/Bg+Umzs7OkL6LyR/OlcjaXiA=="
src="https://unpkg.com/[email protected].3/dist/esri-leaflet-vector.js"
integrity="sha512-/H7f4mjvCB73Rsi7cWCW0Z3Zl1InqvtGOQsipk5ClXhAxfrw6GSjEnPz2VVTuh7dE29ws8tS3OGHowmkEae2/A=="
crossorigin=""></script>
{# Plotly #}
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
Expand Down

0 comments on commit 33d38b2

Please sign in to comment.