From de9525455bb41d4bb484c0057d7fa3d899977a73 Mon Sep 17 00:00:00 2001 From: romer8 Date: Sun, 9 Jun 2024 17:17:48 -0600 Subject: [PATCH] added troute to the nexus metadata --- .../components/hydroFabricLinePlot.js | 46 ++++++++++++- .../hydroFabric/hooks/useHydroFabric.js | 5 ++ .../hydroFabric/store/actions/actionsTypes.js | 5 ++ .../store/reducers/hydroFabricReducer.js | 65 +++++++++++++++++++ reactapp/services/api/app.js | 8 ++- reactapp/views/ngiab/hydroFabricView.js | 60 +++++++++++++++++ tethysapp/ngiab/controllers.py | 60 ++++++++++++++++- tethysapp/ngiab/utils.py | 31 +++++++++ 8 files changed, 276 insertions(+), 4 deletions(-) diff --git a/reactapp/features/hydroFabric/components/hydroFabricLinePlot.js b/reactapp/features/hydroFabric/components/hydroFabricLinePlot.js index 851e69f..74b1abc 100644 --- a/reactapp/features/hydroFabric/components/hydroFabricLinePlot.js +++ b/reactapp/features/hydroFabric/components/hydroFabricLinePlot.js @@ -11,8 +11,9 @@ import '../css/chart.css'; const chartOptions = { axisX: { type: FixedScaleAxis, - divisor: 5, + divisor: 10, labelInterpolationFnc: function(value) { + console.log(value) return new Date(value).toLocaleDateString(); } }, @@ -29,6 +30,8 @@ const chartOptions = { }; + + const HydroFabricLinePlot = (props) => { // Reference to the chart container const chartRef = useRef(null); @@ -39,6 +42,7 @@ const HydroFabricLinePlot = (props) => { useEffect(() => { if (!state.nexus.series) return; const nexusSeries = state.nexus.series.map(point => ({x: new Date(point.x), y: point.y})); + const chartData = { series: [ { name: 'Nexus', data: nexusSeries }, @@ -53,6 +57,7 @@ const HydroFabricLinePlot = (props) => { return () => { + console.log("retuirnin") if(chartInstance && props.singleRowOn){ actions.reset_nexus(); chartInstance.current.detach(); @@ -100,6 +105,45 @@ const HydroFabricLinePlot = (props) => { }; }, [state.catchment.series]); // Re-run effect if series data changes + + + useEffect(() => { + if (!state.troute.series) return; + if (chartRef.current) { + const trouteSeries = state.troute.series.map(point => ({x: new Date(point.x), y: point.y})); + const chartData = { + series: [ + { name: 'Troute', data: trouteSeries }, + ] + }; + + chartInstance.current = new LineChart(chartRef.current, chartData, chartOptions); + + addAnimationToLineChart(chartInstance.current, easings) + makeAxis( + chartRef.current, + 'Time (Date)', + `${state.troute.variable ? state.troute.variable.toLowerCase() : state.troute.variable_list ? state.troute.variable_list[0].label : null}` + ) + + makeTitle( + chartRef.current, + `${state.troute.variable ? state.troute.variable.toLowerCase() : state.troute.variable_list ? state.troute.variable_list[0].label : null}: ${state.troute.id} `) + } + + return () => { + if(props.singleRowOn){ + console.log(props.singleRowOn) + actions.reset_troute(); + chartRef.current.detach(); + document.getElementById('x-axis-title')?.remove(); + document.getElementById('y-axis-title')?.remove(); + } + + }; + }, [state.troute.series]); // Re-run effect if series data changes + + return (
); diff --git a/reactapp/features/hydroFabric/hooks/useHydroFabric.js b/reactapp/features/hydroFabric/hooks/useHydroFabric.js index aa945db..53bcd21 100644 --- a/reactapp/features/hydroFabric/hooks/useHydroFabric.js +++ b/reactapp/features/hydroFabric/hooks/useHydroFabric.js @@ -16,6 +16,11 @@ const useHydroFabric = () => { set_nexus_list: (list) => dispatch({ type: hydroFabricActionsTypes.set_nexus_list, payload: list}), set_catchment_list: (list) => dispatch({ type: hydroFabricActionsTypes.set_catchment_list, payload: list}), set_catchment_variable_list: (list) => dispatch({ type: hydroFabricActionsTypes.set_catchment_variable_list, payload: list}), + set_troute_series: (series) => dispatch({ type: hydroFabricActionsTypes.set_troute_series, payload: series }), + set_troute_id: (id) => dispatch({ type: hydroFabricActionsTypes.set_troute_id, payload: id }), + set_troute_variable: (variable) => dispatch({ type: hydroFabricActionsTypes.set_troute_variable, payload: variable }), + set_troute_variable_list: (list) => dispatch({ type: hydroFabricActionsTypes.set_troute_variable_list, payload: list }), + reset_troute: () => dispatch({ type: hydroFabricActionsTypes.reset_troute }), reset_nexus: () => dispatch({ type: hydroFabricActionsTypes.reset_nexus }), reset_catchment: () => dispatch({ type: hydroFabricActionsTypes.reset_catchment }), reset: () => dispatch({ type: hydroFabricActionsTypes.reset }), diff --git a/reactapp/features/hydroFabric/store/actions/actionsTypes.js b/reactapp/features/hydroFabric/store/actions/actionsTypes.js index 57228f0..6425a3c 100644 --- a/reactapp/features/hydroFabric/store/actions/actionsTypes.js +++ b/reactapp/features/hydroFabric/store/actions/actionsTypes.js @@ -7,8 +7,13 @@ const hydroFabricActionsTypes = { set_catchment_id: 'SET_CATCHMENT_ID', set_catchment_variable: 'SET_CATCHMENT_VARIABLE', set_catchment_variable_list: 'SET_CATCHMENT_VARIABLE_LIST', + set_troute_series: 'SET_TROUTE_SERIES', + set_troute_id: 'SET_TROUTE_ID', + set_troute_variable: 'SET_TROUTE_VARIABLE', + set_troute_variable_list: 'SET_TROUTE_VARIABLE_LIST', reset_nexus: 'RESET_NEXUS', reset_catchment: 'RESET_CATCHMENT', + reset_troute: 'RESET_TROUTE', reset: 'RESET', }; diff --git a/reactapp/features/hydroFabric/store/reducers/hydroFabricReducer.js b/reactapp/features/hydroFabric/store/reducers/hydroFabricReducer.js index 63de112..288b0a7 100644 --- a/reactapp/features/hydroFabric/store/reducers/hydroFabricReducer.js +++ b/reactapp/features/hydroFabric/store/reducers/hydroFabricReducer.js @@ -15,6 +15,13 @@ const hydroFabricInitialStore = { list:null, variable_list:null }, + troute:{ + series:null, + variable:null, + id:null, + list:null, + variable_list:null + } }, actions:{} @@ -124,6 +131,64 @@ const hydroFabricReducer = (state, action) => { } } }; + case hydroFabricActionsTypes.set_troute_series: + return { + ...state, + state: { + ...state.state, + troute: { + ...state.state.troute, + series: action.payload + } + } + }; + case hydroFabricActionsTypes.set_troute_id: + return { + ...state, + state: { + ...state.state, + troute: { + ...state.state.troute, + id: action.payload + } + } + }; + case hydroFabricActionsTypes.set_troute_variable: + return { + ...state, + state: { + ...state.state, + troute: { + ...state.state.troute, + variable: action.payload + } + } + }; + case hydroFabricActionsTypes.set_troute_variable_list: + return { + ...state, + state: { + ...state.state, + troute: { + ...state.state.troute, + variable_list: action.payload + } + } + }; + case hydroFabricActionsTypes.reset_troute: + return { + ...state, + state: { + ...state.state, + troute: { + series:null, + variable:null, + id:null, + list:null, + variable_list:null + } + } + }; case hydroFabricActionsTypes.reset_nexus: return { ...state, diff --git a/reactapp/services/api/app.js b/reactapp/services/api/app.js index d418436..fd68e99 100644 --- a/reactapp/services/api/app.js +++ b/reactapp/services/api/app.js @@ -12,6 +12,12 @@ const appAPI = { getCatchmentTimeSeries: (params) => { return apiClient.get(`${APP_ROOT_URL}getCatchmentTimeSeries/`, { params }); }, -}; + getTrouteVariables: (params) => { + return apiClient.get(`${APP_ROOT_URL}getTrouteVariables/`, { params }); + }, + getTrouteTimeSeries: (params) => { + return apiClient.get(`${APP_ROOT_URL}getTrouteTimeSeries/`, { params }); + } +} export default appAPI; \ No newline at end of file diff --git a/reactapp/views/ngiab/hydroFabricView.js b/reactapp/views/ngiab/hydroFabricView.js index bdafe7c..4e45029 100644 --- a/reactapp/views/ngiab/hydroFabricView.js +++ b/reactapp/views/ngiab/hydroFabricView.js @@ -22,6 +22,7 @@ const HydroFabricView = (props) => { appAPI.getNexusTimeSeries(params).then((response) => { actions.set_nexus_series(response.data); actions.set_nexus_list(response.nexus_ids); + actions.set_troute_id(state.nexus.id); props.toggleSingleRow(false); }).catch((error) => { console.log("Error fetching nexus time series", error); @@ -33,6 +34,51 @@ const HydroFabricView = (props) => { }, [state.nexus.id]); + useEffect(() => { + if (!state.troute.id) return; + props.setIsLoading(true); + + var params = { + troute_id: state.troute.id + } + appAPI.getTrouteVariables(params).then((response) => { + actions.set_troute_variable_list(response.troute_variables); + props.toggleSingleRow(false); + props.setIsLoading(false); + }).catch((error) => { + props.setIsLoading(false); + console.log("Error fetching troute variables", error); + }) + return () => { + if (state.troute.id) return; + actions.reset_troute(); + } + },[state.troute.id]); + + + useEffect(() => { + if (!state.troute.variable || !state.troute.id) return; + // actions.reset_nexus(); + props.setIsLoading(true); + var params = { + troute_id: state.troute.id, + troute_variable: state.troute.variable + } + appAPI.getTrouteTimeSeries(params).then((response) => { + actions.set_troute_series(response.data); + props.toggleSingleRow(false); + props.setIsLoading(false); + }).catch((error) => { + props.setIsLoading(false); + console.log("Error fetching troute time series", error); + }) + return () => { + if (state.troute.id) return; + actions.reset_troute(); + } + },[state.troute.variable]); + + useEffect(() => { if (!state.catchment.id) return; @@ -132,8 +178,22 @@ const HydroFabricView = (props) => { } /> + + } + { + state.troute.id && + +
Troute
+ + +
} + + }> diff --git a/tethysapp/ngiab/controllers.py b/tethysapp/ngiab/controllers.py index 80eb0a0..74e0d73 100644 --- a/tethysapp/ngiab/controllers.py +++ b/tethysapp/ngiab/controllers.py @@ -4,7 +4,15 @@ import json import geopandas as gpd from tethys_sdk.routing import controller -from .utils import get_base_output, getCatchmentsIds, getNexusIDs +from .utils import ( + get_base_output, + getCatchmentsIds, + getNexusIDs, + check_troute_id, + get_troute_vars, + get_troute_df, +) + from .app import App @@ -95,4 +103,52 @@ def getNexusTimeSeries(request, app_workspace): for time, streamflow in zip(time_col.tolist(), streamflow_cms_col.tolist()) ] - return JsonResponse({"data": data, "nexus_ids": getNexusIDs(app_workspace)}) + return JsonResponse( + { + "data": data, + "nexus_ids": getNexusIDs(app_workspace), + } + ) + + +@controller(app_workspace=True) +def getTrouteVariables(request, app_workspace): + troute_id = request.GET.get("troute_id") + clean_troute_id = troute_id.split("-")[1] + df = get_troute_df(app_workspace) + try: + if check_troute_id(df, clean_troute_id): + vars = get_troute_vars(df) + else: + vars = [] + except Exception as e: + vars = [] + + return JsonResponse({"troute_variables": vars}) + + +@controller(app_workspace=True) +def getTrouteTimeSeries(request, app_workspace): + troute_id = request.GET.get("troute_id") + clean_troute_id = troute_id.split("-")[1] + variable_column = request.GET.get("troute_variable") + df = get_troute_df(app_workspace) + df_sliced_by_id = df[df["feature_id"] == int(clean_troute_id)] + + df_sliced_by_id["t0"] = pd.to_datetime(df_sliced_by_id["t0"].iloc[0]) + + # Convert the time_offset column to timedelta + df_sliced_by_id["time"] = pd.to_timedelta(df_sliced_by_id["time"]) + + df_sliced_by_id["t1"] = df_sliced_by_id["t0"] + df_sliced_by_id["time"] + try: + time_col = df_sliced_by_id["t1"] + var_col = df_sliced_by_id[variable_column] + data = [ + {"x": time, "y": val} + for time, val in zip(time_col.tolist(), var_col.tolist()) + ] + except Exception as e: + data = [] + + return JsonResponse({"data": data}) diff --git a/tethysapp/ngiab/utils.py b/tethysapp/ngiab/utils.py index f2570c0..3f524b1 100644 --- a/tethysapp/ngiab/utils.py +++ b/tethysapp/ngiab/utils.py @@ -1,5 +1,36 @@ import os import json +import pandas as pd +import glob + + +def _get_base_troute_output(app_workspace): + base_output_path = os.path.join( + app_workspace.path, "ngen-data", "outputs", "troute" + ) + return base_output_path + + +def get_troute_df(app_workspace): + base_output_path = _get_base_troute_output(app_workspace) + troute_output_files = glob.glob(base_output_path + "/*.csv") + df = pd.read_csv(troute_output_files[0]) + return df + + +def check_troute_id(df, id): + if int(id) in df["feature_id"].values: + return True + return False + + +def get_troute_vars(df): + list_variables = df.columns.tolist()[3:] # remove feature, time and t0 + + variables = [ + {"value": variable, "label": variable.lower()} for variable in list_variables + ] + return variables def get_base_output(app_workspace):