From 38d7087bd87f57873df66ed5343fd3dce043a690 Mon Sep 17 00:00:00 2001 From: Kim Date: Wed, 6 Nov 2024 15:38:43 -0900 Subject: [PATCH] WIP: chart toggle now mostly works, de-emphasizes line when toggled off --- .../timeseries-results-menu.component.html | 16 +- .../timeseries-results-menu.component.ts | 158 ++++++++++-------- .../timeseries-chart.component.ts | 157 +++++++++-------- src/app/models/timeseries.model.ts | 10 ++ src/app/services/point-history.service.ts | 9 + src/app/store/charts/charts.action.ts | 11 +- src/app/store/charts/charts.reducer.ts | 48 +++++- src/app/store/charts/charts.selectors.ts | 32 ++++ 8 files changed, 278 insertions(+), 163 deletions(-) diff --git a/src/app/components/results-menu/timeseries-results-menu/timeseries-results-menu.component.html b/src/app/components/results-menu/timeseries-results-menu/timeseries-results-menu.component.html index 41620d1a2..97dac0228 100644 --- a/src/app/components/results-menu/timeseries-results-menu/timeseries-results-menu.component.html +++ b/src/app/components/results-menu/timeseries-results-menu/timeseries-results-menu.component.html @@ -16,30 +16,30 @@
- @if(pointHistory.length !== 0) { + @if(chartStates.length !== 0) {
- {{task().aoi}} + All AOIs
    - @for (point of pointHistory; track $index) { + @for (point of chartStates; track $index) {
  • place - {{point.flatCoordinates[1] | floatPrecision: 2}}, {{point.flatCoordinates[0] | floatPrecision: 2}} + {{point.geoemetry.getFlatCoordinates()[0] | floatPrecision: 2}}, {{point.geoemetry.getFlatCoordinates()[0] | floatPrecision: 2}} @@ -147,9 +147,7 @@
    - +
    diff --git a/src/app/components/results-menu/timeseries-results-menu/timeseries-results-menu.component.ts b/src/app/components/results-menu/timeseries-results-menu/timeseries-results-menu.component.ts index 1aa97aa2f..13ad08af7 100644 --- a/src/app/components/results-menu/timeseries-results-menu/timeseries-results-menu.component.ts +++ b/src/app/components/results-menu/timeseries-results-menu/timeseries-results-menu.component.ts @@ -1,5 +1,7 @@ import {Component, OnInit, Input, OnDestroy, ViewChild, ElementRef, computed, signal} from '@angular/core'; -import { first, Observable, Subject } from 'rxjs'; +import { first, Observable, + // Subject +} from 'rxjs'; import { ResizeEvent } from 'angular-resizable-element'; import { Store } from '@ngrx/store'; @@ -7,15 +9,20 @@ import { AppState } from '@store'; import * as uiStore from '@store/ui'; import * as searchStore from '@store/search'; import * as mapStore from '@store/map'; +import * as chartStore from '@store/charts'; -import { DrawService, MapService, NetcdfService, PointHistoryService, ScreenSizeService, WktService } from '@services'; +import { DrawService, MapService, NetcdfService, PointHistoryService, ScreenSizeService, + // WktService +} from '@services'; import { Breakpoints, SearchType, MapInteractionModeType, MapDrawModeType } from '@models'; import { SubSink } from 'subsink'; import { Point } from 'ol/geom'; -import { WKT } from 'ol/format'; +// import { WKT } from 'ol/format'; +import { getTimeseriesChartStates } from '@store/charts'; // import { getPathRange } from '@store/filters'; +import * as models from '@models'; export interface Task { aoi: string; @@ -49,9 +56,11 @@ export class TimeseriesResultsMenuComponent implements OnInit, OnDestroy { public breakpoints = Breakpoints; private subs = new SubSink(); - public pointHistory = []; + // public pointHistory = []; - public chartData = new Subject; + // public chartData = new Subject; + public chartStates: models.timeseriesChartItemState[] = [] + public allSeriesChecked$ = this.store$.select(chartStore.getAreAllTimeseriesChecked) public selectedPoint: number; // private timeseries_subscription: Subscription; @@ -60,7 +69,7 @@ export class TimeseriesResultsMenuComponent implements OnInit, OnDestroy { public dateRange = []; public totalPoints = 0; - public isLoading = false; + // public isLoading = false; constructor( private store$: Store, @@ -69,7 +78,7 @@ export class TimeseriesResultsMenuComponent implements OnInit, OnDestroy { private drawService: DrawService, private mapService: MapService, private netcdfService: NetcdfService, - private wktService: WktService + // private wktService: WktService ) { } ngOnInit(): void { @@ -93,45 +102,47 @@ export class TimeseriesResultsMenuComponent implements OnInit, OnDestroy { ) ); + this.subs.add(this.store$.select(getTimeseriesChartStates).subscribe(chartStates => { + this.chartStates = Object.values(chartStates); + } + )) this.subs.add(this.pointHistoryService.history$.subscribe(history => { - this.pointHistory = history; + // this.store$.dispatch(chartStore.set) + // this.pointHistory = history; this.mapService.setDisplacementLayer(history); - console.log('results menu sub this.pointHistory', this.pointHistory); - const task = this.task(); - let found = false - for (const point of this.pointHistory) { - found = false; - for (const pt of task.subtasks) { - if (pt.aoi.toString() === point.flatCoordinates.toString()) { - found = true; - break; - } - } - if (!found) { - let p = {aoi: point.flatCoordinates, checked: true}; - task.subtasks.push(p); - } - } - - console.log('results menu sub task.subtasks', task.subtasks); - - return {...task}; + // const task = this.task(); + // let found = false + // for (const point of this.pointHistory) { + // found = false; + // for (const pt of task.subtasks) { + // if (pt.aoi.toString() === point.flatCoordinates.toString()) { + // found = true; + // break; + // } + // } + // if (!found) { + // let p = {aoi: point.flatCoordinates, checked: true}; + // task.subtasks.push(p); + // } + // } + + // return {...task}; })); - let thing: string = localStorage.getItem('timeseries-points') - if(thing && thing.length > 0) { - let previous_points: any[] = thing?.split(';'); - if(previous_points.length > 0) { - console.log(previous_points) - previous_points = previous_points?.map(value => { - return this.wktService.wktToFeature(value, 'EPSG:4326'); - }) - previous_points?.forEach(point => { - this.pointHistoryService.addPoint(point.getGeometry()); - }) - } - } + // let thing: string = localStorage.getItem('timeseries-points') + // if(thing && thing.length > 0) { + // let previous_points: any[] = thing?.split(';'); + // if(previous_points.length > 0) { + // console.log(previous_points) + // previous_points = previous_points?.map(value => { + // return this.wktService.wktToFeature(value, 'EPSG:4326'); + // }) + // previous_points?.forEach(point => { + // this.pointHistoryService.addPoint(point.getGeometry()); + // }) + // } + // } this.subs.add(this.drawService.polygon$.subscribe(polygon => { if(polygon) { @@ -179,24 +190,21 @@ export class TimeseriesResultsMenuComponent implements OnInit, OnDestroy { this.store$.dispatch(new mapStore.SetMapInteractionMode(MapInteractionModeType.NONE)); } - public onPointClick(index: number) { - this.pointHistoryService.selectedPoint = index; - this.pointHistoryService.passDraw = true; - let format = new WKT(); - let wktRepresenation = format.writeGeometry(this.pointHistory[index]); - this.mapService.loadPolygonFrom(wktRepresenation.toString()) - } + // public onPointClick(index: number) { + // this.pointHistoryService.selectedPoint = index; + // this.pointHistoryService.passDraw = true; + // let format = new WKT(); + // let wktRepresenation = format.writeGeometry(this.pointHistory[index]); + // this.mapService.loadPolygonFrom(wktRepresenation.toString()) + // } public updateChart(): void { let allPointsData = []; - for (const geometry of this.pointHistory) { - this.netcdfService.getTimeSeries(geometry).pipe(first()).subscribe(data => { - console.log('updateChart data', data); - console.log('updateChart geometry', geometry); + for (const series of this.chartStates) { + this.netcdfService.getTimeSeries(series.geoemetry).pipe(first()).subscribe(data => { allPointsData.push(data); }) - console.log('updateChart allPointsData', allPointsData); - this.chartData.next(allPointsData); + // this.chartData.next(allPointsData); } } @@ -214,27 +222,29 @@ export class TimeseriesResultsMenuComponent implements OnInit, OnDestroy { return task.subtasks.some(t => t.checked) && !task.subtasks.every(t => t.checked); }); + public toggleAllSeries(checked: boolean) { + this.store$.dispatch(chartStore.setAllTimeseriesChecked({checked})) + } public updateSeries(checked: boolean, index?: number) { - console.log('updateSeries', checked, index); - this.task.update(task => { - if (index === undefined) { - task.checked = checked; - task.subtasks?.forEach(t => (t.checked = checked)); - } else { - task.subtasks![index].checked = checked; - task.checked = task.subtasks?.every(t => t.checked) ?? true; - this.pointHistoryService.selectedPoint = index; - console.log('updateSeries() this.pointHistoryService.selectedPoint', this.pointHistoryService.selectedPoint); - this.pointHistoryService.passDraw = true; - let format = new WKT(); - let wktRepresentation = format.writeGeometry(this.pointHistory[index]); - this.mapService.loadPolygonFrom(wktRepresentation.toString()) - } - console.log('updateSeries() task', task); - console.log('updateSeries() task.subtasks', task.subtasks); - console.log('updateSeries() this.pointHistory', this.pointHistory); - return {...task}; - }); + const wkt = this.chartStates[index]?.wkt + + this.store$.dispatch(chartStore.setTimeseriesChecked({wkt, checked})) + + // this.task.update(task => { + // if (index === undefined) { + // task.checked = checked; + // task.subtasks?.forEach(t => (t.checked = checked)); + // } else { + // task.subtasks![index].checked = checked; + // task.checked = task.subtasks?.every(t => t.checked) ?? true; + // this.pointHistoryService.selectedPoint = index; + // this.pointHistoryService.passDraw = true; + // let format = new WKT(); + // let wktRepresentation = format.writeGeometry(this.pointHistory[index]); + // this.mapService.loadPolygonFrom(wktRepresentation.toString()) + // } + // return {...task}; + // }); } public deletePoint(index: number) { console.log('delete', index); diff --git a/src/app/components/timeseries-chart/timeseries-chart.component.ts b/src/app/components/timeseries-chart/timeseries-chart.component.ts index a185c9c91..3ab68b73c 100644 --- a/src/app/components/timeseries-chart/timeseries-chart.component.ts +++ b/src/app/components/timeseries-chart/timeseries-chart.component.ts @@ -1,7 +1,10 @@ import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import * as d3 from 'd3'; -import { Observable, Subject } from 'rxjs'; +// import * as models from '@models'; +import { first, Observable, + // Subject +} from 'rxjs'; import { Store } from '@ngrx/store'; import { AppState } from '@store'; @@ -9,6 +12,8 @@ import { AppState } from '@store'; import * as chartsStore from '@store/charts'; import { SubSink } from 'subsink'; import { AsfLanguageService } from "@services/asf-language.service"; +import { NetcdfService } from '@services'; +import * as models from '@models'; // import {style} from '@angular/animations'; interface TimeSeriesChartPoint { @@ -19,7 +24,8 @@ interface TimeSeriesChartPoint { date: string file_name: string, temporal_baseline: number - id: string + id: string, + checked: boolean } interface TimeSeriesData { @@ -29,7 +35,8 @@ interface TimeSeriesData { interface DataReady { name: string, - values: TimeSeriesData[] + values: TimeSeriesData[], + opacity: number, } @Component({ @@ -43,7 +50,7 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { @Input() zoomIn$: Observable; @Input() zoomOut$: Observable; @Input() zoomToFit$: Observable; - @Input() chartData: Subject; + // @Input() chartData: models.timeseriesChartItemState[]; public json_data: string = ''; private svg?: d3.Selection; @@ -76,6 +83,7 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { private lineLabels; private points; public gColorPalette: any; + // private selectedScene: string; @Input() isLoading: boolean = false; @@ -89,17 +97,39 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { constructor( private store$: Store, private language: AsfLanguageService, + private netcdfService: NetcdfService ) { } public ngOnInit(): void { this.translateChartText(); this.createSVG(); + this.subs.add( + this.store$.select(chartsStore.getIsChartOutOfDate).subscribe( - this.chartData.subscribe(data => { - this.data = data; - this.initChart(data); - }) + ) + ) + this.subs.add( + this.store$.select(chartsStore.getTimeseriesChartStates).subscribe( + chartStates => { + const allPointsData: {point: {}, state: models.timeseriesChartItemState}[] = [] + this.store$.dispatch(chartsStore.setChartOutOfDate()) + for (const state of Object.values(chartStates)) { + this.netcdfService.getTimeSeries(state.geoemetry).pipe(first()).subscribe(data => { + allPointsData.push({point: data, state}); + }) + } + this.isLoading = false + this.store$.dispatch(chartsStore.setChartUpToDate()) + this.data = allPointsData + this.initChart(this.data) + } + ) + ) + // this.chartData.subscribe(data => { + // this.data = data; + // this.initChart(data); + // }) this.subs.add( this.store$.select(chartsStore.getShowLines).subscribe( @@ -149,50 +179,52 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { this.updateChart(); } - public initChart(data): void { + public initChart(data: {point: {}, state: models.timeseriesChartItemState}[]): void { this.dataSource = [] + this.dataReadyForChart = [] if (data?.[Symbol.iterator]) { let aoi: string = ''; - for (let result of data) { - aoi = ''; - // pre-process data, remove test v_2 files from results - // won't be necessary in production - for (let key of Object.keys(result)) { - if (key.startsWith('v_2_')) { - delete result[key]; - } - if (key.startsWith('aoi')) { - aoi = result[key]; + for (let result of data) { + aoi = ''; + // pre-process data, remove test v_2 files from results + // won't be necessary in production + for (let key of Object.keys(result.point)) { + if (key.startsWith('v_2_')) { + delete result.point[key]; + } + if (key.startsWith('aoi')) { + aoi = result.point[key]; + } } - } - this.timeSeriesData = []; - for (let key of Object.keys(result).filter(x => x !== 'mean' && x !== 'aoi')) { - this.dataSource.push({ - 'aoi': aoi, - 'short_wavelength_displacement': result[key].short_wavelength_displacement, - 'interferometric_correlation': result[key].interferometric_correlation, - 'temporal_coherence': result[key].temporal_coherence, - 'date': result[key].secondary_datetime, - 'file_name': key, - 'id': key, - 'temporal_baseline': result[key].temporal_baseline - }) - this.timeSeriesData.push({ - 'short_wavelength_displacement': result[key].short_wavelength_displacement, - 'date': result[key].secondary_datetime - }); - } - this.timeSeriesData.sort((a, b) => { - if(a.date < b.date) { - return -1 - } else { - return 1 + this.timeSeriesData = []; + for (let key of Object.keys(result.point).filter(x => x !== 'mean' && x !== 'aoi')) { + this.dataSource.push({ + 'aoi': aoi, + 'short_wavelength_displacement': result.point[key].short_wavelength_displacement, + 'interferometric_correlation': result.point[key].interferometric_correlation, + 'temporal_coherence': result.point[key].temporal_coherence, + 'date': result.point[key].secondary_datetime, + 'file_name': key, + 'id': key, + 'temporal_baseline': result.point[key].temporal_baseline, + 'checked': result.state.checked + }) + this.timeSeriesData.push({ + 'short_wavelength_displacement': result.point[key].short_wavelength_displacement, + 'date': result.point[key].secondary_datetime, + }); } - }) - this.dataReadyForChart.push({ 'name': aoi, 'values': this.timeSeriesData }); - this.averageData = ({ - ...data.mean + this.timeSeriesData.sort((a, b) => { + if(a.date < b.date) { + return -1 + } else { + return 1 + } }) + this.dataReadyForChart.push({ 'name': aoi, 'values': this.timeSeriesData, 'opacity': result.state.checked ? 1.0 : 0.4}); + // this.averageData = ({ + // ...data.mean + // }) } } else { this.dataSource = []; @@ -321,6 +353,7 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { }) // @ts-ignore .attr("stroke", function (d: DataReady) { return colorPalette(d.name) }) + .style("opacity", (d: DataReady) => d.opacity) .style("stroke-width", 1) .style("fill", "none") .style("shape-rendering", "geometricprecision") @@ -356,6 +389,7 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { .append('g') .attr('clip-path', 'url(#clip)') .style('fill', (d) : string=> { return colorPalette(d.name) }) + .style('opacity', (d) => d.opacity) .attr('class', (d) : string=> { return d.name.replace(/\W/g, '')}) .selectAll('circle') .data(d => d.values) @@ -386,21 +420,14 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { // the corresponding line. Note: we don't actually use Voronoi here, since an exhaustive search // is fast enough. private pointerMoved(event, lines, dots, points) { - console.log('pointerMoved'); if (typeof points === 'undefined') { return; } if (points == null) { return; } const [xm, ym] = d3.pointer(event); const i = d3.leastIndex(points, ([x, y]) => Math.hypot(Number(x) - xm, Number(y) - ym)); if (typeof points[i] === 'undefined') { return; } - const [x, y, k] = points[i]; + const [_x, _y, k] = points[i]; let colorName: string; let dClassName: string; - console.log('points', points); - console.log('points[i]', points[i]); - console.log('dots', dots); - console.log('xm', xm, 'ym', ym); - console.log('i', i); - console.log('x', x, 'y', y, 'k', k); lines.style("stroke", (d: DataReady)=> { if (d.name === k) { dClassName = 'g.' + d.name.replace(/\W/g, ''); @@ -408,34 +435,19 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { return colorName; } return '#ddd'; - }); - dots.selectAll('circle').style("fill", '#ddd'); + }) + dots.selectAll('circle').style("fill", '#ddd').style('opacity', d => d.opacity); dots.selectAll(dClassName).style("fill", 'red'); - // dots.selectAll('myDots').data(this.dataReadyForChart).style("fill", (d: DataReady) => { - // dots.selectAll('circle').style("fill", (d: DataReady) => { - // console.log('dots d:', d); - // if (d.name === k) { - // return this.gColorPalette(d.name); - // } - // return '#ddd'; - // }); - // this.lines.style("stroke", "red").filter(({ z }) => z === k).raise(); - // .attr("stroke", function (d: DataReady) { return colorPalette(d.name) }) - // lines.style("stroke", ({z}) => z === k ? null : "#ddd").filter(({z}) => z === k).raise(); - // dots.attr("transform", `translate(${x},${y})`); dots.select("text").text(k); - // this.svg.property("value", this.dataReadyForChart[i]).dispatch("input", {bubbles: true}); } private pointerEntered(lines, dots) { - console.log('pointerEntered lines, dots', lines, dots); lines.style("mix-blend-mode", null).style("stroke", "#ddd"); dots.attr("display", null); } - private pointerLeft(lines, dots) { - console.log('pointerLeft', lines, dots); + private pointerLeft(lines, _dots) { lines.style("stroke", (d: DataReady)=> { return this.gColorPalette(d.name); }) @@ -471,6 +483,7 @@ export class TimeseriesChartComponent implements OnInit, OnDestroy { .attr("d", function (d) { // @ts-ignore return line(d.values) }) + .style('opacity', (d: DataReady) => d.opacity) this.lineLabels .attr("transform",d => `translate(${newX(Date.parse(d.value.date))},${newY(d.value.short_wavelength_displacement)})`) // Put the text at the position of the last point diff --git a/src/app/models/timeseries.model.ts b/src/app/models/timeseries.model.ts index 5885d9f34..d2bec3de6 100644 --- a/src/app/models/timeseries.model.ts +++ b/src/app/models/timeseries.model.ts @@ -1,3 +1,5 @@ +import { SimpleGeometry } from "ol/geom" + export interface TimeSeriesResult { averages: layerInfo, coordinates: number[], @@ -9,4 +11,12 @@ export interface layerInfo { short_wavelength_displacement: number[], interferometric_correlation: number[], temporal_coherence: number[] +} + +export interface timeseriesChartItemState { + checked: boolean, + color: string, + name: string, + wkt: string, + geoemetry: SimpleGeometry } \ No newline at end of file diff --git a/src/app/services/point-history.service.ts b/src/app/services/point-history.service.ts index 3615e7da4..da19882fe 100644 --- a/src/app/services/point-history.service.ts +++ b/src/app/services/point-history.service.ts @@ -1,4 +1,7 @@ import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@store'; +import { addTimeseriesState } from '@store/charts'; import WKT from 'ol/format/WKT'; import { Point } from 'ol/geom'; @@ -14,7 +17,9 @@ export class PointHistoryService { public history$ = new Subject(); public passDraw: boolean = false; public selectedPoint: number = 0; + constructor( + private store$: Store ) { @@ -30,7 +35,11 @@ export class PointHistoryService { this.passDraw = false return } + const format = new WKT() + // this.history.map((value) => { + const wkt = format.writeGeometry(point) this.history.push(point); + this.store$.dispatch(addTimeseriesState({item: {geoemetry: point, checked: true, wkt: wkt, color: '#FFFFFF', name: `Series ${this.history.length}`}})) this.history$.next(this.history); this.savePoints(); } diff --git a/src/app/store/charts/charts.action.ts b/src/app/store/charts/charts.action.ts index 62f5f2bef..6167e0a10 100644 --- a/src/app/store/charts/charts.action.ts +++ b/src/app/store/charts/charts.action.ts @@ -1,5 +1,12 @@ -import { createAction } from '@ngrx/store'; +import { createAction, props } from '@ngrx/store'; +import * as models from '@models'; export const showGraphLines = createAction('[Chart] show lines') export const hideGraphLines = createAction('[Chart] hide lines') -export const reset = createAction('[Chart] reset chart options') \ No newline at end of file +export const reset = createAction('[Chart] reset chart options') +export const setTimeseriesStates = createAction('[Chart] sets the checked timeseries', props<{'items': models.timeseriesChartItemState[]}>()) +export const addTimeseriesState = createAction('[Chart] add checked timeseries', props<{'item': models.timeseriesChartItemState}>()) +export const setTimeseriesChecked = createAction('[Chart] set single timeseries as checked/unchecked', props<{'wkt': string, 'checked': boolean}>()) +export const setAllTimeseriesChecked = createAction('[Chart] set all timeseries as checked/unchecked', props<{'checked': boolean}>()) +export const setChartOutOfDate = createAction('[Chart] mark chart as out of date') +export const setChartUpToDate = createAction('[Chart], mark chart as up to date') \ No newline at end of file diff --git a/src/app/store/charts/charts.reducer.ts b/src/app/store/charts/charts.reducer.ts index 8d6f57802..577df7634 100644 --- a/src/app/store/charts/charts.reducer.ts +++ b/src/app/store/charts/charts.reducer.ts @@ -1,17 +1,53 @@ import { createReducer, on } from '@ngrx/store'; -import { showGraphLines, hideGraphLines, reset } from './charts.action'; +import * as chartActions from './charts.action'; +import * as models from '@models'; export interface ChartsState { - showLines: boolean; + showLines: boolean; + seriesStates: { [key: string]: models.timeseriesChartItemState }; + outOfDate: boolean; } export const initialState: ChartsState = { - showLines: true + showLines: true, + seriesStates: {}, + outOfDate: false }; export const chartsReducer = createReducer( initialState, - on(showGraphLines, (state) => ({...state, showLines: true})), - on(hideGraphLines, (state) => ({...state, showLines: false})), - on(reset, (_) => initialState) + on(chartActions.showGraphLines, (state) => ({ ...state, showLines: true })), + on(chartActions.hideGraphLines, (state) => ({ ...state, showLines: false })), + on(chartActions.setTimeseriesChecked, (state, { wkt, checked }) => { + // const output = { ...state }; + // if (wkt in output.seriesStates) { + // output.seriesStates[wkt].checked = checked; + // } + + const seriesState = { ...state.seriesStates, [wkt]: { ...state.seriesStates[wkt], checked } } + return { ...state, seriesStates: seriesState } + // return output; + }), + on(chartActions.setTimeseriesStates, (state, { items }) => ({ + ...state, seriesStates: items.reduce((prev: { [key: string]: models.timeseriesChartItemState }, curr) => { + prev[curr.wkt] = { checked: true, color: curr.color, name: curr.name, wkt: curr.wkt, geoemetry: curr.geoemetry } + return prev + }, {}) + } + )), + on(chartActions.addTimeseriesState, (state, { item }) => { + + const seriesState = { ...state.seriesStates, [item.wkt]: item } + return { ...state, seriesStates: seriesState } + }), + on(chartActions.setAllTimeseriesChecked, (state, { checked }) => { + const seriesStates = Object.values(state.seriesStates).reduce((prev, curr) => { + prev[curr.wkt] = { ...curr, checked } + return prev + }, {}); + return {...state, seriesStates} + }), + on(chartActions.setChartOutOfDate, (state) => ({...state, outOfDate: true})), + on(chartActions.setChartUpToDate, (state) => ({...state, outOfDate: false})), + on(chartActions.reset, (_) => initialState) ); diff --git a/src/app/store/charts/charts.selectors.ts b/src/app/store/charts/charts.selectors.ts index 94ef485ad..e63bfe248 100644 --- a/src/app/store/charts/charts.selectors.ts +++ b/src/app/store/charts/charts.selectors.ts @@ -6,4 +6,36 @@ export const getChartsState = createFeatureSelector('charts'); export const getShowLines = createSelector( getChartsState, (state: ChartsState) => state.showLines +); + +export const getTimeseriesChartStates = createSelector( + getChartsState, + (state: ChartsState) => state.seriesStates +); + +export const getCheckedTimeseries = createSelector( + getTimeseriesChartStates, + (chartStates) => { + const output = {}; + for (const key in Object.keys(chartStates)) { + if (chartStates[key].checked) { + output[key] = chartStates[key]; + } + } + + return output; + } +); + +export const getAreAllTimeseriesChecked = createSelector( + getTimeseriesChartStates, + getTimeseriesChartStates, + (checked, all) => { + return Object.keys(checked).length === Object.keys(all).length + } +) + +export const getIsChartOutOfDate = createSelector( + getChartsState, + (state) => state.outOfDate ) \ No newline at end of file