From 1272a52b0e4f1b42e172f90dbd92eda382644b2f Mon Sep 17 00:00:00 2001 From: remic Date: Wed, 3 May 2023 16:29:39 -0400 Subject: [PATCH 1/7] Adding the Compare to... time series options --- controls.py | 2 +- enacts/wat_bal/layout_monit.py | 65 +++++++++++++++++++- enacts/wat_bal/maproom_monit.py | 105 +++++++++++++++++++++++++++++--- 3 files changed, 159 insertions(+), 13 deletions(-) diff --git a/controls.py b/controls.py index 46cb196e4..59dcbed56 100644 --- a/controls.py +++ b/controls.py @@ -148,7 +148,7 @@ def Sentence(*elems): groups.extend(elems[0]) for i in range(start, len(elems) - (1 if tail else 0), 2): - assert (isinstance(elems[i], str) or isinstance(elems[i], html.Span)) + #assert (isinstance(elems[i], str) or isinstance(elems[i], html.Span)) groups.append(dbc.Label(elems[i], size="sm", className="m-1 d-inline-block", width="auto")) groups.extend(elems[i + 1]) diff --git a/enacts/wat_bal/layout_monit.py b/enacts/wat_bal/layout_monit.py index 0d0bc9d06..6177a0c7a 100644 --- a/enacts/wat_bal/layout_monit.py +++ b/enacts/wat_bal/layout_monit.py @@ -188,12 +188,12 @@ def controls_layout(lat_min, lat_max, lon_min, lon_max, lat_label, lon_label): and other characteristics of the soil and plants since planting date of the current season and up to now. It is driven by rainfall and the crop cultivars Kc - that can be changed in the Control Panel below. + that can be changed in the Controls Panel below. """ ), html.P( f""" - Map another day of the simulation using the Date control on the top bar, + Map another day of the simulation using the Date control in the top bar, or by clicking a day of interest on the time series graph.. You can pick a day between planting and today (or last day of available data). """ @@ -204,6 +204,15 @@ def controls_layout(lat_min, lat_max, lon_min, lon_max, lat_label, lon_label): with the controls below or by clicking on the map. """ ), + html.P( + f""" + The current evolution (blue) is put in context by comparing it + to another situation (dashed red) that can be altered + by picking another planting date and/or + another crop (Kc parameters) and/or + another year through the Compare to... panel. + """ + ), html.H5("Water Balance Outputs"), ]+[ html.P([html.H6(val["menu_label"]), html.P(val["description"])]) @@ -218,6 +227,14 @@ def controls_layout(lat_min, lat_max, lon_min, lon_max, lat_label, lon_label): {GLOBAL_CONFIG["institution"]}’s archive with satellite rainfall estimates. """ ), + html.P( + f""" + Total Available Water (TAW) regridded on rainfall data from SoilGrids's + absolute total available water capacity (mm), + aggregated over the Effective Root Zone Depth for Maize + data product. + """ + ), ], style={"position":"relative","height":"30%", "overflow":"scroll"}, ), @@ -311,6 +328,50 @@ def controls_layout(lat_min, lat_max, lon_min, lon_max, lat_label, lon_label): ), dbc.Button(id="submit_kc", children='Submit'), ), + Block( + "Compare to...", + Sentence( + "Planting Date", + DateNoYear("planting2_", 1, CONFIG["planting_month"]), + ), + Sentence( + Number("year_ago", 1, min=0, max=99, html_size=3), + "year(s) ago", + ), + Sentence( + "for", + Text("crop2_name", CONFIG["crop_name"]), + "crop cultivars: initiated at", + ), + Sentence( + Number("kc2_init", CONFIG["kc_v"][0], min=0, max=2, html_size=4), + "through", + Number("kc2_init_length", CONFIG["kc_l"][0], min=0, max=99, html_size=2), + "days of initialization to", + ), + Sentence( + Number("kc2_veg", CONFIG["kc_v"][1], min=0, max=2, html_size=4), + "through", + Number("kc2_veg_length", CONFIG["kc_l"][1], min=0, max=99, html_size=2), + "days of growth to", + ), + Sentence( + Number("kc2_mid", CONFIG["kc_v"][2], min=0, max=2, html_size=4), + "through", + Number("kc2_mid_length", CONFIG["kc_l"][2], min=0, max=99, html_size=2), + "days of mid-season to", + ), + Sentence( + Number("kc2_late", CONFIG["kc_v"][3], min=0, max=2, html_size=4), + "through", + Number("kc2_late_length", CONFIG["kc_l"][3], min=0, max=99, html_size=2), + "days of late-season to", + ), + Sentence( + Number("kc2_end", CONFIG["kc_v"][4], min=0, max=2, html_size=4), + ), + dbc.Button(id="submit_kc2", children='Submit'), + ), ], style={"position":"relative","height":"60%", "overflow":"scroll"}, ), diff --git a/enacts/wat_bal/maproom_monit.py b/enacts/wat_bal/maproom_monit.py index 6cdb4d625..40391699a 100644 --- a/enacts/wat_bal/maproom_monit.py +++ b/enacts/wat_bal/maproom_monit.py @@ -14,6 +14,7 @@ import pandas as pd import numpy as np import urllib +import datetime import xarray as xr import agronomy as ag @@ -306,6 +307,7 @@ def pick_location(n_clicks, click_lat_lng, latitude, longitude): Input("loc_marker", "position"), Input("map_choice", "value"), Input("submit_kc", "n_clicks"), + Input("submit_kc2", "n_clicks"), State("planting_day", "value"), State("planting_month", "value"), State("crop_name", "value"), @@ -318,11 +320,25 @@ def pick_location(n_clicks, click_lat_lng, latitude, longitude): State("kc_late", "value"), State("kc_late_length", "value"), State("kc_end", "value"), + State("planting2_day", "value"), + State("planting2_month", "value"), + State("year_ago", "value"), + State("crop2_name", "value"), + State("kc2_init", "value"), + State("kc2_init_length", "value"), + State("kc2_veg", "value"), + State("kc2_veg_length", "value"), + State("kc2_mid", "value"), + State("kc2_mid_length", "value"), + State("kc2_late", "value"), + State("kc2_late_length", "value"), + State("kc2_end", "value"), ) def wat_bal_plots( marker_pos, map_choice, n_clicks, + n2_clicks, planting_day, planting_month, crop_name, @@ -335,9 +351,25 @@ def wat_bal_plots( kc_late, kc_late_length, kc_end, + planting2_day, + planting2_month, + year_ago, + crop2_name, + kc2_init, + kc2_init_length, + kc2_veg, + kc2_veg_length, + kc2_mid, + kc2_mid_length, + kc2_late, + kc2_late_length, + kc2_end, ): + lat = marker_pos[0] lng = marker_pos[1] + taw = pingrid.sel_snap(xr.open_dataarray(Path(CONFIG["taw_file"])), lat, lng) + kc_periods = pd.TimedeltaIndex( [0, int(kc_init_length), int(kc_veg_length), int(kc_mid_length), int(kc_late_length)], unit="D" ) @@ -348,11 +380,6 @@ def wat_bal_plots( p_d = calc.sel_day_and_month( precip["T"], int(planting_day), calc.strftimeb2int(planting_month) ).squeeze(drop=True).rename("p_d") - #p_d = precip["T"].where( - # lambda x: (x.dt.day == int(planting_day)) - # & (x.dt.month == calc.strftimeb2int(planting_month)), - # drop=True - #).squeeze(drop=True).rename("p_d") precip = precip.where(precip["T"] >= p_d, drop=True) try: precip = pingrid.sel_snap(precip, lat, lng) @@ -364,7 +391,6 @@ def wat_bal_plots( error_fig = pingrid.error_fig(error_msg="Grid box out of data domain") return error_fig precip.load() - taw = pingrid.sel_snap(xr.open_dataarray(Path(CONFIG["taw_file"])), lat, lng) try: sm, drainage, et_crop, et_crop_red, planting_date = ag.soil_plant_water_balance( precip, @@ -379,25 +405,84 @@ def wat_bal_plots( error_msg="Please ensure all input boxes are filled for the calculation to run." ) return error_fig + + kc2_periods = pd.TimedeltaIndex([ + 0, + int(kc2_init_length), + int(kc2_veg_length), + int(kc2_mid_length), + int(kc2_late_length), + ], unit="D") + kc2_params = xr.DataArray(data=[ + float(kc2_init), float(kc2_veg), float(kc2_mid), float(kc2_late), float(kc2_end) + ], dims=["kc_periods"], coords=[kc2_periods]) + precip2 = rr_mrg.precip.isel({"T": slice(-366 * (int(year_ago) + 1), None)}) + p_d2 = calc.sel_day_and_month( + precip2["T"], int(planting2_day), calc.strftimeb2int(planting2_month) + ).isel(T=0, drop=True).squeeze(drop=True).rename("p_d2") + precip2 = precip2.where( + (precip2["T"] >= p_d2) & (precip2["T"] < (p_d2 + np.timedelta64(366, "D"))), + drop=True, + ) + try: + precip2 = pingrid.sel_snap(precip2, lat, lng) + isnan = np.isnan(precip2).sum() + if isnan > 0: + error_fig = pingrid.error_fig(error_msg="Data missing at this location") + return error_fig + except KeyError: + error_fig = pingrid.error_fig(error_msg="Grid box out of data domain") + return error_fig + precip2.load() + try: + sm2, drainage2, et_crop2, et_crop_red2, planting_date2 = ag.soil_plant_water_balance( + precip2, + et=5, + taw=taw, + sminit=taw/3., + kc_params=kc2_params, + planting_date=p_d2, + ) + except TypeError: + error_fig = pingrid.error_fig( + error_msg="Please ensure all input boxes are filled for the calculation to run." + ) + return error_fig + if map_choice == "sm": ts = sm + ts2 = sm2 elif map_choice == "drainage": ts = drainage + ts2 = drainage2 elif map_choice == "et_crop": ts = et_crop + ts2 = et_crop2 + + ts2 = ts2.assign_coords({"T": pd.date_range(datetime.datetime( + p_d2.dt.year.values + int(year_ago), p_d2.dt.month.values, p_d2.dt.day.values + ), periods=ts2["T"].size)}) + wat_bal_graph = pgo.Figure() wat_bal_graph.add_trace( pgo.Scatter( x=ts["T"].dt.strftime("%-d %b %y"), y=ts.values, hovertemplate="%{y} on %{x}", - name="", + name="Current", line=pgo.scatter.Line(color="blue"), + connectgaps=False, ) ) - wat_bal_graph.update_traces( - mode="lines", - connectgaps=False, + wat_bal_graph.add_trace( + pgo.Scatter( + x=ts2["T"].dt.strftime("%-d %b %y"), + y=ts2.values, + hovertemplate="%{y} on %{x}", + name="Comparison", + line=pgo.scatter.Line(color="red", dash="dash"), + connectgaps=False, + ) ) wat_bal_graph.update_layout( xaxis_title="Time", From f51f9506ded02a0f7792394606a2d15b03694006 Mon Sep 17 00:00:00 2001 From: remic Date: Fri, 5 May 2023 15:03:00 -0400 Subject: [PATCH 2/7] restructuring local plots to factor out repeat code into functions --- controls.py | 2 +- enacts/wat_bal/agronomy.py | 8 +- enacts/wat_bal/layout_monit.py | 46 ++++++- enacts/wat_bal/maproom_monit.py | 220 +++++++++++++++++--------------- 4 files changed, 166 insertions(+), 110 deletions(-) diff --git a/controls.py b/controls.py index 59dcbed56..875661e04 100644 --- a/controls.py +++ b/controls.py @@ -153,7 +153,7 @@ def Sentence(*elems): groups.extend(elems[i + 1]) if tail: - assert (isinstance(elems[-1], str) or isinstance(elems[-1], html.Span)) + #assert (isinstance(elems[-1], str) or isinstance(elems[-1], html.Span)) groups.append(dbc.Label(elems[-1], size="sm", className="m-1 d-inline-block", width="auto")) return dbc.Form(groups) diff --git a/enacts/wat_bal/agronomy.py b/enacts/wat_bal/agronomy.py index 85737c457..9ed5151b0 100644 --- a/enacts/wat_bal/agronomy.py +++ b/enacts/wat_bal/agronomy.py @@ -301,7 +301,13 @@ def soil_plant_water_balance( planting_date = (peffective[time_dim][-1].drop_vars(time_dim) - (planted_since - np.timedelta64(1, "D")) ) - return sm, drainage, et_crop, et_crop_red, planting_date + return ( + sm.rename("sm"), + drainage.rename("drainage"), + et_crop.rename("et_crop"), + et_crop_red.rename("et_crop_red"), + planting_date.rename("planting_date"), + ) def api_runoff( diff --git a/enacts/wat_bal/layout_monit.py b/enacts/wat_bal/layout_monit.py index 6177a0c7a..99f0ab734 100644 --- a/enacts/wat_bal/layout_monit.py +++ b/enacts/wat_bal/layout_monit.py @@ -39,7 +39,10 @@ def app_layout(): # Initialization rr_mrg = calc.read_zarr_data(RR_MRG_ZARR) - center_of_the_map = [((rr_mrg["Y"][int(rr_mrg["Y"].size/2)].values)), ((rr_mrg["X"][int(rr_mrg["X"].size/2)].values))] + center_of_the_map = [ + ((rr_mrg["Y"][int(rr_mrg["Y"].size/2)].values)), + ((rr_mrg["X"][int(rr_mrg["X"].size/2)].values)), + ] lat_res = np.around((rr_mrg["Y"][1]-rr_mrg["Y"][0]).values, decimals=10) lat_min = np.around((rr_mrg["Y"][0]-lat_res/2).values, decimals=10) lat_max = np.around((rr_mrg["Y"][-1]+lat_res/2).values, decimals=10) @@ -48,6 +51,10 @@ def app_layout(): lon_max = np.around((rr_mrg["X"][-1]+lon_res/2).values, decimals=10) lat_label = str(lat_min)+" to "+str(lat_max)+" by "+str(lat_res)+"˚" lon_label = str(lon_min)+" to "+str(lon_max)+" by "+str(lon_res)+"˚" + first_year = rr_mrg["T"][0].dt.year.values + one_to_last_year = rr_mrg["T"][-367].dt.year.values + last_year = rr_mrg["T"][-1].dt.year.values + year_label = str(first_year)+" to "+str(last_year) return dbc.Container( [ @@ -56,7 +63,18 @@ def app_layout(): dbc.Row( [ dbc.Col( - controls_layout(lat_min, lat_max, lon_min, lon_max, lat_label, lon_label), + controls_layout( + lat_min, + lat_max, + lon_min, + lon_max, + lat_label, + lon_label, + first_year, + one_to_last_year, + last_year, + year_label, + ), sm=12, md=4, style={ @@ -166,7 +184,18 @@ def navbar_layout(): ) -def controls_layout(lat_min, lat_max, lon_min, lon_max, lat_label, lon_label): +def controls_layout( + lat_min, + lat_max, + lon_min, + lon_max, + lat_label, + lon_label, + other_year_min, + other_year_default, + other_year_max, + year_label +): return dbc.Container( [ html.Div( @@ -333,10 +362,13 @@ def controls_layout(lat_min, lat_max, lon_min, lon_max, lat_label, lon_label): Sentence( "Planting Date", DateNoYear("planting2_", 1, CONFIG["planting_month"]), - ), - Sentence( - Number("year_ago", 1, min=0, max=99, html_size=3), - "year(s) ago", + Number( + "planting2_year", + other_year_default, + min=other_year_min, + max=other_year_max, + html_size=5 + ), ), Sentence( "for", diff --git a/enacts/wat_bal/maproom_monit.py b/enacts/wat_bal/maproom_monit.py index 40391699a..7bc5ebc69 100644 --- a/enacts/wat_bal/maproom_monit.py +++ b/enacts/wat_bal/maproom_monit.py @@ -302,6 +302,69 @@ def pick_location(n_clicks, click_lat_lng, latitude, longitude): return [lat, lng], lat, lng +def wat_bal_ts( + precip, + map_choice, + taw, + planting_day, + planting_month, + kc_init_length, + kc_veg_length, + kc_mid_length, + kc_late_length, + kc_init, + kc_veg, + kc_mid, + kc_late, + kc_end, + planting_year=None, + time_coord="T", +): + + kc_periods = pd.TimedeltaIndex( + [0, kc_init_length, kc_veg_length, kc_mid_length, kc_late_length], unit="D" + ) + kc_params = xr.DataArray(data=[ + kc_init, kc_veg, kc_mid, kc_late, kc_end + ], dims=["kc_periods"], coords=[kc_periods]) + p_d = calc.sel_day_and_month( + precip[time_coord], planting_day, planting_month + ) + p_d = (p_d[-1] if planting_year is None else p_d.where( + p_d.dt.year == planting_year, drop=True + )).squeeze(drop=True).rename("p_d") + precip = precip.where( + (precip["T"] >= p_d) & (precip["T"] < (p_d + np.timedelta64(365, "D"))), + drop=True, + ) + precip.load() + try: + water_balance_outputs = ag.soil_plant_water_balance( + precip, + et=5, + taw=taw, + sminit=taw/3., + kc_params=kc_params, + planting_date=p_d, + ) + for wbo in water_balance_outputs: + if (wbo.name == map_choice): + ts = wbo + except TypeError: + ts = None + return ts + + +def plot_scatter(ts, name, color, dash=None): + return pgo.Scatter( + x=ts["T"].dt.strftime("%-d %b %y"), + y=ts.values, + hovertemplate="%{y} on %{x}", + name=name, + line=pgo.scatter.Line(color=color, dash=dash), + connectgaps=False, + ) + @APP.callback( Output("wat_bal_plot", "figure"), Input("loc_marker", "position"), @@ -322,7 +385,7 @@ def pick_location(n_clicks, click_lat_lng, latitude, longitude): State("kc_end", "value"), State("planting2_day", "value"), State("planting2_month", "value"), - State("year_ago", "value"), + State("planting2_year", "value"), State("crop2_name", "value"), State("kc2_init", "value"), State("kc2_init_length", "value"), @@ -353,7 +416,7 @@ def wat_bal_plots( kc_end, planting2_day, planting2_month, - year_ago, + planting2_year, crop2_name, kc2_init, kc2_init_length, @@ -366,124 +429,79 @@ def wat_bal_plots( kc2_end, ): + first_year = rr_mrg.precip["T"][0].dt.year.values + last_year = rr_mrg.precip["T"][-1].dt.year.values + if planting2_year is None: + return pingrid.error_fig( + error_msg=f"Planting date must be between {first_year} and {last_year}" + ) + lat = marker_pos[0] lng = marker_pos[1] - taw = pingrid.sel_snap(xr.open_dataarray(Path(CONFIG["taw_file"])), lat, lng) - - kc_periods = pd.TimedeltaIndex( - [0, int(kc_init_length), int(kc_veg_length), int(kc_mid_length), int(kc_late_length)], unit="D" - ) - kc_params = xr.DataArray(data=[ - float(kc_init), float(kc_veg), float(kc_mid), float(kc_late), float(kc_end) - ], dims=["kc_periods"], coords=[kc_periods]) - precip = rr_mrg.precip.isel({"T": slice(-366, None)}) - p_d = calc.sel_day_and_month( - precip["T"], int(planting_day), calc.strftimeb2int(planting_month) - ).squeeze(drop=True).rename("p_d") - precip = precip.where(precip["T"] >= p_d, drop=True) try: - precip = pingrid.sel_snap(precip, lat, lng) - isnan = np.isnan(precip).any() - if isnan: - error_fig = pingrid.error_fig(error_msg="Data missing at this location") - return error_fig + taw = pingrid.sel_snap(xr.open_dataarray(Path(CONFIG["taw_file"])), lat, lng) except KeyError: - error_fig = pingrid.error_fig(error_msg="Grid box out of data domain") - return error_fig - precip.load() - try: - sm, drainage, et_crop, et_crop_red, planting_date = ag.soil_plant_water_balance( - precip, - et=5, - taw=taw, - sminit=taw/3., - kc_params=kc_params, - planting_date=p_d, - ) - except TypeError: - error_fig = pingrid.error_fig( + return pingrid.error_fig(error_msg="Grid box out of data domain") + precip = pingrid.sel_snap(rr_mrg.precip, lat, lng) + if np.isnan(precip).all(): + return pingrid.error_fig(error_msg="Data missing at this location") + + ts = wat_bal_ts( + precip, + map_choice, + taw, + int(planting_day), + calc.strftimeb2int(planting_month), + int(kc_init_length), + int(kc_veg_length), + int(kc_mid_length), + int(kc_late_length), + float(kc_init), + float(kc_veg), + float(kc_mid), + float(kc_late), + float(kc_end), + ) + if (ts is None): + return pingrid.error_fig( error_msg="Please ensure all input boxes are filled for the calculation to run." ) - return error_fig - kc2_periods = pd.TimedeltaIndex([ - 0, + ts2 = wat_bal_ts( + precip, + map_choice, + taw, + int(planting2_day), + calc.strftimeb2int(planting2_month), int(kc2_init_length), int(kc2_veg_length), int(kc2_mid_length), int(kc2_late_length), - ], unit="D") - kc2_params = xr.DataArray(data=[ - float(kc2_init), float(kc2_veg), float(kc2_mid), float(kc2_late), float(kc2_end) - ], dims=["kc_periods"], coords=[kc2_periods]) - precip2 = rr_mrg.precip.isel({"T": slice(-366 * (int(year_ago) + 1), None)}) - p_d2 = calc.sel_day_and_month( - precip2["T"], int(planting2_day), calc.strftimeb2int(planting2_month) - ).isel(T=0, drop=True).squeeze(drop=True).rename("p_d2") - precip2 = precip2.where( - (precip2["T"] >= p_d2) & (precip2["T"] < (p_d2 + np.timedelta64(366, "D"))), - drop=True, + float(kc2_init), + float(kc2_veg), + float(kc2_mid), + float(kc2_late), + float(kc2_end), + planting_year=int(planting2_year) ) - try: - precip2 = pingrid.sel_snap(precip2, lat, lng) - isnan = np.isnan(precip2).sum() - if isnan > 0: - error_fig = pingrid.error_fig(error_msg="Data missing at this location") - return error_fig - except KeyError: - error_fig = pingrid.error_fig(error_msg="Grid box out of data domain") - return error_fig - precip2.load() - try: - sm2, drainage2, et_crop2, et_crop_red2, planting_date2 = ag.soil_plant_water_balance( - precip2, - et=5, - taw=taw, - sminit=taw/3., - kc_params=kc2_params, - planting_date=p_d2, - ) - except TypeError: - error_fig = pingrid.error_fig( + if (ts2 is None): + return pingrid.error_fig( error_msg="Please ensure all input boxes are filled for the calculation to run." ) - return error_fig - - if map_choice == "sm": - ts = sm - ts2 = sm2 - elif map_choice == "drainage": - ts = drainage - ts2 = drainage2 - elif map_choice == "et_crop": - ts = et_crop - ts2 = et_crop2 + p_d2 = calc.sel_day_and_month( + precip["T"], int(planting2_day), calc.strftimeb2int(planting2_month) + ) + p_d2 = p_d2.where( + abs(ts["T"][0] - p_d2) == abs(ts["T"][0] - p_d2).min(), drop=True + ).squeeze(drop=True).rename("p_d2") ts2 = ts2.assign_coords({"T": pd.date_range(datetime.datetime( - p_d2.dt.year.values + int(year_ago), p_d2.dt.month.values, p_d2.dt.day.values + p_d2.dt.year.values, p_d2.dt.month.values, p_d2.dt.day.values ), periods=ts2["T"].size)}) wat_bal_graph = pgo.Figure() - wat_bal_graph.add_trace( - pgo.Scatter( - x=ts["T"].dt.strftime("%-d %b %y"), - y=ts.values, - hovertemplate="%{y} on %{x}", - name="Current", - line=pgo.scatter.Line(color="blue"), - connectgaps=False, - ) - ) - wat_bal_graph.add_trace( - pgo.Scatter( - x=ts2["T"].dt.strftime("%-d %b %y"), - y=ts2.values, - hovertemplate="%{y} on %{x}", - name="Comparison", - line=pgo.scatter.Line(color="red", dash="dash"), - connectgaps=False, - ) - ) + wat_bal_graph.add_trace(plot_scatter(ts, "Current", "green", dash=None)) + wat_bal_graph.add_trace(plot_scatter(ts2, "Comparison", "blue", dash="dash")) wat_bal_graph.update_layout( xaxis_title="Time", yaxis_title=f"{CONFIG['map_text'][map_choice]['menu_label']} [{CONFIG['map_text'][map_choice]['units']}]", From 42908f1215e4f81ac6e4df776c9b84aa591626ef Mon Sep 17 00:00:00 2001 From: remic Date: Fri, 5 May 2023 15:08:47 -0400 Subject: [PATCH 3/7] Controls referred to in italic --- enacts/wat_bal/layout_monit.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/enacts/wat_bal/layout_monit.py b/enacts/wat_bal/layout_monit.py index 99f0ab734..4bdbb82b6 100644 --- a/enacts/wat_bal/layout_monit.py +++ b/enacts/wat_bal/layout_monit.py @@ -212,35 +212,35 @@ def controls_layout( ), dcc.Loading(html.P(id="map_description"), type="dot"), html.P( - f""" + dcc.Markdown(""" The soil-plant-water balance algorithm estimates soil moisture and other characteristics of the soil and plants since planting date of the current season and up to now. It is driven by rainfall and the crop cultivars Kc - that can be changed in the Controls Panel below. - """ + that can be changed in the _Controls Panel_ below. + """) ), html.P( - f""" - Map another day of the simulation using the Date control in the top bar, + dcc.Markdown(""" + Map another day of the simulation using the _Date_ control in the top bar, or by clicking a day of interest on the time series graph.. You can pick a day between planting and today (or last day of available data). - """ + """) ), html.P( - f""" + dcc.Markdown(""" Pick another point to monitor evolution since planting - with the controls below or by clicking on the map. - """ + with the _Pick a point_ controls or by clicking on the map. + """) ), html.P( - f""" + dcc.Markdown(""" The current evolution (blue) is put in context by comparing it to another situation (dashed red) that can be altered by picking another planting date and/or another crop (Kc parameters) and/or - another year through the Compare to... panel. - """ + another year through the _Compare to..._ panel. + """) ), html.H5("Water Balance Outputs"), ]+[ From c7ce1387bf58bf6a05d6a98ece518c3ca7a31041 Mon Sep 17 00:00:00 2001 From: remic Date: Tue, 9 May 2023 14:48:33 -0400 Subject: [PATCH 4/7] outer alignment fixes unexpected plotly behavior and some cosmetics --- enacts/wat_bal/maproom_monit.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/enacts/wat_bal/maproom_monit.py b/enacts/wat_bal/maproom_monit.py index 7bc5ebc69..0b47c41a9 100644 --- a/enacts/wat_bal/maproom_monit.py +++ b/enacts/wat_bal/maproom_monit.py @@ -355,11 +355,15 @@ def wat_bal_ts( return ts -def plot_scatter(ts, name, color, dash=None): +def plot_scatter(ts, name, color, dash=None, customdata=None): + hovertemplate = "%{y} on %{x}" + if customdata is not None: + hovertemplate = hovertemplate + " %{customdata}" return pgo.Scatter( - x=ts["T"].dt.strftime("%-d %b %y"), + x=ts["T"].dt.strftime("%-d %b"), y=ts.values, - hovertemplate="%{y} on %{x}", + customdata=customdata, + hovertemplate=hovertemplate, name=name, line=pgo.scatter.Line(color=color, dash=dash), connectgaps=False, @@ -499,8 +503,10 @@ def wat_bal_plots( p_d2.dt.year.values, p_d2.dt.month.values, p_d2.dt.day.values ), periods=ts2["T"].size)}) + ts, ts2 = xr.align(ts, ts2, join="outer") + wat_bal_graph = pgo.Figure() - wat_bal_graph.add_trace(plot_scatter(ts, "Current", "green", dash=None)) + wat_bal_graph.add_trace(plot_scatter(ts, "Current", "green", customdata=ts["T"].dt.strftime("%Y"))) wat_bal_graph.add_trace(plot_scatter(ts2, "Comparison", "blue", dash="dash")) wat_bal_graph.update_layout( xaxis_title="Time", From 9e448e28653f0092771f12be3ae5973eaa9ea787 Mon Sep 17 00:00:00 2001 From: remic Date: Wed, 10 May 2023 11:24:19 -0400 Subject: [PATCH 5/7] Sentence needs alternation of objects and strings --- controls.py | 4 ++-- enacts/wat_bal/layout_monit.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/controls.py b/controls.py index 875661e04..46cb196e4 100644 --- a/controls.py +++ b/controls.py @@ -148,12 +148,12 @@ def Sentence(*elems): groups.extend(elems[0]) for i in range(start, len(elems) - (1 if tail else 0), 2): - #assert (isinstance(elems[i], str) or isinstance(elems[i], html.Span)) + assert (isinstance(elems[i], str) or isinstance(elems[i], html.Span)) groups.append(dbc.Label(elems[i], size="sm", className="m-1 d-inline-block", width="auto")) groups.extend(elems[i + 1]) if tail: - #assert (isinstance(elems[-1], str) or isinstance(elems[-1], html.Span)) + assert (isinstance(elems[-1], str) or isinstance(elems[-1], html.Span)) groups.append(dbc.Label(elems[-1], size="sm", className="m-1 d-inline-block", width="auto")) return dbc.Form(groups) diff --git a/enacts/wat_bal/layout_monit.py b/enacts/wat_bal/layout_monit.py index 4bdbb82b6..14bedeeae 100644 --- a/enacts/wat_bal/layout_monit.py +++ b/enacts/wat_bal/layout_monit.py @@ -362,6 +362,7 @@ def controls_layout( Sentence( "Planting Date", DateNoYear("planting2_", 1, CONFIG["planting_month"]), + "", Number( "planting2_year", other_year_default, From 6462ee8804d77e284fc83b8a86a50c0eeca4008c Mon Sep 17 00:00:00 2001 From: remic Date: Thu, 18 May 2023 15:13:34 -0400 Subject: [PATCH 6/7] some styling --- controls.py | 16 ++++++++++------ enacts/wat_bal/layout_monit.py | 11 ++++++++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/controls.py b/controls.py index 46cb196e4..ca1bff577 100644 --- a/controls.py +++ b/controls.py @@ -22,7 +22,7 @@ def Text(id, default): dbc Input component with text inputs. """ return [ dbc.Input(id=id, type="text", - size="sm", className="m-1 d-inline-block w-auto",debounce=True, value=default) ] + size="sm", className="m-1 d-inline-block w-auto", debounce=True, value=default) ] def Number(id, default, min=None, max=None, html_size=None): @@ -158,7 +158,7 @@ def Sentence(*elems): return dbc.Form(groups) -def Block(title, *body, is_on=True, width="100%"): #width of the block in its container +def Block(title, *body, is_on=True, width="100%", border_color="grey"): #width of the block in its container """Separates out components in individual Cards Auto-generates a formatted block with a card header and body. @@ -185,10 +185,14 @@ def Block(title, *body, is_on=True, width="100%"): #width of the block in its co the_display = "inline-block" else: the_display = "none" - return dbc.Card([ - dbc.CardHeader(title), - dbc.CardBody(body), - ], className="mb-4 ml-4 mr-4", style={"display": the_display, "width": width}) + return dbc.Card( + [ + dbc.CardHeader(title), + dbc.CardBody(body), + ], + className="mb-4 ml-4 mr-4", + style={"display": the_display, "width": width, "border-color": border_color, "line-height": 1}, + ) def Options(options,labels=None): """ Creates options for definition of different Dash components. diff --git a/enacts/wat_bal/layout_monit.py b/enacts/wat_bal/layout_monit.py index 14bedeeae..4b7d5ed29 100644 --- a/enacts/wat_bal/layout_monit.py +++ b/enacts/wat_bal/layout_monit.py @@ -280,6 +280,7 @@ def controls_layout( min=lat_min, max=lat_max, type="number", + style={"height": "auto", "padding-bottom": "0px"}, ), dbc.Label("Latitude", style={"font-size": "80%"}), dbc.Tooltip( @@ -296,6 +297,7 @@ def controls_layout( min=lon_min, max=lon_max, type="number", + style={"height": "auto", "padding-bottom": "0px"}, ), dbc.Label("Longitude", style={"font-size": "80%"}), dbc.Tooltip( @@ -305,7 +307,7 @@ def controls_layout( ) ]), ), - dbc.Button(id="submit_lat_lng", children='Submit'), + dbc.Button(id="submit_lat_lng", children='Submit', color="secondary"), ], ), ), @@ -317,6 +319,7 @@ def controls_layout( {"label": val["menu_label"], "value": key} for key, val in CONFIG["map_text"].items() ], + style={"padding-top": "0px", "padding-bottom": "0px"}, ), ), Block( @@ -355,7 +358,8 @@ def controls_layout( Sentence( Number("kc_end", CONFIG["kc_v"][4], min=0, max=2, html_size=4), ), - dbc.Button(id="submit_kc", children='Submit'), + dbc.Button(id="submit_kc", children='Submit', color="success"), + border_color="green", ), Block( "Compare to...", @@ -403,7 +407,8 @@ def controls_layout( Sentence( Number("kc2_end", CONFIG["kc_v"][4], min=0, max=2, html_size=4), ), - dbc.Button(id="submit_kc2", children='Submit'), + dbc.Button(id="submit_kc2", children='Submit', color="primary"), + border_color="blue", ), ], style={"position":"relative","height":"60%", "overflow":"scroll"}, From 9ee2633fb4ce4569d227ee053b9bd665a8059ea1 Mon Sep 17 00:00:00 2001 From: remic Date: Mon, 22 May 2023 12:03:31 -0400 Subject: [PATCH 7/7] dbc.Button doesn't know colors --- enacts/wat_bal/layout_monit.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/enacts/wat_bal/layout_monit.py b/enacts/wat_bal/layout_monit.py index 4b7d5ed29..b273e2b11 100644 --- a/enacts/wat_bal/layout_monit.py +++ b/enacts/wat_bal/layout_monit.py @@ -358,7 +358,12 @@ def controls_layout( Sentence( Number("kc_end", CONFIG["kc_v"][4], min=0, max=2, html_size=4), ), - dbc.Button(id="submit_kc", children='Submit', color="success"), + dbc.Button( + id="submit_kc", + children='Submit', + color="light", + style={"color": "green", "border-color": "green"}, + ), border_color="green", ), Block( @@ -407,7 +412,12 @@ def controls_layout( Sentence( Number("kc2_end", CONFIG["kc_v"][4], min=0, max=2, html_size=4), ), - dbc.Button(id="submit_kc2", children='Submit', color="primary"), + dbc.Button( + id="submit_kc2", + children='Submit', + color="light", + style={"color": "blue", "border-color": "green"}, + ), border_color="blue", ), ],