diff --git a/install.yml b/install.yml index 8537097..e0dd76d 100644 --- a/install.yml +++ b/install.yml @@ -1,5 +1,5 @@ # This file should be committed to your app code. -version: 1.5.1 +version: 1.7 # This should match the app - package name in your setup.py name: geoglows_hydroviewer diff --git a/setup.py b/setup.py index 584b820..05b4955 100644 --- a/setup.py +++ b/setup.py @@ -16,12 +16,12 @@ setup( name=release_package, - version='1.5.1', + version='1.7', description='Interfaces with the GEOGloWS ECMWF Streamflow model developed by the BYU Hydroinformatics lab.', long_description='Contains an interactive map interface for retrieving data using the ESRI Dynamic Mapping Service ' 'retrieved from the Living Atlas. Includes tools for extracting a subset of the delineated ' ' shapefiles and creating a customized hydroviewer interface.', - keywords='geoglows, ', + keywords='geoglows, ecmwf, streamflow', author='Riley Hales, Kyler Ashby', author_email='', url='', diff --git a/tethysapp/geoglows_hydroviewer/app.py b/tethysapp/geoglows_hydroviewer/app.py index 0bc3efe..67859fd 100644 --- a/tethysapp/geoglows_hydroviewer/app.py +++ b/tethysapp/geoglows_hydroviewer/app.py @@ -6,7 +6,6 @@ class GeoglowsHydroviewer(TethysAppBase): """ Tethys app class for the GEOGloWS ECMWF Streamflow Hydroviewer """ - name = 'GEOGloWS ECMWF Streamflow Hydroviewer' index = 'geoglows_hydroviewer:home' icon = 'geoglows_hydroviewer/images/water.jpeg' @@ -20,7 +19,6 @@ class GeoglowsHydroviewer(TethysAppBase): def url_maps(self): UrlMap = url_map_maker(self.root_url) - return ( # the geoglows hydroviewer page (home page) UrlMap(name='home', @@ -31,15 +29,18 @@ def url_maps(self): controller=f'{self.package}.controllers.hydroshare_view'), # handles the requests to get the various plots in the app modals - UrlMap(name='get_streamflow', - url=f'{self.root_url}/hydroviewer/getStreamflow', - controller=f'{self.package}.controllers.get_streamflow'), + UrlMap(name='getForecastData', + url=f'{self.root_url}/hydroviewer/getForecastData', + controller=f'{self.package}.controllers.get_forecast_data'), + UrlMap(name='getHistoricalData', + url=f'{self.root_url}/hydroviewer/getHistoricalData', + controller=f'{self.package}.controllers.get_historical_data'), + UrlMap(name='getBiasAdjusted', + url=f'{self.root_url}/hydroviewer/getBiasAdjusted', + controller=f'{self.package}.controllers.get_bias_adjusted'), UrlMap(name='upload_new_observations', url=f'{self.root_url}/hydroviewer/upload_new_observations', controller=f'{self.package}.manage_uploaded_observations.upload_new_observations'), - UrlMap(name='correct_bias', - url=f'{self.root_url}/hydroviewer/correctBias', - controller=f'{self.package}.controllers.correct_bias'), # some other utilities UrlMap(name='find_reach_id', @@ -51,7 +52,7 @@ def url_maps(self): url=f'{self.root_url}/getGaugeGeoJSON', controller=f'{self.package}.controllers.get_gauge_geojson'), - # geoglows hydroviewer creator main pages + # geoglows hydroviewer creator main pages (navigable) UrlMap(name='geoglows_hydroviewer_creator', url=f'{self.root_url}/creator', controller=f'{self.package}.controllers_creator.home'), @@ -62,7 +63,7 @@ def url_maps(self): url=f'{self.root_url}/creator/render', controller=f'{self.package}.controllers_creator.render_hydroviewer'), - # creator pages and urls for adding/deleting projects + # urls for adding/deleting projects (non-navigable) UrlMap(name='add_new_project', url=f'{self.root_url}/creator/add-new-project', controller=f'{self.package}.controllers_creator.add_new_project'), @@ -70,7 +71,7 @@ def url_maps(self): url=f'{self.root_url}/creator/delete_existing_project', controller=f'{self.package}.controllers_creator.delete_existing_project'), - # geoprocessing shapefiles urls + # geoprocessing shapefiles urls (non-navigable) UrlMap(name='geoprocess_idregion', url=f'{self.root_url}/creator/project/geoprocessing/geoprocess_idregion', controller=f'{self.package}.controllers_creator_geoprocess.geoprocess_hydroviewer_idregion'), @@ -81,24 +82,28 @@ def url_maps(self): url=f'{self.root_url}/creator/project/geoprocessing/geoprocess_zip_shapefiles', controller=f'{self.package}.controllers_creator_geoprocess.geoprocess_zip_shapefiles'), - # project editing urls and pages + # project boundary editing pages (navigable) UrlMap(name='draw_boundaries', url=f'{self.root_url}/creator/project/edit/draw_boundaries', - controller=f'{self.package}.controllers_creator.draw_hydroviewer_boundaries'), - UrlMap(name='choose_boundaries', - url=f'{self.root_url}/creator/project/edit/choose_boundaries', - controller=f'{self.package}.controllers_creator.choose_hydroviewer_boundaries'), - UrlMap(name='upload_boundaries', - url=f'{self.root_url}/creator/project/edit/upload_boundaries', - controller=f'{self.package}.controllers_creator.upload_boundary'), + controller=f'{self.package}.controllers_creator.draw_boundaries'), + UrlMap(name='choose_boundary_country', + url=f'{self.root_url}/creator/project/edit/choose_boundary_country', + controller=f'{self.package}.controllers_creator.choose_boundary_country'), + UrlMap(name='boundary_by_outlet', + url=f'{self.root_url}/creator/project/edit/boundary_by_outlet', + controller=f'{self.package}.controllers_creator.boundary_by_outlet'), + # project boundary save/retrieve urls (non-navigable) UrlMap(name='save_boundaries', url=f'{self.root_url}/creator/project/edit/save_boundaries', controller=f'{self.package}.controllers_creator.save_boundaries'), + UrlMap(name='find_upstream_boundaries', + url=f'{self.root_url}/creator/project/edit/find_upstream_boundaries', + controller=f'{self.package}.controllers_creator.find_upstream_boundaries'), UrlMap(name='retrieve_boundaries', url=f'{self.root_url}/creator/project/edit/retrieve_boundaries', controller=f'{self.package}.controllers_creator.retrieve_hydroviewer_boundaries'), - # project and shapefile exporting options + # project and shapefile exporting options (non-navigable) UrlMap(name='export_zipfile', url=f'{self.root_url}/creator/project/export/zipfile', controller=f'{self.package}.controllers_creator_export.export_zipfile'), @@ -113,6 +118,7 @@ def url_maps(self): controller=f'{self.package}.controllers_creator_export.export_html'), ) + def custom_settings(self): return ( CustomSetting( diff --git a/tethysapp/geoglows_hydroviewer/controllers.py b/tethysapp/geoglows_hydroviewer/controllers.py index c843d52..ba53f03 100644 --- a/tethysapp/geoglows_hydroviewer/controllers.py +++ b/tethysapp/geoglows_hydroviewer/controllers.py @@ -34,6 +34,8 @@ ('North America', 'north_america-geoglows', '43ae93136e10439fbf2530e02156caf0'), ) +gsf.ENDPOINT = gsf.AZURE + def home(request): """ @@ -63,11 +65,9 @@ def home(request): context = { # constants 'endpoint': gsf.ENDPOINT, - # uploaded data 'uploaded_observations': uploaded_observations, 'upload_new_observation': upload_new_observation, - # gauge_networks 'gauge_networks': gauge_networks, } @@ -125,30 +125,41 @@ def hydroshare_view(request): context = { # constants 'endpoint': gsf.ENDPOINT, - # uploaded data 'uploaded_observations': uploaded_observations, 'upload_new_observation': upload_new_observation, - # gauge_networks 'gauge_networks': gauge_networks, - + # controls 'watersheds_select_input': watersheds_select_input, } return render(request, 'geoglows_hydroviewer/geoglows_hydroviewer.html', context) -def get_streamflow(request): +def get_forecast_data(request): # get data s = requests.Session() - s.get(gsf.ENDPOINT.replace('api/', '')) reach_id = request.GET['reach_id'] stats = gsf.forecast_stats(reach_id, s=s) - # rec = gsf.forecast_records(reach_id) ens = gsf.forecast_ensembles(reach_id, s=s) + rper = gsf.return_periods(reach_id, s=s) + s.close() + # process data + title_headers = {'Reach ID': reach_id} + # return json of plot html + return JsonResponse(dict( + plot=gpp.forecast_stats(stats, rper, titles=title_headers, outformat='plotly_html'), + table=gpp.probabilities_table(stats, ens, rper), + )) + + +def get_historical_data(request): + # get data + s = requests.Session() + reach_id = request.GET['reach_id'] hist = gsf.historic_simulation(reach_id, s=s) - rper = gsf.return_periods(reach_id) + rper = gsf.return_periods(reach_id, s=s) s.close() # process data dayavg = hydrostats.data.daily_average(hist, rolling=True) @@ -156,18 +167,15 @@ def get_streamflow(request): title_headers = {'Reach ID': reach_id} # return json of plot html return JsonResponse(dict( - # fp=gpp.hydroviewer(rec, stats, ens, rper, titles=title_headers, outformat='plotly_html', record_days=0), - fp=gpp.forecast_stats(stats, rper, titles=title_headers, outformat='plotly_html'), - hp=gpp.historic_simulation(hist, rper, titles=title_headers, outformat='plotly_html'), - dp=gpp.daily_averages(dayavg, titles=title_headers, outformat='plotly_html'), - mp=gpp.monthly_averages(monavg, titles=title_headers, outformat='plotly_html'), + plot=gpp.historic_simulation(hist, rper, titles=title_headers, outformat='plotly_html'), + table=gpp.return_periods_table(rper), + dayavg=gpp.daily_averages(dayavg, titles=title_headers, outformat='plotly_html'), + monavg=gpp.monthly_averages(monavg, titles=title_headers, outformat='plotly_html'), fdp=gpp.flow_duration_curve(hist, titles=title_headers, outformat='plotly_html'), - prob_table=gpp.probabilities_table(stats, ens, rper), - rp_table=gpp.return_periods_table(rper), )) -def correct_bias(request): +def get_bias_adjusted(request): # accept the parameters from the user data = request.GET.dict() network = data.get('gauge_network', False) diff --git a/tethysapp/geoglows_hydroviewer/controllers_creator.py b/tethysapp/geoglows_hydroviewer/controllers_creator.py index 84a2064..141111e 100644 --- a/tethysapp/geoglows_hydroviewer/controllers_creator.py +++ b/tethysapp/geoglows_hydroviewer/controllers_creator.py @@ -3,6 +3,7 @@ import shutil import urllib.parse +import geoglows.streamflow as gsf import geopandas as gpd from django.contrib import messages from django.http import JsonResponse @@ -11,8 +12,10 @@ from tethys_sdk.permissions import login_required from .app import GeoglowsHydroviewer as App +from .hydroviewer_creator_tools import get_livingatlas_geojson from .hydroviewer_creator_tools import get_project_directory from .hydroviewer_creator_tools import shapefiles_downloaded +from .hydroviewer_creator_tools import walk_upstream SHAPE_DIR = App.get_custom_setting('global_delineation_shapefiles_directory') @@ -194,7 +197,7 @@ def render_hydroviewer(request): @login_required() -def draw_hydroviewer_boundaries(request): +def draw_boundaries(request): project = request.GET.get('project', False) if not project: messages.error(request, 'Unable to find this project') @@ -232,29 +235,20 @@ def draw_hydroviewer_boundaries(request): @login_required() -def save_boundaries(request): - proj_dir = get_project_directory(request.POST['project']) - - with open(os.path.join(proj_dir, 'boundaries.json'), 'w') as gj: - gj.write(request.POST.get('geojson')) - - lat = round(float(request.POST.get('center_lat')), 4) - lon = round(float(request.POST.get('center_lng')), 4) - with open(os.path.join(proj_dir, 'export_configs.json'), 'r') as a: - ec = json.loads(a.read()) - ec['zoom'] = request.POST.get('zoom', 4) - ec['center'] = f'{lat},{lon}' - with open(os.path.join(proj_dir, 'export_configs.json'), 'w') as a: - a.write(json.dumps(ec)) - - gjson_file = gpd.read_file(os.path.join(proj_dir, 'boundaries.json')) - gjson_file = gjson_file.to_crs("EPSG:3857") - gjson_file.to_file(os.path.join(proj_dir, 'projected_boundaries')) - return JsonResponse({'status': 'success'}) +def boundary_by_outlet(request): + project = request.GET.get('project', False) + if not project: + messages.error(request, 'Unable to find this project') + return redirect(reverse('geoglows_hydroviewer:geoglows_hydroviewer_creator')) + context = { + 'project': project, + 'project_title': project.replace('_', ' '), + } + return render(request, 'geoglows_hydroviewer/creator_boundaries_outlet.html', context) @login_required() -def choose_hydroviewer_boundaries(request): +def choose_boundary_country(request): project = request.GET.get('project', False) if not project: messages.error(request, 'Unable to find this project') @@ -299,42 +293,81 @@ def choose_hydroviewer_boundaries(request): 'regions': regions, 'geojson': bool(os.path.exists(os.path.join(get_project_directory(project), 'boundaries.json'))), } - return render(request, 'geoglows_hydroviewer/creator_boundaries_choose_predefined.html', context) + return render(request, 'geoglows_hydroviewer/creator_boundaries_choose.html', context) @login_required() -def retrieve_hydroviewer_boundaries(request): - proj_dir = get_project_directory(request.GET['project']) - with open(os.path.join(proj_dir, 'boundaries.json'), 'r') as geojson: - return JsonResponse(json.load(geojson)) +def save_boundaries(request): + proj_dir = get_project_directory(request.POST['project']) + + geojson = request.POST.get('geojson', False) + esri = request.POST.get('esri', False) + if geojson: + with open(os.path.join(proj_dir, 'boundaries.json'), 'w') as gj: + gj.write(geojson) + elif esri: + with open(os.path.join(proj_dir, 'boundaries.json'), 'w') as gj: + gj.write(get_livingatlas_geojson(esri)) + else: + return JsonResponse({'status': 'fail'}) + + lat = round(float(request.POST.get('center_lat')), 4) + lon = round(float(request.POST.get('center_lng')), 4) + with open(os.path.join(proj_dir, 'export_configs.json'), 'r') as a: + ec = json.loads(a.read()) + ec['zoom'] = request.POST.get('zoom', 4) + ec['center'] = f'{lat},{lon}' + with open(os.path.join(proj_dir, 'export_configs.json'), 'w') as a: + a.write(json.dumps(ec)) + + return JsonResponse({'status': 'success'}) @login_required() -def upload_boundary(request): - project = request.POST.get('project', False) +def find_upstream_boundaries(request): + project = request.GET.get('project', False) + reachid = int(request.GET.get('reachid')) if not project: return JsonResponse({'status': 'error', 'error': 'project not found'}) proj_dir = get_project_directory(project) - # make the projected selections folder - tmp_dir = os.path.join(proj_dir, 'projected_boundaries') - if os.path.exists(tmp_dir): - shutil.rmtree(tmp_dir) - os.mkdir(tmp_dir) - - # save the uploaded shapefile to that folder - files = request.FILES.getlist('files') - for file in files: - file_name = 'projected_boundaries' + os.path.splitext(file.name)[-1] - with open(os.path.join(tmp_dir, file_name), 'wb') as dst: - for chunk in file.chunks(): - dst.write(chunk) - - # read the uploaded shapefile with geopandas and save it to selections.geojson - boundaries_gdf = gpd.read_file(os.path.join(tmp_dir, 'projected_boundaries.shp')) - boundaries_gdf = boundaries_gdf.to_crs("EPSG:3857") - boundaries_gdf.to_file(os.path.join(tmp_dir, 'projected_boundaries.shp')) - boundaries_gdf = boundaries_gdf.to_crs("EPSG:4326") - boundaries_gdf.to_file(os.path.join(proj_dir, "boundaries.json"), driver='GeoJSON') + try: + # remove the boundaries if they exist + boundary_json = os.path.join(proj_dir, "boundaries.json") + if os.path.exists(boundary_json): + os.remove(boundary_json) + + # figure out the region stream network shapefile to read and then open it + region = gsf.reach_to_region(reachid) + zipped_shp = os.path.join(SHAPE_DIR, f'{region}-catchment.zip') + gdf = gpd.read_file("zip:///" + os.path.join(zipped_shp, f'{region}-catchment.shp')) + gdf = gdf.to_crs(epsg=4326) + gdf = gdf[['COMID', 'NextDownID', 'geometry']] + + # traverse the network, select, dissolve + upstream = walk_upstream(gdf, reachid, 'COMID', 'NextDownID') + gdf = gdf[gdf['COMID'].isin(upstream)] + gdf['dissolve'] = 'dissolve' + gdf = gdf.dissolve(by="dissolve") + gdf = gdf[['geometry']] + gdf.to_file(boundary_json, driver='GeoJSON') + + # write the zoom/centroid info to the configs json + with open(os.path.join(proj_dir, 'export_configs.json'), 'r') as a: + ec = json.loads(a.read()) + ec['zoom'] = 6 + ec['center'] = f'{gdf.centroid.y[0]},{gdf.centroid.x[0]}' + with open(os.path.join(proj_dir, 'export_configs.json'), 'w') as a: + a.write(json.dumps(ec)) + + return JsonResponse({'status': 'success'}) + + except Exception: + return JsonResponse({'status': 'fail'}) - return JsonResponse({'status': 'success'}) + +@login_required() +def retrieve_hydroviewer_boundaries(request): + proj_dir = get_project_directory(request.GET['project']) + with open(os.path.join(proj_dir, 'boundaries.json'), 'r') as geojson: + return JsonResponse(json.load(geojson)) diff --git a/tethysapp/geoglows_hydroviewer/controllers_creator_geoprocess.py b/tethysapp/geoglows_hydroviewer/controllers_creator_geoprocess.py index 4fabe31..d16b0db 100644 --- a/tethysapp/geoglows_hydroviewer/controllers_creator_geoprocess.py +++ b/tethysapp/geoglows_hydroviewer/controllers_creator_geoprocess.py @@ -19,7 +19,8 @@ def geoprocess_hydroviewer_idregion(request): if not project: raise FileNotFoundError('project directory not found') proj_dir = get_project_directory(project) - gjson_gdf = gpd.read_file(os.path.join(proj_dir, 'projected_boundaries', 'projected_boundaries.shp')) + gjson_gdf = gpd.read_file(os.path.join(proj_dir, 'boundaries.json')) + gjson_gdf = gjson_gdf.to_crs(epsg=3857) for region_zip in glob.glob(os.path.join(SHAPE_DIR, '*-boundary.zip')): region_name = os.path.splitext(os.path.basename(region_zip))[0] @@ -45,7 +46,8 @@ def geoprocess_hydroviewer_clip(request): shutil.rmtree(dl_folder) os.mkdir(dl_folder) - gjson_gdf = gpd.read_file(os.path.join(proj_dir, 'projected_boundaries', 'projected_boundaries.shp')) + gjson_gdf = gpd.read_file(os.path.join(proj_dir, 'boundaries.json')) + gjson_gdf = gjson_gdf.to_crs(epsg=3857) dl_name = region_name.replace('boundary', 'drainageline') dl_path = os.path.join(SHAPE_DIR, dl_name + '.zip', dl_name + '.shp') dl_gdf = gpd.read_file("zip:///" + dl_path) @@ -93,8 +95,6 @@ def geoprocess_zip_shapefiles(request): zipped.write(component, arcname=os.path.basename(component)) shutil.rmtree(os.path.join(proj_dir, 'drainageline_shapefile')) - shutil.rmtree(os.path.join(proj_dir, 'projected_boundaries')) - return JsonResponse({'status': 'success'}) except FileNotFoundError as e: diff --git a/tethysapp/geoglows_hydroviewer/hydroviewer_creator_tools.py b/tethysapp/geoglows_hydroviewer/hydroviewer_creator_tools.py index 9dba278..ff36f3f 100644 --- a/tethysapp/geoglows_hydroviewer/hydroviewer_creator_tools.py +++ b/tethysapp/geoglows_hydroviewer/hydroviewer_creator_tools.py @@ -1,6 +1,9 @@ import glob import os +import pandas as pd +import requests + from .app import GeoglowsHydroviewer as App SHAPE_DIR = App.get_custom_setting('global_delineation_shapefiles_directory') @@ -17,3 +20,111 @@ def shapefiles_downloaded(): if len(shape_dir_contents) == 39: return True return False + + +def walk_upstream(df: pd.DataFrame, target_id: int, id_col: str, next_col: str, order_col: str = None, + same_order: bool = False) -> tuple: + """ + Traverse a stream network table containing a column of unique ID's, a column of the ID for the stream/basin + downstream of that point, and, optionally, a column containing the stream order. + (c) Riley Hales, all rights reserved. This function is from a not-yet-published python package. + + Args: + df (pd.DataFrame): a pandas DataFrame containing the id_col, next_col, and order_col if same_order is True + target_id (int): the ID of the stream to begin the search from + id_col (str): name of the DataFrame column which contains stream/basin ID's + next_col (str): name of the DataFrame column which contains stream/basin ID's of the downstream segments + order_col (str): name of the DataFrame column which contains stream orders + same_order (bool): True limits searching to streams of the same order as the starting stream. False searches + all streams until the head of each branch is found + + Returns: + Tuple of stream ids in the order they come from the starting point. If you chose same_order = False, the + streams will appear in order on each upstream branch but the various branches will appear mixed in the tuple in + the order they were encountered by the iterations. + """ + df_ = df[[id_col, next_col]] + + # start a list of the upstream ids + upstream_ids = [target_id, ] + upstream_rows = df_[df_[next_col] == target_id] + + while not upstream_rows.empty or len(upstream_rows) > 0: + print('yes') + if len(upstream_rows) == 1: + upstream_ids.append(upstream_rows[id_col].values[0]) + upstream_rows = df_[df_[next_col] == upstream_rows[id_col].values[0]] + elif len(upstream_rows) > 1: + for s_id in upstream_rows[id_col].values.tolist(): + upstream_ids += list(walk_upstream(df_, s_id, id_col, next_col, order_col, same_order)) + upstream_rows = df_[df_[next_col] == s_id] + return tuple(set(upstream_ids)) + + +def get_livingatlas_geojson(location: str = None) -> dict: + """ + Requests a geojson from the ESRI living atlas services for World Regions or Generalized Country Boundaries + + Args: + location: the name of the Country or World Region, properly spelled and capitalized + + Returns: + dict + """ + countries = [ + 'Afghanistan', 'Albania', 'Algeria', 'American Samoa', 'Andorra', 'Angola', 'Anguilla', 'Antarctica', + 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Aruba', 'Australia', 'Austria', 'Azerbaijan', 'Bahamas', + 'Bahrain', 'Baker Island', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bermuda', + 'Bhutan', 'Bolivia', 'Bonaire', 'Bosnia and Herzegovina', 'Botswana', 'Bouvet Island', 'Brazil', + 'British Indian Ocean Territory', 'British Virgin Islands', 'Brunei Darussalam', 'Bulgaria', 'Burkina Faso', + 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Cayman Islands', 'Central African Republic', + 'Chad', 'Chile', 'China', 'Christmas Island', 'Cocos Islands', 'Colombia', 'Comoros', 'Congo', 'Congo DRC', + 'Cook Islands', 'Costa Rica', "Côte d'Ivoire", 'Croatia', 'Cuba', 'Curacao', 'Cyprus', 'Czech Republic', + 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'Ecuador', 'Egypt', 'El Salvador', + 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Falkland Islands', 'Faroe Islands', 'Fiji', + 'Finland', 'France', 'French Guiana', 'French Polynesia', 'French Southern Territories', 'Gabon', 'Gambia', + 'Georgia', 'Germany', 'Ghana', 'Gibraltar', 'Glorioso Island', 'Greece', 'Greenland', 'Grenada', + 'Guadeloupe', 'Guam', 'Guatemala', 'Guernsey', 'Guinea', 'Guinea-Bissau', 'Guyana', 'Haiti', + 'Heard Island and McDonald Islands', 'Honduras', 'Howland Island', 'Hungary', 'Iceland', 'India', + 'Indonesia', 'Iran', 'Iraq', 'Ireland', 'Isle of Man', 'Israel', 'Italy', 'Jamaica', 'Jan Mayen', 'Japan', + 'Jarvis Island', 'Jersey', 'Johnston Atoll', 'Jordan', 'Juan De Nova Island', 'Kazakhstan', 'Kenya', + 'Kiribati', 'Kuwait', 'Kyrgyzstan', 'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', + 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', + 'Marshall Islands', 'Martinique', 'Mauritania', 'Mauritius', 'Mayotte', 'Mexico', 'Micronesia', + 'Midway Islands', 'Moldova', 'Monaco', 'Mongolia', 'Montenegro', 'Montserrat', 'Morocco', 'Mozambique', + 'Myanmar', 'Namibia', 'Nauru', 'Nepal', 'Netherlands', 'New Caledonia', 'New Zealand', 'Nicaragua', 'Niger', + 'Nigeria', 'Niue', 'Norfolk Island', 'North Korea', 'Northern Mariana Islands', 'Norway', 'Oman', + 'Pakistan', 'Palau', 'Palestinian Territory', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', + 'Philippines', 'Pitcairn', 'Poland', 'Portugal', 'Puerto Rico', 'Qatar', 'Réunion', 'Romania', + 'Russian Federation', 'Rwanda', 'Saba', 'Saint Barthelemy', 'Saint Eustatius', 'Saint Helena', + 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Martin', 'Saint Pierre and Miquelon', + 'Saint Vincent and the Grenadines', 'Samoa', 'San Marino', 'Sao Tome and Principe', 'Saudi Arabia', + 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Sint Maarten', 'Slovakia', 'Slovenia', + 'Solomon Islands', 'Somalia', 'South Africa', 'South Georgia', 'South Korea', 'South Sudan', 'Spain', + 'Sri Lanka', 'Sudan', 'Suriname', 'Svalbard', 'Swaziland', 'Sweden', 'Switzerland', 'Syria', 'Tajikistan', + 'Tanzania', 'Thailand', 'The Former Yugoslav Republic of Macedonia', 'Timor-Leste', 'Togo', 'Tokelau', + 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Turks and Caicos Islands', 'Tuvalu', + 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'Uruguay', + 'US Virgin Islands', 'Uzbekistan', 'Vanuatu', 'Vatican City', 'Venezuela', 'Vietnam', 'Wake Island', + 'Wallis and Futuna', 'Yemen', 'Zambia', 'Zimbabwe'] + regions = ('Antarctica', 'Asiatic Russia', 'Australia/New Zealand', 'Caribbean', 'Central America', 'Central Asia', + 'Eastern Africa', 'Eastern Asia', 'Eastern Europe', 'European Russia', 'Melanesia', 'Micronesia', + 'Middle Africa', 'Northern Africa', 'Northern America', 'Northern Europe', 'Polynesia', 'South America', + 'Southeastern Asia', 'Southern Africa', 'Southern Asia', 'Southern Europe', 'Western Africa', + 'Western Asia', 'Western Europe') + + # get the geojson data from esri + base = 'https://services.arcgis.com/P3ePLMYs2RVChkJx/ArcGIS/rest/services/' + + if location is None: + return {'countries': countries, 'regions': regions} + elif location in regions: + url = base + 'World_Regions/FeatureServer/0/query?f=pgeojson&outSR=4326&where=REGION+%3D+%27' + location + '%27' + elif location in countries: + url = base + f'World__Countries_Generalized_analysis_trim/FeatureServer/0/query?' \ + f'f=pgeojson&outSR=4326&where=NAME+%3D+%27{location}%27' + else: + raise Exception('Country or World Region not recognized') + + req = requests.get(url=url) + return req.text diff --git a/tethysapp/geoglows_hydroviewer/public/js/creator_chooseBoundary.js b/tethysapp/geoglows_hydroviewer/public/js/creator_chooseBoundary.js index 81674f5..bd15f47 100644 --- a/tethysapp/geoglows_hydroviewer/public/js/creator_chooseBoundary.js +++ b/tethysapp/geoglows_hydroviewer/public/js/creator_chooseBoundary.js @@ -98,10 +98,6 @@ function regionsESRI() { style: {color: 'rgb(0,0,0)', opacity: 1, weight: 1, fillColor: 'rgba(0,0,0,0)', fillOpacity: 'rgba(0,0,0,0)'}, outSR: 4326, where: where, - onEachFeature: function (feature, layer) { - let place = feature.properties.REGION; - layer.bindPopup('' + place + ''); - }, }; if (region !== '') {params['where'] = "REGION = '" + region + "'"} let layer = L.esri.featureLayer(params); @@ -119,9 +115,6 @@ function countriesESRI() { style: {color: 'rgba(0,0,0,1)', opacity: 1, weight: 1, fillColor: 'rgba(0,0,0,0)', fillOpacity: 'rgba(0,0,0,0)'}, outSR: 4326, where: where, - onEachFeature: function (feature, layer) { - layer.bindPopup('Get timeseries for ' + region + ''); - }, }; let layer = L.esri.featureLayer(params); layer.addTo(mapObj); @@ -133,14 +126,16 @@ function countriesESRI() { let layerRegion = regionsESRI(); $("#submit-boundaries").click(function () { let esri = $("#regions").val(); - console.log(esri); if (esri === '' || esri === null) { esri = $("#countries").val() - console.log(esri); } + const center = mapObj.getCenter(); let data = { - project: $("#project").val(), - esri: esri + project: project, + esri: esri, + zoom: mapObj.getZoom(), + center_lat: Number(center.lat), + center_lng: Number(center.lng), } let return_home_button = $("#return_to_overview"); let load_bar = $("#loading-bar-div"); diff --git a/tethysapp/geoglows_hydroviewer/public/js/creator_drawBoundaries.js b/tethysapp/geoglows_hydroviewer/public/js/creator_drawBoundaries.js index a6692e5..5fc173b 100644 --- a/tethysapp/geoglows_hydroviewer/public/js/creator_drawBoundaries.js +++ b/tethysapp/geoglows_hydroviewer/public/js/creator_drawBoundaries.js @@ -172,7 +172,7 @@ $("#submit-boundaries").on('click', function () { load_status.html('loading...'); let center = mapObj.getCenter(); let data = { - project: $("#project").val(), + project: project, geojson: JSON.stringify(drawnItems.toGeoJSON()), zoom: mapObj.getZoom(), center_lat: Number(center.lat), diff --git a/tethysapp/geoglows_hydroviewer/public/js/creator_outletBoundary.js b/tethysapp/geoglows_hydroviewer/public/js/creator_outletBoundary.js new file mode 100644 index 0000000..fb7de5a --- /dev/null +++ b/tethysapp/geoglows_hydroviewer/public/js/creator_outletBoundary.js @@ -0,0 +1,78 @@ +const mapObj = L.map('map', { + zoom: 3, + minZoom: 2, + boxZoom: true, + maxBounds: L.latLngBounds(L.latLng(-100, -225), L.latLng(100, 225)), + center: [20, 0], +}); +let REACHID; +let marker = null; +let SelectedSegment = L.geoJSON(false, {weight: 5, color: '#00008b'}).addTo(mapObj); +const basemapsJson = { + "ESRI Topographic": L.esri.basemapLayer('Topographic').addTo(mapObj), + "ESRI Terrain": L.layerGroup([L.esri.basemapLayer('Terrain'), L.esri.basemapLayer('TerrainLabels')]), + "ESRI Grey": L.esri.basemapLayer('Gray'), +} +//////////////////////////////////////////////////////////////////////// ADD LEGEND LAT LON BOX +// lat/lon tracking box on the bottom left of the map +let latlon = L.control({position: 'bottomleft'}); +latlon.onAdd = function () { + let div = L.DomUtil.create('div', 'well well-sm'); + div.innerHTML = '
'; + return div; +}; +latlon.addTo(mapObj); +mapObj.on("mousemove", function (event) { + $("#mouse-position").html('Lat: ' + event.latlng.lat.toFixed(4) + ', Lon: ' + event.latlng.lng.toFixed(4)); +}); +const globalLayer = L.esri.dynamicMapLayer({ + url: 'https://livefeeds2.arcgis.com/arcgis/rest/services/GEOGLOWS/GlobalWaterModel_Medium/MapServer', + useCors: false, + layers: [0], +}).addTo(mapObj); +L.control.layers(basemapsJson, { + 'Stream Network': globalLayer, +}, {'collapsed': false}).addTo(mapObj); + +mapObj.on("click", function (event) { + if (mapObj.getZoom() <= 9.5) { + mapObj.flyTo(event.latlng, 10); + return + } else { + mapObj.flyTo(event.latlng) + } + if (marker) { + mapObj.removeLayer(marker) + } + marker = L.marker(event.latlng).addTo(mapObj); + L.esri.identifyFeatures({ + url: 'https://livefeeds2.arcgis.com/arcgis/rest/services/GEOGLOWS/GlobalWaterModel_Medium/MapServer' + }) + .on(mapObj) + // querying point with tolerance + .at([event.latlng['lat'], event.latlng['lng']]) + .tolerance(10) // map pixels to buffer search point + .precision(3) // decimals in the returned coordinate pairs + .run(function (error, featureCollection) { + if (error) { + alert('Error finding the reach_id'); + return + } + SelectedSegment.clearLayers(); + SelectedSegment.addData(featureCollection.features[0].geometry) + REACHID = featureCollection.features[0].properties["COMID (Stream Identifier)"]; + + $.ajax({ + type: 'GET', + url: URL_findUpstreamBoundaries, + data: {reachid: REACHID, project: project}, + dataType: 'json', + success: function (response) { + + }, + error: function (response) { + + }, + }) + }) +}); diff --git a/tethysapp/geoglows_hydroviewer/public/js/creator_project_overview.js b/tethysapp/geoglows_hydroviewer/public/js/creator_project_overview.js index a4d73bd..e6c212e 100644 --- a/tethysapp/geoglows_hydroviewer/public/js/creator_project_overview.js +++ b/tethysapp/geoglows_hydroviewer/public/js/creator_project_overview.js @@ -1,12 +1,16 @@ const mapObj = L.map('preview-map', { zoom: 3, minZoom: 2, - maxZoom: 10, + maxZoom: 12, boxZoom: true, maxBounds: L.latLngBounds(L.latLng(-100, -225), L.latLng(100, 225)), center: [20, 0], }) -const basemapObj = {'ESRI Topographic Map': L.esri.basemapLayer('Topographic').addTo(mapObj)} +const basemapsJson = { + "ESRI Topographic": L.esri.basemapLayer('Topographic').addTo(mapObj), + "ESRI Terrain": L.layerGroup([L.esri.basemapLayer('Terrain'), L.esri.basemapLayer('TerrainLabels')]), + "ESRI Grey": L.esri.basemapLayer('Gray'), +} const message = L.control({position: 'bottomleft'}); message.onAdd = function () { let div = L.DomUtil.create('div', 'well well-sm'); @@ -41,8 +45,19 @@ if (exported) { catchments = L.geoJSON(false); } +const globalLayer = L.esri.dynamicMapLayer({ + url: 'https://livefeeds2.arcgis.com/arcgis/rest/services/GEOGLOWS/GlobalWaterModel_Medium/MapServer', + useCors: false, + layers: [0], +}); + L.control.layers( - basemapObj, - {'Project Boundaries': bounds, 'Catchments': catchments, 'Drainage Lines': drainagelines}, + basemapsJson, + { + 'Global Stream Network': globalLayer, + 'Project Boundaries': bounds, + 'Project Catchments': catchments, + 'Project Drainage Lines': drainagelines + }, {'collapsed': false} ).addTo(mapObj); \ No newline at end of file diff --git a/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_core.js b/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_core.js index 153fb68..74d4da2 100644 --- a/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_core.js +++ b/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_core.js @@ -44,19 +44,26 @@ function findReachID() { $.ajax({ type: 'GET', async: true, - url: URL_find_reach_id + L.Util.getParamString({reach_id: $("#search_reachid_input").val()}), + url: URL_find_reach_id + L.Util.getParamString({reach_id: prompt("Please enter a Reach ID to search for")}), success: function (response) { - if (reachMarker) {mapObj.removeLayer(reachMarker)} + if (reachMarker) { + mapObj.removeLayer(reachMarker) + } reachMarker = L.marker(L.latLng(response['lat'], response['lon'])).addTo(mapObj); mapObj.flyTo(L.latLng(response['lat'], response['lon']), 9); }, - error: function () {alert('Unable to find the reach_id specified')} + error: function () { + alert('Unable to find the reach_id specified') + } }) } function findLatLon() { - if (latlonMarker) {mapObj.removeLayer(latlonMarker)} - let ll = $("#search_latlon_input").val().replace(' ', '').split(',') + if (latlonMarker) { + mapObj.removeLayer(latlonMarker) + } + let ll = prompt("Enter a latitude and longitude coordinate pair separated by a comma. For example, to get to BYU you would enter: 40.25, -111.65", "40.25, -111.65") + ll = ll.replace(" ", '').replace("(", "").replace(")", "").split(',') latlonMarker = L.marker(L.latLng(ll[0], ll[1])).addTo(mapObj); mapObj.flyTo(L.latLng(ll[0], ll[1]), 9); } @@ -79,12 +86,9 @@ function updateDownloadLinks(type) { if (type === 'clear') { $("#download-forecast-btn").attr('href', ''); $("#download-historical-btn").attr('href', ''); - $("#download-seasonal-btn").attr('href', ''); } else if (type === 'set') { $("#download-forecast-btn").attr('href', endpoint + 'ForecastStats/?reach_id=' + REACHID); - $("#download-records-btn").attr('href', endpoint + 'ForecastRecords/?reach_id=' + REACHID); $("#download-historical-btn").attr('href', endpoint + 'HistoricSimulation/?reach_id=' + REACHID); - $("#download-seasonal-btn").attr('href', endpoint + 'SeasonalAverage/?reach_id=' + REACHID); } } //////////////////////////////////////////////////////////////////////// GET BIAS CORRECTED PLOTS FUNCTION @@ -94,7 +98,7 @@ function getBiasCorrectedPlots(gauge_metadata) { data = gauge_metadata; data['gauge_network'] = $("#gauge_networks").val(); } else if (!REACHID) { - alert('No reach-id found'); + alert('No Reach-ID has been chosen. You must successfully retrieve streamflow before attempting calibration'); return } else { let csv = $("#uploaded_observations").val(); @@ -115,7 +119,7 @@ function getBiasCorrectedPlots(gauge_metadata) { type: 'GET', async: true, data: data, - url: URL_correct_bias, + url: URL_getBiasAdjusted, success: function (html) { // forecast tab $("#forecast_tab_link").tab('show'); @@ -183,7 +187,7 @@ $("#hydrograph-csv-form").on('submit', function (e) { uploaded_input.empty(); let new_file_list = response['new_file_list']; for (let i = 0; i < new_file_list.length; i++) { - uploaded_input.append('') + uploaded_input.append('') } }, error: function (response) { @@ -191,58 +195,110 @@ $("#hydrograph-csv-form").on('submit', function (e) { }, }); }); -$("#start-bias-correction-btn").click(function (e) {getBiasCorrectedPlots(false)}) //////////////////////////////////////////////////////////////////////// GET DATA FROM API AND MANAGING PLOTS -const chart_divs = [$("#forecast-chart"), $("#corrected-forecast-chart"), $("#forecast-table"), $("#historical-chart"), $("#historical-table"), $("#daily-avg-chart"), $("#monthly-avg-chart"), $("#flowduration-chart"), $("#volume_plot"), $("#scatters"), $("#stats_table")]; -function getStreamflowPlots() { - updateStatusIcons('load'); - updateDownloadLinks('clear'); - for (let i in chart_divs) {chart_divs[i].html('')} // clear the contents of old chart divs +const chart_divs = [$("#forecast-chart"), $("#forecast-table"), $("#historical-chart"), $("#historical-table"), $("#daily-avg-chart"), $("#monthly-avg-chart"), $("#flowduration-chart"), $("#volume_plot"), $("#scatters"), $("#stats_table")]; + +function clearChartDivs() { + for (let i in chart_divs) { + chart_divs[i].html('') + } +} + +function showGetHistorical() { + $("#historical_tab_link").show(); + $("#get-historical-btn").show(); +} + +function hideGetHistorical() { + $("#historical_tab_link").hide(); + $("#get-historical-btn").hide(); +} + +function hideHistoricalTabs() { + $("#avg_flow_tab_link").hide(); + $("#flow_duration_tab_link").hide(); +} + +function showHistoricalTabs() { + $("#avg_flow_tab_link").show(); + $("#flow_duration_tab_link").show(); +} + +function hideBiasCalibrationTabs() { + $("#bias_correction_tab_link").hide(); +} + +function showBiasCalibrationTabs() { + $("#bias_correction_tab_link").show(); +} + +function getForecastData() { let ftl = $("#forecast_tab_link"); // select divs with jquery so we can reuse them - let fc = chart_divs[0]; ftl.tab('show') + let fc = chart_divs[0]; fc.html(''); fc.css('text-align', 'center'); $.ajax({ type: 'GET', async: true, data: {reach_id: REACHID}, - url: URL_get_streamflow, - success: function (html) { + url: URL_getForecastData, + success: function (response) { // forecast tab ftl.tab('show'); - fc.html(html['fp']); - $("#forecast-table").html(html['prob_table']); + fc.html(response['plot']); + $("#forecast-table").html(response['table']); + updateDownloadLinks('set'); + updateStatusIcons('ready'); + showGetHistorical(); + showBiasCalibrationTabs(); + }, + error: function () { + updateStatusIcons('fail'); + REACHID = null; + } + }) +} + +function getHistoricalData() { + updateStatusIcons('load'); + updateDownloadLinks('clear'); + let tl = $("#historical_tab_link"); // select divs with jquery so we can reuse them + tl.tab('show') + let plotdiv = chart_divs[2]; + plotdiv.html(''); + plotdiv.css('text-align', 'center'); + $.ajax({ + type: 'GET', + async: true, + data: {reach_id: REACHID}, + url: URL_getHistoricalData, + success: function (response) { + showHistoricalTabs(); // historical tab - $("#historical_tab_link").tab('show'); - $("#historical-chart").html(html['hp']); - $("#historical-table").html(html['rp_table']); + tl.tab('show'); + $("#get-historical-btn").hide(); + plotdiv.html(response['plot']); + $("#historical-table").html(response['table']); // average flows tab $("#avg_flow_tab_link").tab('show'); - $("#daily-avg-chart").html(html['dp']); - $("#monthly-avg-chart").html(html['mp']); - // $("#monthly-avg-chart").html(html['sp']); + $("#daily-avg-chart").html(response['dayavg']); + $("#monthly-avg-chart").html(response['monavg']); // flow duration tab $("#flow_duration_tab_link").tab('show'); - $("#flowduration-chart").html(html['fdp']); + $("#flowduration-chart").html(response['fdp']); // update other messages and links - ftl.tab('show'); + tl.tab('show'); updateStatusIcons('ready'); - updateDownloadLinks('set'); - $("#bias_correction_tab_link").show() }, error: function () { updateStatusIcons('fail'); - $("#bias_correction_tab_link").hide() - for (let i in chart_divs) { - chart_divs[i].html('') - } } }) } function fix_buttons(tab) { - let buttons = [$("#download-forecast-btn"), $("#download-historical-btn"), $("#download-averages-btn"), $("#start-bias-correction-btn")] + let buttons = [$("#download-forecast-btn"), $("#download-historical-btn"), $("#start-bias-correction-btn")] for (let i in buttons) { buttons[i].hide() } @@ -250,10 +306,8 @@ function fix_buttons(tab) { buttons[0].show() } else if (tab === 'historical') { buttons[1].show() - } else if (tab === 'averages') { - buttons[2].show() } else if (tab === 'biascorrection') { - buttons[3].show() + buttons[2].show() } fix_chart_sizes(tab); $("#resize_charts").attr({'onclick': "fix_chart_sizes('" + tab + "')"}) @@ -262,8 +316,6 @@ function fix_chart_sizes(tab) { let divs = []; if (tab === 'forecast') { divs = [$("#forecast-chart .js-plotly-plot")] - } else if (tab === 'records') { - divs = [$("#records-chart .js-plotly-plot")] } else if (tab === 'historical') { divs = [$("#historical-5-chart .js-plotly-plot")] } else if (tab === 'averages') { diff --git a/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_esri.js b/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_esri.js index c325b34..79fe6bc 100644 --- a/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_esri.js +++ b/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_esri.js @@ -32,15 +32,20 @@ const slider = $("#time-slider"); slider.change(function () { refreshLayerAnimation() }) + function refreshLayerAnimation() { layerAnimationTime = new Date(startDateTime); layerAnimationTime.setUTCHours(slider.val() * 3); currentDate.html(layerAnimationTime); globalLayer.setTimeRange(layerAnimationTime, endDateTime); } + let animate = false; + function playAnimation(once = false) { - if (!animate) {return} + if (!animate) { + return + } if (layerAnimationTime < endDateTime) { slider.val(Number(slider.val()) + 1) } else { @@ -53,6 +58,7 @@ function playAnimation(once = false) { } setTimeout(playAnimation, 750); } + $("#animationPlay").click(function () { animate = true; playAnimation() @@ -60,11 +66,11 @@ $("#animationPlay").click(function () { $("#animationStop").click(function () { animate = false; }) -$("#animationPlus1").click(function(){ +$("#animationPlus1").click(function () { animate = true; playAnimation(true) }) -$("#animationBack1").click(function(){ +$("#animationBack1").click(function () { if (layerAnimationTime > startDateTime) { slider.val(Number(slider.val()) - 1) } else { @@ -73,7 +79,11 @@ $("#animationBack1").click(function(){ refreshLayerAnimation(); }) //////////////////////////////////////////////////////////////////////// ADD WMS LAYERS FOR DRAINAGE LINES, VIIRS, ETC - SEE HOME.HTML TEMPLATE -let VIIRSlayer = L.tileLayer('https://floods.ssec.wisc.edu/tiles/RIVER-FLDglobal-composite/{z}/{x}/{y}.png', {layers: 'RIVER-FLDglobal-composite: Latest', crossOrigin: true, pane: 'viirs',}); +let VIIRSlayer = L.tileLayer('https://floods.ssec.wisc.edu/tiles/RIVER-FLDglobal-composite/{z}/{x}/{y}.png', { + layers: 'RIVER-FLDglobal-composite: Latest', + crossOrigin: true, + pane: 'viirs', +}); const globalLayer = L.esri.dynamicMapLayer({ url: 'https://livefeeds2.arcgis.com/arcgis/rest/services/GEOGLOWS/GlobalWaterModel_Medium/MapServer', useCors: false, @@ -81,7 +91,11 @@ const globalLayer = L.esri.dynamicMapLayer({ from: startDateTime, to: endDateTime, }).addTo(mapObj); -L.control.layers(basemapsJson, {'Stream Network': globalLayer, 'Gauge Network': gaugeNetwork, 'VIIRS Imagery': VIIRSlayer}, {'collapsed': false}).addTo(mapObj); +L.control.layers(basemapsJson, { + 'Stream Network': globalLayer, + 'Gauge Network': gaugeNetwork, + 'VIIRS Imagery': VIIRSlayer +}, {'collapsed': false}).addTo(mapObj); mapObj.on("click", function (event) { if (mapObj.getZoom() <= 9.5) { @@ -94,9 +108,6 @@ mapObj.on("click", function (event) { mapObj.removeLayer(marker) } marker = L.marker(event.latlng).addTo(mapObj); - for (let i in chart_divs) { - chart_divs[i].html('') - } updateStatusIcons('identify'); $("#chart_modal").modal('show'); L.esri.identifyFeatures({ @@ -122,7 +133,12 @@ mapObj.on("click", function (event) { SelectedSegment.clearLayers(); SelectedSegment.addData(featureCollection.features[0].geometry) REACHID = featureCollection.features[0].properties["COMID (Stream Identifier)"]; + clearChartDivs(); + hideGetHistorical(); + hideHistoricalTabs(); + hideBiasCalibrationTabs(); updateStatusIcons('load'); - getStreamflowPlots(); + updateDownloadLinks('clear'); + getForecastData(); }) }); \ No newline at end of file diff --git a/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_hydroshare.js b/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_hydroshare.js index 352bf94..dad7c76 100644 --- a/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_hydroshare.js +++ b/tethysapp/geoglows_hydroviewer/public/js/geoglows_hydroviewer_hydroshare.js @@ -50,7 +50,7 @@ function getWatershedComponent(layername) { function getDrainageLine(layername) { let region = layername.replace('-drainageline', ''); - return L.tileLayer.wms('https://geoserver.hydroshare.org/geoserver/wms', { + return L.tileLayer.WMFS('https://geoserver.hydroshare.org/geoserver/wms', { version: '1.1.0', layers: 'HS-' + watersheds_hydroshare_ids[region] + ':' + layername + ' ' + layername, useCache: true, @@ -122,7 +122,6 @@ $("#watersheds_select_input").change(function () { mapObj.setMaxZoom(12); }); -// todo mapObj.on("click", function (event) { if (mapObj.getZoom() >= cd_threshold) { if (marker) { @@ -130,13 +129,14 @@ mapObj.on("click", function (event) { } REACHID = drainage_layer.GetFeatureInfo(event); marker = L.marker(event.latlng).addTo(mapObj); - marker.bindPopup('Watershed/Region: ' + $("#watersheds_select_input").val() + '
Reach ID: ' + reachid); - updateStatusIcons('cleared'); - for (let i in chart_divs) { - chart_divs[i].html('') - } - $("#forecast-table").html(''); + marker.bindPopup('Watershed/Region: ' + $("#watersheds_select_input").val() + '
Reach ID: ' + REACHID); $("#chart_modal").modal('show'); - askAPI(); + clearChartDivs(); + hideGetHistorical(); + hideHistoricalTabs(); + hideBiasCalibrationTabs(); + updateStatusIcons('load'); + updateDownloadLinks('clear'); + getForecastData(); } }); \ No newline at end of file diff --git a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/base.html b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/base.html index 00c3dbd..632502e 100644 --- a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/base.html +++ b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/base.html @@ -19,81 +19,46 @@ {% url 'geoglows_hydroviewer:render_hydroviewer' as render %} {% url 'geoglows_hydroviewer:geoglows_hydroviewer_creator' as creator %} - {% if request.path == hydroviewer %} + {% if request.user.is_authenticated %} +
  • App Features
  • +
  • Global Hydroviewer
  • +
  • Hydroviewer Creator
  • + {% endif %} + + {% if request.path == hydroviewer or request.path == hydroshare %}
  • Map Controls
  • - - -
  • -
    - - - - -
    - -
  • - - - Find A Reach ID - -
  • - - - Find A Lat/Lon Location - -
  • - About - VIIRS Imagery - -
  • Bias Correction
  • - Upload New - Observation -
  • - {% gizmo select_input gauge_networks %} - {% if request.user.is_authenticated %} - Go to Hydroviewer Creator + {% if request.path == hydroviewer %} + + +
  • +
    + + + + +
    {% else %} - Go to - Hydroviewer Creator +
  • {% gizmo select_input watersheds_select_input %}
  • {% endif %} - - {% elif request.path == hydroshare %} -
  • Map Controls
  • -
  • {% gizmo select_input watersheds_select_input %}
  • - - Find A Reach ID -
  • - - - Find A Lat/Lon Location - + Zoom to Lat/Lon Coordinates
  • - About - VIIRS Imagery - -
  • Bias Correction
  • - Upload New - Observation -
  • - {% gizmo select_input gauge_networks %} - {% if request.user.is_authenticated %} - Go to Hydroviewer Creator + {% if request.path == hydroviewer %} + Switch to HydroShare Map {% else %} - Go to - Hydroviewer Creator + Switch to ESRI Map {% endif %} - {% else %} -
  • - Return to Hydroviewer Main - Page
  • + {% gizmo select_input gauge_networks %} + {% else %}
  • Hydroviewer Creation Tools
  • Project Selection
  • Project Overview
  • diff --git a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_choose_predefined.html b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_choose.html similarity index 89% rename from tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_choose_predefined.html rename to tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_choose.html index 9a672b3..b4e5809 100644 --- a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_choose_predefined.html +++ b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_choose.html @@ -3,10 +3,9 @@ {% load static %} {% block app_content %} -

    Project: {{ project_title }}

    - -
    -
    +
    +
    +

    Project: {{ project_title }}

    {% gizmo select_input regions %}
    @@ -15,15 +14,17 @@

    Project: {{ project_title }}

    -

    +
    +

    Upload Status: {% if geojson %}A previously determined boundary has already been uploaded{% endif %}

    + Return to Overview +
    -
    +
    {% endblock %} @@ -48,6 +49,7 @@

    Project: {{ project_title }}

    {{ block.super }} diff --git a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_draw.html b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_draw.html index 08bc78a..a5f8a37 100644 --- a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_draw.html +++ b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_draw.html @@ -3,24 +3,30 @@ {% load static %} {% block app_content %} -

    Project: {{ project_title }}

    - -
    -
    +
    +
    +

    Project: {{ project_title }}

    1. {% gizmo select_input watersheds_select_input %}
    2. Zoom/Pan around the map as necessary to find the area you are interested in.
    3. -
    4. Use the controls on the top-left of the map (the pentagon, square, edit, and trash can icons) to draw shapes that contain the areas you want to include in your customized shapefiles.
    5. -
    6. When you are finished drawing, press the "Submit Boundaries" button to save your drawing to your project directory on the server.
    7. +
    8. Use the controls on the top-left of the map (the pentagon, square, edit, and trash can icons) to draw shapes + that contain the areas you want to include in your customized shapefiles. +
    9. +
    10. When you are finished drawing, press the "Submit Boundaries" button to save your drawing to your project + directory on the server. +
    11. -

      Upload Status: {% if geojson %}A previously determined boundary has already been uploaded{% endif %}

      +

      Upload Status: {% if geojson %}A previously determined boundary has already + been uploaded{% endif %}

    12. - +
    -
    +
    {% endblock %} @@ -46,6 +52,7 @@

    Project: {{ project_title }}

    {{ block.super }} diff --git a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_outlet.html b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_outlet.html new file mode 100644 index 0000000..63491ba --- /dev/null +++ b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_boundaries_outlet.html @@ -0,0 +1,47 @@ +{% extends "geoglows_hydroviewer/base.html" %} +{% load tethys_gizmos %} +{% load static %} + +{% block app_content %} +
    +
    +

    Project: {{ project_title }}

    +
    +
    +
    +{% endblock %} + +{% block app_actions %} +
    + +
    + +
    +
    +{% endblock %} + +{% block content_dependent_styles %} + {{ block.super }} + + {# #} + +{% endblock %} + +{% block scripts %} + {{ block.super }} + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_project_overview.html b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_project_overview.html index 376517a..2ed43f8 100644 --- a/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_project_overview.html +++ b/tethysapp/geoglows_hydroviewer/templates/geoglows_hydroviewer/creator_project_overview.html @@ -16,15 +16,15 @@

  • Define Project Boundaries
  • -
    - -
    - + + +
    +
    -
    +
    - - -
    +
    + +
    + +
    +
    {% endif %} {% if boundaries %} @@ -205,30 +208,6 @@
    -