Skip to content

Commit

Permalink
Merge branch 'time-slider' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
devincowan committed Jun 29, 2024
2 parents 5405d85 + 4f0ce05 commit 8b6a142
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 49 deletions.
59 changes: 31 additions & 28 deletions frontend/src/components/LineChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,25 @@
</v-btn>
</v-expansion-panel-text>
</v-expansion-panel>
<v-expansion-panel :disabled="selectedTimes.length == 0" value="selectedTimes">
<v-expansion-panel :disabled="selectedTimeseriesPoints.length == 0" value="selectedTimeseriesPoints">
<v-expansion-panel-title>Selected Timestamps</v-expansion-panel-title>
<v-expansion-panel-text>
<v-list>
<v-list-item v-for="node in selectedTimes" :key="node.datetime">
<v-list-item v-for="timeSeriesPoint in selectedTimeseriesPoints" :key="timeSeriesPoint.datetime">
<template v-slot:append>
<v-icon :icon="mdiCloseBox" color="error" @click="removeSelectedNode(node)"></v-icon>
<v-icon :icon="mdiCloseBox" color="error"
@click="removeSelectedTimeseriesPoint(timeSeriesPoint)"></v-icon>
</template>
<v-list-item-content>
<v-list-item-title>{{ node.datetime }}</v-list-item-title>
<v-list-item-subtitle>{{ node.time_str }}</v-list-item-subtitle>
<v-list-item-title>{{ timeSeriesPoint.datetime }}</v-list-item-title>
<v-list-item-subtitle>{{ timeSeriesPoint.time_str }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-expansion-panel-text>
<v-btn v-if="selectedTimes.length > 0" class="ma-1 float-right" color="input" @click="viewLongProfileByDates">
<v-btn v-if="selectedTimeseriesPoints.length > 0" :loading="!chartStore.hasNodeData"
:disabled="!chartStore.hasNodeData" class="ma-1 float-right" color="input"
@click="viewLongProfileByDates">
<v-icon :icon="mdiChartBellCurveCumulative"></v-icon>
View Long Profile
</v-btn>
Expand All @@ -74,24 +77,24 @@ import {
import { Line } from 'vue-chartjs'
import 'chartjs-adapter-date-fns';
import { enUS } from 'date-fns/locale';
import { addMinutes, subMinutes } from "date-fns";
import { useChartsStore } from '@/stores/charts'
import { useAlertStore } from '@/stores/alerts'
import { useFeaturesStore } from '@/stores/features'
import { ref } from 'vue'
import { customCanvasBackgroundColor } from '@/_helpers/charts/plugins'
import { mdiDownloadBox, mdiFileDelimited, mdiCodeJson, mdiMagnifyMinusOutline, mdiChartBellCurveCumulative, mdiCloseBox } from '@mdi/js'
import { downloadCsv, downloadFeatureJson } from '../_helpers/hydroCron';
import { useDisplay } from 'vuetify'
import zoomPlugin from 'chartjs-plugin-zoom';
import { capitalizeFirstLetter } from '@/_helpers/charts/plugins'
import { NODE_DATETIME_VARIATION } from '@/constants'
const { lgAndUp } = useDisplay()
const panel = ref(["plotOptions"])
const selectedTimes = ref([])
const selectedTimeseriesPoints = ref([])
const chartStore = useChartsStore()
const alertStore = useAlertStore()
const featuresStore = useFeaturesStore()
const props = defineProps({ data: Object, chosenVariable: Object })
const line = ref(null)
const plotStyle = ref('Scatter')
Expand Down Expand Up @@ -230,46 +233,46 @@ const handleTimeseriesPointClick = (e) => {
// })
// console.log("average data for datetime:", dataForDatetime)
addSelectedNode(data)
addSelectedTimeseriesPoint(data)
}
const viewLongProfileByDates = () => {
// TODO use the datetime and plot all of the nodes' data for that datetime
// chartStore.filterDatasetsToTimeRange(allDatasets, subMinutes(datetime, NODE_DATETIME_VARIATION), addMinutes(datetime, NODE_DATETIME_VARIATION))
alertStore.displayAlert({
title: 'Long Profile Filter Not Implemented',
text: `The long profile filter by dates has not been implemented yet.`,
type: 'warning',
closable: true,
duration: 3
})
chartStore.setDatasetVisibility(chartStore.nodeChartData.datasets, true)
chartStore.filterDatasetsBySetOfDates(null, selectedTimeseriesPoints.value)
chartStore.chartTab = "distance"
// TODO: update the date range slider based on the selections
featuresStore.timeRange.value = [selectedTimeseriesPoints.value[0].datetime, selectedTimeseriesPoints.value[selectedTimeseriesPoints.value.length - 1].datetime]
}
const addSelectedNode = (node) => {
const addSelectedTimeseriesPoint = (timeSeriesPoint) => {
// first make sure the node is not already selected
if (selectedTimes.value.includes(node)) {
if (selectedTimeseriesPoints.value.includes(timeSeriesPoint)) {
alertStore.displayAlert({
title: 'Point already selected',
text: `The point at ${node.datetime} has already been selected.`,
text: `The point at ${timeSeriesPoint.datetime} has already been selected.`,
type: 'warning',
closable: true,
duration: 3
})
return
}
selectedTimes.value.push(node)
panel.value = ["selectedTimes"]
// instead of push, make sure the points are in order by datetime
const newPoints = [...selectedTimeseriesPoints.value, timeSeriesPoint]
newPoints.sort((a, b) => new Date(a.datetime) - new Date(b.datetime))
selectedTimeseriesPoints.value = newPoints
panel.value = ["selectedTimeseriesPoints"]
// TODO use datalabels to show selection?
// https://chartjs-plugin-datalabels.netlify.app/samples/events/selection.html
}
const removeSelectedNode = (node) => {
const index = selectedTimes.value.indexOf(node)
const removeSelectedTimeseriesPoint = (timeSeriesPoint) => {
const index = selectedTimeseriesPoints.value.indexOf(timeSeriesPoint)
if (index > -1) {
selectedTimes.value.splice(index, 1)
selectedTimeseriesPoints.value.splice(index, 1)
}
if (selectedTimes.value.length === 0) {
if (selectedTimeseriesPoints.value.length === 0) {
panel.value = ["plotOptions"]
}
}
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/components/TimeRangeSlider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import { ref } from 'vue'
import { useFeaturesStore } from '../stores/features';
import { useChartsStore } from '@/stores/charts'
import { addMinutes, subMinutes } from "date-fns";
import { NODE_DATETIME_VARIATION } from '@/constants'
// define an update event that emits the new range
const emit = defineEmits(['update'])
Expand Down Expand Up @@ -58,9 +56,7 @@ const updateDateRange = () => {
}
async function filterDatasetsToTimeRange() {
const startTime = subMinutes(dateRange.value[0], NODE_DATETIME_VARIATION)
const endTime = addMinutes(dateRange.value[1], NODE_DATETIME_VARIATION)
chartStore.filterDatasetsToTimeRange(chartStore.nodeChartData.datasets, startTime, endTime)
chartStore.filterDatasetsToTimeRange(chartStore.nodeChartData.datasets, dateRange.value[0], dateRange.value[1])
emit('update', sliderRange.value)
}
Expand Down
74 changes: 63 additions & 11 deletions frontend/src/stores/charts.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { defineStore } from 'pinia'
import { ref } from 'vue'
import { useFeaturesStore } from '@/stores/features'
import { NODE_DATETIME_VARIATION } from '@/constants'
import { addMinutes, subMinutes } from 'date-fns'

export const useChartsStore = defineStore('charts', () => {
let chartData = ref({})
let nodeChartData = ref({})
const showChart = ref(false)
const hasNodeData = ref(false)
const chartTab = ref('timeseries')

const updateChartData = (data) => {
// TODO: bug in reactivity
Expand Down Expand Up @@ -70,6 +72,12 @@ export const useChartsStore = defineStore('charts', () => {
return data
}

const setDatasetVisibility = (datasets, visible) => {
datasets.forEach((dataset) => {
dataset.hidden = !visible
})
}

const filterDataQuality = (dataQualityFlags, datasets) => {
console.log('Filtering data quality', dataQualityFlags)
console.log('Starting Datasets', datasets)
Expand All @@ -92,7 +100,7 @@ export const useChartsStore = defineStore('charts', () => {
})
}

const filterDatasetsToTimeRange = (datasets, start, end) => {
const filterDatasetsToTimeRange = (datasets, start, end, tolerance) => {
// if end is null, use now
if (end == null) {
end = new Date()
Expand All @@ -104,9 +112,16 @@ export const useChartsStore = defineStore('charts', () => {
console.error('Invalid time range')
return
}
if (tolerance == null) {
tolerance = NODE_DATETIME_VARIATION
}

start = subMinutes(start, tolerance)
end = addMinutes(end, tolerance)

if (datasets == null) {
console.log('Filtering using all datasets')
datasets = chartData.value.datasets
datasets = nodeChartData.value.datasets
}
console.log('Filtering time range', start, end)
console.log('Starting Datasets', datasets)
Expand All @@ -123,6 +138,41 @@ export const useChartsStore = defineStore('charts', () => {
console.log('Ending Datasets', datasets)
}

const filterDatasetsBySetOfDates = (datasets, selectedTimeseriesPoints, tolerance) => {
console.log('Filtering datasets by set of dates', datasets, selectedTimeseriesPoints)
if (tolerance == null) {
tolerance = NODE_DATETIME_VARIATION
}
if (selectedTimeseriesPoints == null) {
console.error('No dateTimeGroups provided')
return
}
if (datasets == null) {
console.log('Filtering using all datasets')
datasets = nodeChartData.value.datasets
}
console.log('Starting Datasets', datasets)

datasets.forEach((dataset) => {
// determine whether the dataset matches with the dateTimeGroups
if (
selectedTimeseriesPoints.some((timeSeriesPoint) => {
const startCuttoff = subMinutes(timeSeriesPoint.datetime, tolerance)
const endCuttoff = addMinutes(timeSeriesPoint.datetime, tolerance)
if (dataset.minDateTime > endCuttoff || dataset.maxDateTime < startCuttoff) {
return false
}
return true
})
) {
dataset.hidden = false
} else {
dataset.hidden = true
}
})
console.log('Ending Datasets', datasets)
}

const getNodeTimeStamps = (measurements) => {
// group the data by time range close enough to be considered the same
// takes an array of measurements and returns an array of arrays of measurements
Expand Down Expand Up @@ -161,8 +211,6 @@ export const useChartsStore = defineStore('charts', () => {
return timeStampGroups
}



const filterMeasurements = (measurements, dataQualityFlags) => {
// TODO: this is a hack to remove the invalid measurements
// need to handle this with a formal validator
Expand Down Expand Up @@ -244,7 +292,9 @@ export const useChartsStore = defineStore('charts', () => {
const datasets = timeStampGroups.map((timeStampGroup) => {
console.log('Time Stamp Group', timeStampGroup)
return {
label: `${featureStore.getFeatureName(nodes[0])} | ${nodes[0]?.properties?.reach_id} @ ${timeStampGroup[0].time_str}`,
label: `${featureStore.getFeatureName(nodes[0])} | ${nodes[0]?.properties?.reach_id} @ ${
timeStampGroup[0].time_str
}`,
data: timeStampGroup,
parsing: {
xAxisKey: 'p_dist_out',
Expand All @@ -253,8 +303,7 @@ export const useChartsStore = defineStore('charts', () => {
...getNodeDataSetStyle(timeStampGroup),
...getMinMaxDateTimes(timeStampGroup)
}
}
)
})
return datasets
}

Expand Down Expand Up @@ -345,7 +394,7 @@ export const useChartsStore = defineStore('charts', () => {
const styles = {
pointColors: [],
pointStyles: [],
dynamicColors: [],
dynamicColors: []
}
dataSet.forEach((m) => {
const { pointStyle, color } = getPointStyle(m.node_q)
Expand All @@ -362,10 +411,10 @@ export const useChartsStore = defineStore('charts', () => {
fill: styles.dynamicColors,
// color: styles.colors,
borderColor: styles.dynamicColors, // The line fill color.
backgroundColor: styles.dynamicColors, // The line color.
backgroundColor: styles.dynamicColors, // The line color.
spanGaps: false,
pointBackgroundColor: styles.pointColors,
pointBorderColor: styles.pointColors,
pointBorderColor: styles.pointColors
// borderWidth: 1,
}
}
Expand All @@ -383,6 +432,9 @@ export const useChartsStore = defineStore('charts', () => {
dynamicColors,
filterDataQuality,
filterDatasetsToTimeRange,
getNodeTimeStamps
filterDatasetsBySetOfDates,
setDatasetVisibility,
getNodeTimeStamps,
chartTab
}
})
8 changes: 3 additions & 5 deletions frontend/src/views/ChartsView.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<v-container v-if="hasData" fluid fill-height>
<v-tabs v-model="tab" align-tabs="center" fixed-tabs color="primary" grow>
<v-tabs v-model="chartStore.chartTab" align-tabs="center" fixed-tabs color="primary" grow>
<v-tab value="timeseries">
<v-icon :icon="mdiTimelineClock"></v-icon>
Timeseries
Expand All @@ -10,8 +10,8 @@
Distance
</v-tab>
</v-tabs>
<TimeSeriesCharts v-if="tab === 'timeseries'" />
<DistanceCharts v-if="tab === 'distance'" />
<TimeSeriesCharts v-if="chartStore.chartTab === 'timeseries'" />
<DistanceCharts v-if="chartStore.chartTab === 'distance'" />
</v-container>

<v-container v-if="!hasData">
Expand Down Expand Up @@ -56,6 +56,4 @@ let sheetText = ref(null)
let hasData = computed(() => chartStore.chartData && chartStore.chartData.datasets?.length > 0)
let tab = ref('timeseries')
</script>

0 comments on commit 8b6a142

Please sign in to comment.