@@ -30,7 +30,7 @@ import {
3030import { GeoJSON , GeoJsonProperties } from 'geojson' ;
3131import { AuthContext } from 'react-oauth2-code-pkce' ;
3232import { setToken } from 'store/AuthSlice' ;
33- import { ScenarioVisibility } from 'store/DataSelectionSlice' ;
33+ import { AggregationWindow , ScenarioVisibility } from 'store/DataSelectionSlice' ;
3434
3535interface DataContextType {
3636 geoData : GeoJSON ;
@@ -68,9 +68,26 @@ export default function SelectedDataContext(props: {baseData: BaseData; children
6868 const referenceDate = useAppSelector ( ( state ) => state . dataSelection . simulationStart ) ;
6969 const groupFilters = useAppSelector ( ( state ) => state . dataSelection . groupFilters ) ;
7070 const relativeNumbers = useAppSelector ( ( state ) => state . dataSelection . relativeNumbers ) ;
71+ const aggregationWindow = useAppSelector ( ( state ) => state . dataSelection . aggregationWindow ) ;
7172
7273 const { token} = useContext ( AuthContext ) ;
7374
75+ // Helper to subtract days from an ISO date string (YYYY-MM-DD)
76+ function subtractDays ( isoDate : string , days : number ) : string {
77+ const d = new Date ( isoDate ) ;
78+ d . setUTCDate ( d . getUTCDate ( ) - days ) ;
79+ const year = d . getUTCFullYear ( ) ;
80+ const month = String ( d . getUTCMonth ( ) + 1 ) . padStart ( 2 , '0' ) ;
81+ const day = String ( d . getUTCDate ( ) ) . padStart ( 2 , '0' ) ;
82+ return `${ year } -${ month } -${ day } ` ;
83+ }
84+
85+ const aggregationOffset = useMemo ( ( ) => {
86+ if ( aggregationWindow === AggregationWindow . OneDay ) return 1 ;
87+ if ( aggregationWindow === AggregationWindow . SevenDays ) return 7 ;
88+ return 0 ;
89+ } , [ aggregationWindow ] ) ;
90+
7491 useEffect ( ( ) => {
7592 dispatch ( setToken ( token ) ) ;
7693 } , [ dispatch , token ] ) ;
@@ -93,7 +110,11 @@ export default function SelectedDataContext(props: {baseData: BaseData; children
93110 scenarioId : caseData || '' ,
94111 } ,
95112 query : {
96- startDate : referenceDate ! ,
113+ startDate : referenceDate
114+ ? aggregationOffset > 0
115+ ? subtractDays ( referenceDate , aggregationOffset )
116+ : referenceDate
117+ : undefined ,
97118 endDate : referenceDate ! ,
98119 nodes : [ selectedDistrict ] ,
99120 percentiles : [ '50' ] ,
@@ -131,7 +152,8 @@ export default function SelectedDataContext(props: {baseData: BaseData; children
131152 {
132153 pathIds : activeScenarios ,
133154 query : {
134- startDate : selectedDate ! ,
155+ startDate :
156+ selectedDate && aggregationOffset > 0 ? subtractDays ( selectedDate , aggregationOffset ) : selectedDate ! ,
135157 endDate : selectedDate ! ,
136158 nodes : [ selectedDistrict ] ,
137159 percentiles : [ '50' ] ,
@@ -156,7 +178,8 @@ export default function SelectedDataContext(props: {baseData: BaseData; children
156178 {
157179 pathIds : activeScenarios ,
158180 query : {
159- startDate : selectedDate ! ,
181+ startDate :
182+ selectedDate && aggregationOffset > 0 ? subtractDays ( selectedDate , aggregationOffset ) : selectedDate ! ,
160183 endDate : selectedDate ! ,
161184 nodes : [ selectedDistrict ] ,
162185 percentiles : [ '50' ] ,
@@ -208,7 +231,8 @@ export default function SelectedDataContext(props: {baseData: BaseData; children
208231 {
209232 path : { scenarioId : selectedScenario ! } ,
210233 query : {
211- startDate : selectedDate ! ,
234+ startDate :
235+ selectedDate && aggregationOffset > 0 ? subtractDays ( selectedDate , aggregationOffset ) : selectedDate ! ,
212236 endDate : selectedDate ! ,
213237 compartments : [ selectedCompartment ! ] ,
214238 groups : totalGroup ? [ totalGroup . id ] : [ ] ,
@@ -225,42 +249,86 @@ export default function SelectedDataContext(props: {baseData: BaseData; children
225249 return map ;
226250 } , [ props . baseData . nodes ] ) ;
227251
228- const normalizeInfectionData = useMemo ( ( ) => {
252+ // Derive windowed series (total/new1d/new7d) per node/group/compartment/percentile
253+ const deriveWindowedSeries = useMemo ( ( ) => {
254+ return ( data : InfectionData , window : AggregationWindow | null ) : InfectionData => {
255+ if ( ! data || ! Array . isArray ( data ) || window === null || window === AggregationWindow . Total ) {
256+ console . log ( 'deriveWindowedSeries' , data , window ) ;
257+ return data ?? [ ] ;
258+ }
259+
260+ const groups = new Map < string , InfectionData > ( ) ;
261+ const makeKey = ( e : ( typeof data ) [ number ] ) =>
262+ `${ e . node ?? '' } |${ e . group ?? '' } |${ e . compartment ?? '' } |${ e . aggregation ?? '' } |${ e . percentile } ` ;
263+
264+ for ( const e of data ) {
265+ const k = makeKey ( e ) ;
266+ if ( ! groups . has ( k ) ) groups . set ( k , [ ] ) ;
267+ groups . get ( k ) ! . push ( e ) ;
268+ }
269+
270+ const out : InfectionData = [ ] ;
271+ for ( const entries of groups . values ( ) ) {
272+ const sorted = [ ...entries ] . sort ( ( a , b ) => ( a . date ?? '' ) . localeCompare ( b . date ?? '' ) ) ;
273+ const offset = window === AggregationWindow . OneDay ? 1 : 7 ;
274+ for ( let i = 0 ; i < sorted . length ; i ++ ) {
275+ const curr = sorted [ i ] ;
276+ const j = i - offset ;
277+
278+ // skip if previous date < start of data
279+ if ( j < 0 ) continue ;
280+ const prev = sorted [ j ] ;
281+ const diff = curr . value - prev . value ;
282+ out . push ( { ...curr , value : diff } ) ;
283+ }
284+ }
285+
286+ return out ;
287+ } ;
288+ } , [ ] ) ;
289+
290+ // Transform utility: apply window derivation (optionally) then relative normalization
291+ const transformInfectionData = useMemo ( ( ) => {
229292 return ( data : InfectionData | undefined ) : InfectionData => {
230- if ( ! data || ! relativeNumbers ) return data ?? [ ] ;
293+ if ( ! data ) return [ ] ;
294+ console . log ( 'transformInfectionData' , data , aggregationWindow ) ;
231295
232- return data . map ( ( entry ) => {
296+ const windowed = deriveWindowedSeries ( data , aggregationWindow ) ;
297+
298+ if ( ! relativeNumbers ) return windowed ?? [ ] ;
299+ console . log ( 'transformInfectionData window' , windowed ) ;
300+
301+ return windowed . map ( ( entry ) => {
233302 const nuts = entry . node ? nodeIdToNuts [ entry . node ] : undefined ;
234303 const pop = nuts ? props . baseData . populationByNuts [ nuts ] : undefined ;
235304 const validPop = typeof pop === 'number' && isFinite ( pop ) && pop > 0 ? pop : undefined ;
236305 const value = validPop ? ( entry . value / validPop ) * 100000 : entry . value ;
237306 return { ...entry , value} ;
238307 } ) ;
239308 } ;
240- } , [ nodeIdToNuts , props . baseData . populationByNuts , relativeNumbers ] ) ;
309+ } , [ aggregationWindow , deriveWindowedSeries , nodeIdToNuts , props . baseData . populationByNuts , relativeNumbers ] ) ;
241310
242- const normalizeMultiInfectionData = useMemo ( ( ) => {
311+ const transformMultiInfectionData = useMemo ( ( ) => {
243312 return ( multi : Record < string , InfectionData > | undefined ) : Record < string , InfectionData > => {
244- if ( ! multi || ! relativeNumbers ) return multi ?? { } ;
245-
313+ if ( ! multi ) return { } ;
246314 const result : Record < string , InfectionData > = { } ;
247315 Object . entries ( multi ) . forEach ( ( [ k , v ] ) => {
248- result [ k ] = normalizeInfectionData ( v ) ;
316+ result [ k ] = transformInfectionData ( v ) ;
249317 } ) ;
250318 return result ;
251319 } ;
252- } , [ normalizeInfectionData , relativeNumbers ] ) ;
320+ } , [ transformInfectionData ] ) ;
253321
254322 const contextValue : DataContextType = useMemo (
255323 ( ) => ( {
256324 ...props . baseData ,
257- mapData : normalizeInfectionData ( mapData ) ?? [ ] ,
258- lineChartData : normalizeMultiInfectionData ( lineChartData ) ?? { } ,
259- referenceDateValues : normalizeInfectionData ( referenceDateValues ) ?? [ ] ,
260- scenarioCardData : normalizeMultiInfectionData ( scenarioCardData ) ?? { } ,
325+ mapData : transformInfectionData ( mapData ) ?? [ ] ,
326+ lineChartData : transformMultiInfectionData ( lineChartData ) ?? { } ,
327+ referenceDateValues : transformInfectionData ( referenceDateValues ) ?? [ ] ,
328+ scenarioCardData : transformMultiInfectionData ( scenarioCardData ) ?? { } ,
261329 scenarioCardMetaData : scenarioCardMetaData ?? { } ,
262- groupFilterCardData : normalizeMultiInfectionData ( groupFilterCardData ) ?? { } ,
263- groupFilterLineChartData : normalizeInfectionData ( groupFilterLineChartData ) ?? [ ] ,
330+ groupFilterCardData : transformMultiInfectionData ( groupFilterCardData ) ?? { } ,
331+ groupFilterLineChartData : transformInfectionData ( groupFilterLineChartData ) ?? [ ] ,
264332 selectedScenarioData : selectedScenarioData ! ,
265333 selectedSimulationModel : selectedSimulationModel ! ,
266334 parameterDefinitions : parameterDefinitions ?? { } ,
@@ -277,8 +345,8 @@ export default function SelectedDataContext(props: {baseData: BaseData; children
277345 selectedScenarioData ,
278346 selectedSimulationModel ,
279347 parameterDefinitions ,
280- normalizeInfectionData ,
281- normalizeMultiInfectionData ,
348+ transformInfectionData ,
349+ transformMultiInfectionData ,
282350 ]
283351 ) ;
284352
0 commit comments