Skip to content

Commit 7e0a8ab

Browse files
authored
Merge pull request #141 from CUAHSI/CAM-399/graph-lines-markers
CAM-399/graph-lines-markers
2 parents c3cf158 + a83ca25 commit 7e0a8ab

8 files changed

Lines changed: 113 additions & 42 deletions

File tree

frontend/src/components/DataQuality.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232

3333
<script setup>
3434
import { useChartsStore } from '@/stores/charts'
35+
import { useStatsStore } from '../stores/stats'
3536
import { ref } from 'vue'
3637
import { storeToRefs } from 'pinia'
3738
3839
const chartStore = useChartsStore()
40+
const statsStore = useStatsStore()
3941
const { dataQualityFlags } = storeToRefs(chartStore)
4042
const panel = ref([])
4143
@@ -45,6 +47,9 @@ function qualityHasChanged() {
4547
4648
// apply the time slider filter to the data
4749
chartStore.filterDatasetsToTimeRange()
48-
chartStore.updateAllCharts()
50+
chartStore.refreshAllCharts()
51+
52+
// stats are not shown after update of data quality
53+
statsStore.toggleSeriesStatistics(chartStore.showStatistics.value)
4954
}
5055
</script>

frontend/src/components/LineChart.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import { useDisplay } from 'vuetify'
7575
import { onMounted, nextTick } from 'vue'
7676
import { mdiChartBellCurveCumulative, mdiCloseBox, mdiMagnifyMinusOutline } from '@mdi/js'
7777
import { convertDateStringToSeconds } from '@/_helpers/time'
78+
import { useStatsStore } from '../stores/stats'
7879
7980
const { lgAndUp } = useDisplay()
8081
const panel = ref(['plotActions'])
@@ -83,6 +84,7 @@ const selectedTimeseriesPoints = ref([])
8384
const hasSelectedTimeseriesPoints = computed(() => selectedTimeseriesPoints.value.length > 0)
8485
8586
const chartStore = useChartsStore()
87+
const statsStore = useStatsStore()
8688
const alertStore = useAlertStore()
8789
const featuresStore = useFeaturesStore()
8890
const props = defineProps({ data: Object, chosenPlot: Object })
@@ -111,7 +113,7 @@ onMounted(async () => {
111113
112114
// push the chart to the store
113115
chartStore.storeMountedChart(activeReachChart.value)
114-
chartStore.updateShowLine()
116+
chartStore.updateSymbology()
115117
})
116118
117119
const getParsing = () => {
@@ -272,7 +274,9 @@ const viewLongProfileByDates = () => {
272274
// chartStore.filterDatasetsToTimeRange()
273275
chartStore.chartTab = 'distance'
274276
chartStore.updateNodeDataSetStyles()
275-
chartStore.updateAllCharts()
277+
chartStore.refreshAllCharts()
278+
// if stats are turned on, the stats will be stale
279+
statsStore.toggleSeriesStatistics(chartStore.showStatistics.value)
276280
}
277281
278282
const addSelectedTimeseriesPoint = (timeSeriesPoint) => {

frontend/src/components/NodeChart.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ onMounted(async () => {
5757
5858
// push the chart to the store
5959
chartStore.storeMountedChart(activeNodeChart.value)
60-
chartStore.updateShowLine()
60+
chartStore.updateSymbology()
6161
})
6262
6363
const resetZoom = () => {

frontend/src/components/PlotOptions.vue

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
<v-expansion-panel-text>
66
<StatisticsToggle v-if="chartStore.chartTab === 'distance'" />
77
<v-select
8-
label="Plot Style"
9-
v-model="showLine"
10-
:items="[
11-
{ title: 'Scatter', value: false },
12-
{ title: 'Connected', value: true }
13-
]"
14-
@update:modelValue="chartStore.updateShowLine"
15-
></v-select>
8+
label="Symbology"
9+
v-model="symbology"
10+
:items="['Lines', 'Markers']"
11+
multiple
12+
chips
13+
@update:modelValue="chartStore.updateSymbology"
14+
:rules="[rules.filter]"
15+
>
16+
</v-select>
1617
</v-expansion-panel-text>
1718
</v-expansion-panel>
1819
</v-expansion-panels>
@@ -24,5 +25,14 @@ import { storeToRefs } from 'pinia'
2425
import StatisticsToggle from './StatisticsToggle.vue'
2526
2627
const chartStore = useChartsStore()
27-
const { showLine } = storeToRefs(chartStore)
28+
const { symbology } = storeToRefs(chartStore)
29+
30+
const rules = {
31+
filter: (v) => {
32+
if (!v.includes('Lines') && !v.includes('Markers')) {
33+
return 'No datasets will be displayed - this can be useful for exploring statistics.'
34+
}
35+
return true
36+
}
37+
}
2838
</script>

frontend/src/components/TimeRangeSelector.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ const timeRangeUpdateComplete = async () => {
8080
chartStore.updateNodeChartData(datasets)
8181
8282
// update the chart
83-
chartStore.updateAllCharts()
83+
chartStore.refreshAllCharts()
84+
statsStore.toggleSeriesStatistics(chartStore.showStatistics.value)
8485
}
8586
}
8687

frontend/src/stores/charts.js

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineStore, storeToRefs } from 'pinia'
2-
import { ref, watch } from 'vue'
2+
import { nextTick, ref, watch } from 'vue'
33
import { useFeaturesStore } from '@/stores/features'
44
import { NODE_DATETIME_VARIATION } from '@/constants'
55
import { addMinutes, subMinutes } from 'date-fns'
@@ -22,7 +22,7 @@ export const useChartsStore = defineStore(
2222
const hasNodeData = ref(false)
2323
const chartTab = ref('timeseries')
2424
const showStatistics = ref(false)
25-
const showLine = ref(true)
25+
const symbology = ref(['Lines', 'Markers'])
2626
const dataQualityFlags = ref([0, 1, 2]) // by default don't show bad data
2727
let colorScale = chroma.scale('YlGnBu').mode('lch').colors(3)
2828
const router = useRouter()
@@ -141,17 +141,29 @@ export const useChartsStore = defineStore(
141141
hasNodeData.value = false
142142
}
143143

144+
const symbologyContains = (value) => {
145+
// check if symbology.value is an array
146+
if (Array.isArray(symbology.value)) {
147+
return symbology.value.includes(value)
148+
}
149+
return false
150+
}
151+
144152
const getLabels = (selectedFeatures) => {
145153
// TODO: for now we just use the first query
146154
// when compact = true, there will only be a single feature
147-
const propertyObject = selectedFeatures[0].queries[0].results.geojson.features[0].properties
148-
const labels = propertyObject.time_str.map((time_str) => {
149-
if (time_str == 'no_data') {
150-
return
151-
}
152-
return time_str
153-
})
154-
return labels.filter((l) => l != undefined)
155+
try {
156+
const propertyObject = selectedFeatures[0].queries[0].results.geojson.features[0].properties
157+
const labels = propertyObject.time_str.map((time_str) => {
158+
if (time_str == 'no_data') {
159+
return
160+
}
161+
return time_str
162+
})
163+
return labels.filter((l) => l != undefined)
164+
} catch (error) {
165+
console.error('Error getting labels', error)
166+
}
155167
}
156168

157169
const getTitle = () => {
@@ -234,7 +246,9 @@ export const useChartsStore = defineStore(
234246
datasets.forEach((dataset) => {
235247
dataQualityFilterSingleDataset(dataset)
236248
// update the line visibility
237-
dataset.showLine = showLine.value
249+
dataset.showLine = symbologyContains('Lines')
250+
// https://www.chartjs.org/docs/latest/charts/line.html#dataset-properties
251+
// storedChart.chart.options.elements.point.pointstyle = false
238252
})
239253
}
240254
}
@@ -257,7 +271,7 @@ export const useChartsStore = defineStore(
257271
dataset.pointBorderColor = getPointBorderColors(dataset)
258272
}
259273

260-
const filterDatasetsToTimeRange = (start, end, tolerance) => {
274+
const filterDatasetsToTimeRange = async (start, end, tolerance) => {
261275
// if end is null, use now
262276
const featureStore = useFeaturesStore()
263277
const { timeRange } = storeToRefs(featureStore)
@@ -338,8 +352,9 @@ export const useChartsStore = defineStore(
338352
}
339353
})
340354
}
355+
await nextTick()
341356
updateNodeDataSetStyles()
342-
updateAllCharts()
357+
refreshAllCharts()
343358
}
344359

345360
const filterDatasetsBySetOfDates = (datasets, selectedTimeseriesPoints, tolerance) => {
@@ -667,7 +682,7 @@ export const useChartsStore = defineStore(
667682

668683
const getDataSetStyle = () => {
669684
const style = {
670-
showLine: showLine.value,
685+
showLine: symbologyContains('Lines'),
671686
fill: true,
672687
pointBorderColor: (ctx) => getPointBorderColors(ctx.dataset),
673688
pointStyle: (ctx) => getPointStyles(ctx.dataset),
@@ -711,7 +726,7 @@ export const useChartsStore = defineStore(
711726
const getNodeDataSetStyle = (dataSet) => {
712727
const colors = getDateGradientColors(dataSet)
713728
return {
714-
showLine: showLine.value,
729+
showLine: symbologyContains('Lines'),
715730
pointRadius: 5,
716731
pointHoverRadius: 15,
717732
//fill: styles.dynamicColors,
@@ -745,17 +760,26 @@ export const useChartsStore = defineStore(
745760
}
746761
}
747762
})
763+
updateSymbology()
748764
}
749765

750-
const updateShowLine = () => {
766+
const updateSymbology = () => {
767+
let showLine = true
768+
let showMarkers = false
769+
// if symbology.value is an array of strings, check if it includes 'Lines' or 'Markers'
770+
if (Array.isArray(symbology.value)) {
771+
showLine = symbology.value.includes('Lines')
772+
showMarkers = symbology.value.includes('Markers')
773+
}
751774
// iterate over stored charts and update the line visibility
752775
storedCharts.value.forEach((storedChart) => {
753776
try {
754777
if (storedChart.chart != null) {
755778
storedChart.chart.data.datasets
756779
.filter((ds) => ds.seriesType != 'computed_series')
757780
.forEach((dataset) => {
758-
dataset.showLine = showLine.value
781+
dataset.showLine = showLine
782+
dataset.pointRadius = showMarkers ? 5 : 0
759783
})
760784
storedChart.chart.update()
761785
}
@@ -765,7 +789,7 @@ export const useChartsStore = defineStore(
765789
})
766790
}
767791

768-
const updateAllCharts = () => {
792+
const updateAllChartsData = () => {
769793
// iterate over stored charts and update the line visibility
770794
storedCharts.value.forEach((storedChart) => {
771795
try {
@@ -776,6 +800,7 @@ export const useChartsStore = defineStore(
776800
} else {
777801
storedChart.chart.data.datasets = chartData.value.datasets
778802
}
803+
updateSymbology()
779804
storedChart.chart.update()
780805

781806
// if all datasets in the chart are hidden, alert the user!
@@ -798,6 +823,18 @@ export const useChartsStore = defineStore(
798823
})
799824
}
800825

826+
const refreshAllCharts = async () => {
827+
// iterate over stored charts and refresh
828+
storedCharts.value.forEach(async (storedChart) => {
829+
try {
830+
await storedChart.chart.update()
831+
} catch (error) {
832+
console.error('Error refreshing chart', error)
833+
}
834+
})
835+
return
836+
}
837+
801838
const storeMountedChart = (chart) => {
802839
storedCharts.value.push(chart)
803840

@@ -809,7 +846,7 @@ export const useChartsStore = defineStore(
809846
const query = {
810847
variables: activePlt.value.abbreviation,
811848
plot: chartTab.value,
812-
showLine: showLine.value,
849+
symbology: symbology.value,
813850
showStatistics: showStatistics.value,
814851
dataQualityFlags: dataQualityFlags.value
815852
// timeRange: timeRange.value,
@@ -840,12 +877,20 @@ export const useChartsStore = defineStore(
840877
console.error('Invalid Plot Abbreviation', query.variables)
841878
}
842879
}
843-
if (query.showLine) {
844-
showLine.value = query.showLine == 'true'
845-
}
846880
if (query.showStatistics) {
847881
showStatistics.value = query.showStatistics == 'true'
848882
}
883+
if (query.symbology) {
884+
// if the query is a string, convert it to an array
885+
if (typeof query.symbology === 'string') {
886+
query.symbology = [query.symbology]
887+
}
888+
889+
// and remove duplicates
890+
query.symbology = [...new Set(query.symbology)]
891+
892+
symbology.value = query.symbology
893+
}
849894
if (query.dataQualityFlags) {
850895
// if the query is a string, convert it to an array
851896
if (typeof query.dataQualityFlags === 'string') {
@@ -873,8 +918,8 @@ export const useChartsStore = defineStore(
873918
console.log('Active PLT Changed', pre, post)
874919
updateRouteAfterPlotChange()
875920
})
876-
watch(showLine, () => {
877-
console.log('Show Line Changed', showLine.value)
921+
watch(symbology, () => {
922+
console.log('Symbology Changed', symbology.value)
878923
updateRouteAfterPlotChange()
879924
})
880925
watch(showStatistics, () => {
@@ -906,7 +951,8 @@ export const useChartsStore = defineStore(
906951
hasNodeData,
907952
dynamicColors,
908953
dataQualityFilterAllDatasets,
909-
updateAllCharts,
954+
updateAllChartsData,
955+
refreshAllCharts,
910956
filterDatasetsToTimeRange,
911957
filterDatasetsBySetOfDates,
912958
dataQualityOptions,
@@ -917,8 +963,8 @@ export const useChartsStore = defineStore(
917963
nodeCharts,
918964
reachCharts,
919965
showStatistics,
920-
showLine,
921-
updateShowLine,
966+
symbology,
967+
updateSymbology,
922968
storeMountedChart,
923969
activePlt,
924970
generateDataQualityLegend,

frontend/src/stores/stats.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export const useStatsStore = defineStore('stats', () => {
126126
chartStore.updateNodeChartData(updatedDatasets)
127127

128128
// update the charts
129-
chartStore.updateAllCharts()
129+
chartStore.updateAllChartsData()
130130
}
131131

132132
return {

frontend/src/views/ChartsView.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
<script setup>
3636
import { useChartsStore } from '../stores/charts'
37+
import { useStatsStore } from '../stores/stats'
3738
import { useFeaturesStore } from '../stores/features'
3839
import { RouterLink, useRouter } from 'vue-router'
3940
import { computed, watch, onMounted } from 'vue'
@@ -51,7 +52,9 @@ const router = useRouter()
5152
5253
const chartStore = useChartsStore()
5354
const featuresStore = useFeaturesStore()
55+
const statsStore = useStatsStore()
5456
const { querying, activeFeature, selectedFeatures } = storeToRefs(featuresStore)
57+
const { showStatistics } = storeToRefs(chartStore)
5558
5659
// use a watcher to run the query once the active feature is set
5760
// we do this because it seems that async queries from esri leaflet
@@ -89,6 +92,8 @@ const runQuery = async () => {
8992
await getNodeDataForReach(activeFeature.value)
9093
querying.value.nodes = false
9194
chartStore.buildDistanceChart(featuresStore.nodes)
95+
// show stats if they are enabled
96+
statsStore.toggleSeriesStatistics(showStatistics.value)
9297
}
9398
9499
let hasData = computed(() => chartStore.chartData && chartStore.chartData.datasets?.length > 0)

0 commit comments

Comments
 (0)