/* * Hydrograph charting module. */ import {select} from 'd3-selection'; import {bindActionCreators} from 'redux'; import ReduxConnectVue from 'redux-connect-vue'; import {createStructuredSelector} from 'reselect'; import {createApp, ref} from 'vue'; import config from 'ui/config.js'; import {drawInfoAlert} from 'd3render/alerts'; import {renderTimeSeriesUrlParams} from 'ml/url-params'; import {getInputsForRetrieval} from 'ml/selectors/hydrograph-state-selector'; import {retrieveGroundwaterLevelData} from 'ml/store/groundwater-level-field-visits'; import {retrieveHydrographData} from 'ml/store/hydrograph-data'; import {retrieveHydrographParameters} from 'ml/store/hydrograph-parameters'; import {setSelectedParameterCode, setCompareDataVisibility, setSelectedTimeSpan, setSelectedIVMethodID } from 'ml/store/hydrograph-state'; import {Actions as floodDataActions} from 'ml/store/flood-data'; import {getPreferredIVMethodID} from './selectors/time-series-data'; import {showDataIndicators} from './data-indicator'; import {initializeTimeSeriesGraph, drawTimeSeriesGraphData} from './time-series-graph'; import DataTablesApp from './DataTablesApp.vue'; import GraphControlsApp from './GraphControlsApp.vue'; import HydrographApp from './HydrographApp.vue'; import ParameterSelectionApp from './ParameterSelectionApp.vue'; import StatisticsTableApp from './StatisticsTableApp.vue'; import TimeDownloadGraphControlsApp from './TimeDownloadGraphControlsApp.vue'; /* eslint-disable vue/one-component-per-file */ /* * Renders the hydrograph on the node element using the Redux store for state information. The siteno, latitude, and * longitude are required parameters. All others are optional and are used to set the initial state of the hydrograph. * @param {Redux store} store * @param {DOM node} node * @param {Object} - string properties to set initial state information. The property siteno is required * @param {Promise} loadPromise - will resolve when any data needed by this module * that is fetched by the caller has been fetched * */ export const attachToNode = function(store, node, { siteno, agencyCd, sitename, parameterCode, compare, period, startDT, endDT, timeSeriesId, showOnlyGraph = false, showMLName = false } = {}) { const nodeElem = select(node); const addTimeDownloadGraphControlsApp = function() { const timeDownloadGraphControlsApp = createApp(TimeDownloadGraphControlsApp, {}); timeDownloadGraphControlsApp.use(ReduxConnectVue, { store, mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch), mapStateToPropsFactory: createStructuredSelector }); timeDownloadGraphControlsApp.provide('store', store); timeDownloadGraphControlsApp.provide('siteno', siteno); timeDownloadGraphControlsApp.provide('agencyCd', agencyCd); timeDownloadGraphControlsApp.mount('#time-download-graph-controls-anchor'); }; if (!config.ivPeriodOfRecord && !config.gwPeriodOfRecord) { addTimeDownloadGraphControlsApp(); select(node).select('.graph-container').call(drawInfoAlert, {title: 'Hydrograph Alert', body: 'No IV or field visit data is available.'}); return; } const initialLoadCompare = compare === 'true' || compare === true ? true : false; const thisShowOnlyGraph = showOnlyGraph === 'true' || showOnlyGraph === true ? true : false; const thisShowMLName = showMLName === 'true' || showMLName === true ? true : false; // Initialize all hydrograph state variables store.dispatch(setSelectedParameterCode(parameterCode)); store.dispatch(setCompareDataVisibility(initialLoadCompare)); if (period) { store.dispatch(setSelectedTimeSpan(period)); } else if (startDT && endDT) { store.dispatch(setSelectedTimeSpan({ start: startDT, end: endDT })); } else { store.dispatch(setSelectedTimeSpan(config.ivPeriodOfRecord && config.ivPeriodOfRecord[parameterCode] ? 'P7D' : 'P1Y')); } const initialLoadingComplete = ref(false); // Fetch all data needed to render the hydrograph const fetchHydrographDataPromise = store.dispatch(retrieveHydrographData(siteno, agencyCd, getInputsForRetrieval(store.getState()), true)); let fetchDataPromises = [fetchHydrographDataPromise]; // if showing only graph make a call to retrieve all of the groundwater level data. Otherwise, // this is done when retrieving all hydrograph parameter meta data. if (thisShowOnlyGraph) { if (config.gwPeriodOfRecord) { fetchDataPromises.push(store.dispatch(retrieveGroundwaterLevelData(siteno, agencyCd))); } } else { fetchDataPromises.push(store.dispatch(retrieveHydrographParameters(siteno, agencyCd))); } // Fetch flood levels if (config.ivPeriodOfRecord && config.GAGE_HEIGHT_PARAMETER_CODE in config.ivPeriodOfRecord) { const fetchFloodLevelsPromise = store.dispatch(floodDataActions.retrieveFloodLevels(siteno)); // If flood levels are to be shown then wait to render the hydrograph until those have been fetched. if (parameterCode === config.GAGE_HEIGHT_PARAMETER_CODE) { fetchDataPromises.push(fetchFloodLevelsPromise); } } // Render initial UI elements prior to completion of data fetching if (!showOnlyGraph) { addTimeDownloadGraphControlsApp(); } const graphContainer = nodeElem.select('.graph-container'); graphContainer.call(initializeTimeSeriesGraph, store, siteno, agencyCd, sitename, thisShowMLName, !thisShowOnlyGraph); showDataIndicators(true, store); if (!showOnlyGraph) { //TODO: The tooltips, legend and the main hydrograph can be added to the HydrographApp. // The main hydrograph should be converted to a Vue component last. As part of that task we // will figure out how to handle the loading indicator and the no data overlay // The addition of the hydrograph-brush-container is merely to create the brush in the correct location // This will change as we add things and eventually we won't need to do this. graphContainer.append('div').attr('id', 'hydrograph-brush-container'); const hydrographApp = createApp(HydrographApp, {}); hydrographApp.use(ReduxConnectVue, { store, mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch), mapStateToPropsFactory: createStructuredSelector }); hydrographApp.provide('store', store); hydrographApp.provide('siteno', siteno); hydrographApp.provide('initialLoadingComplete', initialLoadingComplete); hydrographApp.mount('#hydrograph-brush-container'); } if (!showOnlyGraph) { // const graphControlsApp = createApp(GraphControlsApp, {}); // graphControlsApp.use(ReduxConnectVue, { // store, // mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch), // mapStateToPropsFactory: createStructuredSelector // }); // graphControlsApp.provide('store', store); // graphControlsApp.provide('siteno', siteno); // graphControlsApp.mount('.ts-legend-controls-container'); const parameterSelectionApp = createApp(ParameterSelectionApp, {}); parameterSelectionApp.use(ReduxConnectVue, { store, mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch), mapStateToPropsFactory: createStructuredSelector }); parameterSelectionApp.provide('store', store); parameterSelectionApp.provide('siteno', siteno); parameterSelectionApp.provide('agencyCode', agencyCd); parameterSelectionApp.mount('.select-time-series-container'); } // Once hydrograph data has been fetched, render the time series data. Promise.all(fetchDataPromises).then(() => { initialLoadingComplete.value = true; // selectedIVMethodID should be set regardless of whether we are showing only the graph but the preferred method ID // can not be determined until the data is fetched so that is done here. const initialIVMethodID = timeSeriesId || getPreferredIVMethodID('primary')(store.getState()); store.dispatch(setSelectedIVMethodID(initialIVMethodID)); showDataIndicators(false, store); graphContainer.call(drawTimeSeriesGraphData, store, !thisShowOnlyGraph); if (!thisShowOnlyGraph) { // eslint-disable-next-line vue/one-component-per-file const dataTablesApp = createApp(DataTablesApp, {}); dataTablesApp.use(ReduxConnectVue, { store, mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch), mapStateToPropsFactory: createStructuredSelector }); dataTablesApp.provide('store', store); // data for DownLoadData component dataTablesApp.provide('siteno', siteno); dataTablesApp.provide('agencyCd', agencyCd); dataTablesApp.mount('#iv-data-table-container'); renderTimeSeriesUrlParams(store); const statisticsTableApp = createApp(StatisticsTableApp, {}); statisticsTableApp.use(ReduxConnectVue, { store, mapDispatchToPropsFactory: (actionCreators) => (dispatch) => bindActionCreators(actionCreators, dispatch), mapStateToPropsFactory: createStructuredSelector }); statisticsTableApp.mount('.daily-statistical-data'); } }) .catch(reason => { console.error(reason); throw reason; }); };