diff --git a/frontend/src/components/DataQuality.vue b/frontend/src/components/DataQuality.vue index 8681811..7588ca8 100644 --- a/frontend/src/components/DataQuality.vue +++ b/frontend/src/components/DataQuality.vue @@ -45,6 +45,7 @@ function qualityHasChanged() { // apply the time slider filter to the data chartStore.filterDatasetsToTimeRange() - chartStore.updateAllCharts() + + chartStore.updateCurrentChart() } diff --git a/frontend/src/components/LineChart.vue b/frontend/src/components/LineChart.vue index f02f9b9..7058687 100644 --- a/frontend/src/components/LineChart.vue +++ b/frontend/src/components/LineChart.vue @@ -92,8 +92,11 @@ onMounted(async () => { await nextTick() // push the chart to the store - chartStore.storeMountedChart(activeReachChart.value) - chartStore.updateShowLine() + chartStore.storeMountedChart(activeReachChart.value, xLabel, yLabel) + + // force a re-render of the line charts + chartStore.updateCurrentChart() + //chartStore.updateShowLine() }) const getParsing = () => { @@ -102,9 +105,34 @@ const getParsing = () => { parsing.xAxisKey = plt.xvar.abbreviation parsing.yAxisKey = plt.yvar.abbreviation + return parsing } +const getXScale = () => { + let xvar_abbr = props.chosenPlot.xvar.abbreviation + if (xvar_abbr == 'time_str') { + return { + type: 'time', + time: { + locale: enUS + }, + title: { + display: true, + text: xLabel + } + } + } else { + return { + type: 'linear', + title: { + display: true, + text: xLabel + } + } + } +} + const options = { responsive: true, maintainAspectRatio: false, @@ -171,20 +199,7 @@ const options = { } }, scales: { - x: { - type: 'time', - time: { - // unit: 'day', - // displayFormats: { - // day: 'MM.dd' - // }, - locale: enUS - }, - title: { - display: true, - text: xLabel - } - }, + x: getXScale(), y: { title: { display: true, diff --git a/frontend/src/components/NodeChart.vue b/frontend/src/components/NodeChart.vue index 0bfcd8e..29cb117 100644 --- a/frontend/src/components/NodeChart.vue +++ b/frontend/src/components/NodeChart.vue @@ -41,8 +41,10 @@ onMounted(async () => { await nextTick() // push the chart to the store + //chartStore.storeMountedChart(activeNodeChart.value, xLabel, yLabel) chartStore.storeMountedChart(activeNodeChart.value) - chartStore.updateShowLine() + //chartStore.updateShowLine() + chartStore.updateCurrentChart() }) const getParsing = () => { diff --git a/frontend/src/stores/charts.js b/frontend/src/stores/charts.js index b6f41c7..03f1d2b 100644 --- a/frontend/src/stores/charts.js +++ b/frontend/src/stores/charts.js @@ -73,14 +73,6 @@ export const useChartsStore = defineStore('charts', () => { title: 'Reach Width along Reach Length', help: "Reach Width plotted against Reach Length for all nodes in the selected reach", name: 'Width vs Distance', - }, - { - abbreviation: 'wse/width', - xvar: swotVariables.value.find((v) => v.abbreviation == 'width'), - yvar: swotVariables.value.find((v) => v.abbreviation == 'wse'), - title: 'Water Surface Elevation vs Reach Width', - help: "Water Surface Elevation plotted against Reach Width for all nodes in the selected reach", - name: 'WSE vs Width', } ]) @@ -119,6 +111,14 @@ export const useChartsStore = defineStore('charts', () => { title: 'Reach Slope', help: swotVariables.value.find((v) => v.abbreviation == 'slope').definition, name: 'Slope vs Time', + }, + { + abbreviation: 'wse/width', + xvar: swotVariables.value.find((v) => v.abbreviation == 'width'), + yvar: swotVariables.value.find((v) => v.abbreviation == 'wse'), + title: 'Water Surface Elevation vs Reach Width', + help: "Water Surface Elevation plotted against Reach Width for all nodes in the selected reach", + name: 'WSE vs Width', } ]) @@ -744,10 +744,12 @@ export const useChartsStore = defineStore('charts', () => { // iterate over stored charts and update the line visibility storedCharts.value.forEach((storedChart) => { try { - storedChart.chart.data.datasets.filter(ds => ds.seriesType != 'computed_series').forEach((dataset) => { - dataset.showLine = showLine.value - }) - storedChart.chart.update() + if (storedChart.chart != null) { + storedChart.chart.data.datasets.filter(ds => ds.seriesType != 'computed_series').forEach((dataset) => { + dataset.showLine = showLine.value + }) + storedChart.chart.update() + } } catch (error) { console.error('Error updating chart lines', error) } @@ -755,27 +757,36 @@ export const useChartsStore = defineStore('charts', () => { } const updateAllCharts = () => { + // This function updates the styling for all stored charts + // iterate over stored charts and update the line visibility storedCharts.value.forEach((storedChart) => { - try { - // check if the chart is a node chart or a reach chart - // and refresh the data accordingly - if (storedChart.chart.data.datasets[0].seriesType == 'swot_node_series') { - storedChart.chart.data.datasets = nodeChartData.value.datasets - } else { - storedChart.chart.data.datasets = chartData.value.datasets - } - storedChart.chart.update() - } catch (error) { - console.error('Error updating chart', error) + if (storedChart.chart != null) { + try { + // check if the chart is a node chart or a reach chart + // and refresh the data accordingly + if (storedChart.chart.data.datasets[0].seriesType == 'swot_node_series') { + storedChart.chart.data.datasets = nodeChartData.value.datasets + } else { + storedChart.chart.data.datasets = chartData.value.datasets + } + storedChart.chart.update() + } catch (error) { + console.error('Error updating chart', error) + } } }) + } - const storeMountedChart = (chart) => { + const storeMountedChart = (chart, x, y) => { + + chart.x = x + chart.y = y storedCharts.value.push(chart) - // clean stored charts that are undifined + console.log('Storing New Chart -> ', chart) + // clean stored charts that are undefined cleanStoredCharts() } @@ -785,6 +796,75 @@ export const useChartsStore = defineStore('charts', () => { } +const sortChartByX = (chart) => { + + // get the chart data and sort it by the x-axis variable. + // If the x-axis variable is time, sort by time otherwise + // sort numerically. + let chartData = chart.data.datasets[0].data + let xvar = activePlt.value.xvar.abbreviation + + if (xvar == 'time_str') { + return chartData.sort((a,b) => new Date(a.time_str) - new Date(b.time_str)); + } + else { + return chartData.sort((a,b) => parseFloat(a[xvar]) - parseFloat(b[xvar])); + } +} + +const getActiveChart = () => +{ + // Retrieves the currently active chart from the list of + // stored charts using its x and y labels + + // return null if no charts exist in the storedCharts object. This + // can happen on the initial page load. + if (storedCharts.value.length == 0) { + return null + } + + // get the x and y labels for the currenty active chart + let xlabel = activePlt.value.xvar.name + let ylabel = activePlt.value.yvar.name + + // add units to the labels, if they exist + if (activePlt.value.xvar.unit != null) { xlabel += ' (' + activePlt.value.xvar.unit + ')'} + if (activePlt.value.yvar.unit != null) { ylabel += ' (' + activePlt.value.yvar.unit + ')'} + + // return the stored chart that matches the current x and y labels + return storedCharts.value.filter(c => (c.x == xlabel && c.y == ylabel) )[0] +} + +const updateCurrentChart = () => { + + // get the currently active chart from the chartStore. + let storedChart = getActiveChart() + if (storedChart == null) { + return + } + + try { + // check if the chart is a node chart or a reach chart + // and refresh the data accordingly + if (storedChart.chart.data.datasets[0].seriesType == 'swot_node_series') { + storedChart.chart.data.datasets = nodeChartData.value.datasets + } else { + storedChart.chart.data.datasets = chartData.value.datasets + } + } catch (error) { + console.error('Error updating chart', error) + } + + storedChart.chart.data.datasets.filter(ds => ds.seriesType != 'computed_series').forEach((dataset) => { + dataset.showLine = showLine.value + dataset.pointStyle = getPointStyles(dataset) + }) + + // sort the chart data by the x-axis variable + storedChart.chart.data.datasets[0].data = sortChartByX(storedChart.chart) + + storedChart.chart.update() +} return { updateNodeChartData, @@ -811,8 +891,10 @@ export const useChartsStore = defineStore('charts', () => { updateShowLine, storeMountedChart, activePlt, + sortChartByX, activeNodeChart, activeReachChart, updateNodeDataSetStyles, + updateCurrentChart, } }) diff --git a/frontend/src/views/ChartsView.vue b/frontend/src/views/ChartsView.vue index c030047..a43fd0f 100644 --- a/frontend/src/views/ChartsView.vue +++ b/frontend/src/views/ChartsView.vue @@ -3,7 +3,7 @@ - Reach Timeseries + Reach Averaged diff --git a/frontend/src/views/DistanceCharts.vue b/frontend/src/views/DistanceCharts.vue index fb9f083..1cf427f 100644 --- a/frontend/src/views/DistanceCharts.vue +++ b/frontend/src/views/DistanceCharts.vue @@ -84,6 +84,21 @@ onMounted(() => { }) const changePlot = (plt) => { + // re-sort the chart data by the x-axis variable + // before rending the chart. Set the sorted data + // to the active data in the chart prior to rendering + const {activePlt} = storeToRefs(chartStore) + + // save the active plot so we can update it when + // controls (e.g. quality) are changed later. + activePlt.value = plt + + //chartData.value.datasets[0].data = chartStore.sortChartByX(plt) + + // force a re-render of the line charts + chartStore.updateCurrentChart() + //chartStore.updateShowLine() + router.push({ query: { ...router.currentRoute.value.query, variables: plt.abbreviation } }) } diff --git a/frontend/src/views/TimeSeriesCharts.vue b/frontend/src/views/TimeSeriesCharts.vue index ec66acb..0025d29 100644 --- a/frontend/src/views/TimeSeriesCharts.vue +++ b/frontend/src/views/TimeSeriesCharts.vue @@ -84,7 +84,27 @@ onMounted(() => { }) const changePlot = (plt) => { + // re-sort the chart data by the x-axis variable + // before rending the chart. Set the sorted data + // to the active data in the chart prior to rendering + //const {chartData, activeChartVariables, activeReachChart} = storeToRefs(chartStore) + const {activePlt} = storeToRefs(chartStore) + + + // save the active plot so we can update it when + // controls (e.g. quality) are changed later. + activePlt.value = plt + router.push({ query: { ...router.currentRoute.value.query, variables: plt.abbreviation } }) + //chartData.value.datasets[0].data = chartStore.sortChartByX(plt) + + // TODO: save this plt as the active plot so we can refresh the data in the chart.js class later. + + // force a re-render of the line charts + chartStore.updateCurrentChart() +// chartStore.updateShowLine() + + }