@@ -27,11 +27,11 @@ export interface AggregateOptions {
2727 baseTime : Date ;
2828}
2929
30- function leadHoursAt ( times : string [ ] , i : number , baseTime : Date ) : number {
30+ function leadHoursAt ( time : string , baseTime : Date ) : number {
3131 // open-meteo returns timezone-shifted ISO strings without a TZ marker.
3232 // Both the times array and our cursor are in the same local frame, so a
3333 // direct millisecond diff is correct.
34- const t = new Date ( times [ i ] ) . getTime ( ) ;
34+ const t = new Date ( time ) . getTime ( ) ;
3535 return Math . max ( 0 , ( t - baseTime . getTime ( ) ) / 3_600_000 ) ;
3636}
3737
@@ -53,11 +53,15 @@ function weightedMean(perModel: ModelSamples, weights: Map<string, number>): { m
5353 let varSum = 0 ;
5454 for ( const id in used ) {
5555 const v = perModel [ id ] ;
56- if ( v == null ) continue ;
57- varSum += ( used [ id ] / totalW ) * ( v - mean ) ** 2 ;
56+ const w = used [ id ] ;
57+ if ( v == null || w === undefined ) continue ;
58+ varSum += ( w / totalW ) * ( v - mean ) ** 2 ;
5859 }
5960 // Renormalize so the exposed weights sum to 1 even after dropping nulls.
60- for ( const id in used ) used [ id ] = used [ id ] / totalW ;
61+ for ( const id in used ) {
62+ const w = used [ id ] ;
63+ if ( w !== undefined ) used [ id ] = w / totalW ;
64+ }
6165 return { mean, stdDev : Math . sqrt ( varSum ) , effectiveWeights : used } ;
6266}
6367
@@ -88,7 +92,10 @@ function weightedCircularMean(perModel: ModelSamples, weights: Map<string, numbe
8892 const R = Math . min ( 1 , Math . sqrt ( mx * mx + my * my ) ) ;
8993 // Circular standard deviation in radians: sqrt(-2 * ln(R)). Convert to degrees.
9094 const stdDev = R > 0 ? ( Math . sqrt ( - 2 * Math . log ( R ) ) * 180 ) / Math . PI : 180 ;
91- for ( const id in used ) used [ id ] = used [ id ] / totalW ;
95+ for ( const id in used ) {
96+ const w = used [ id ] ;
97+ if ( w !== undefined ) used [ id ] = w / totalW ;
98+ }
9299 return { mean, stdDev, effectiveWeights : used } ;
93100}
94101
@@ -125,7 +132,10 @@ function severityWeightedMode(perModel: ModelSamples, weights: Map<string, numbe
125132 bestCodeW = w ;
126133 }
127134 }
128- for ( const id in used ) used [ id ] = used [ id ] / totalW ;
135+ for ( const id in used ) {
136+ const w = used [ id ] ;
137+ if ( w !== undefined ) used [ id ] = w / totalW ;
138+ }
129139 return { code : bestCode , effectiveWeights : used } ;
130140}
131141
@@ -135,7 +145,9 @@ export function aggregateSeries(times: string[], series: Record<string, (number
135145 const { variable, models, lat, lon, baseTime } = opts ;
136146 const result : AggregatePoint [ ] = [ ] ;
137147 for ( let i = 0 ; i < times . length ; i ++ ) {
138- const leadH = leadHoursAt ( times , i , baseTime ) ;
148+ const timeStr = times [ i ] ;
149+ if ( timeStr === undefined ) continue ;
150+ const leadH = leadHoursAt ( timeStr , baseTime ) ;
139151 const weights = normalizedWeights ( models , leadH , lat , lon , variable ) ;
140152 const perModel : ModelSamples = { } ;
141153 for ( const m of models ) {
@@ -150,7 +162,7 @@ export function aggregateSeries(times: string[], series: Record<string, (number
150162 }
151163 const { code, effectiveWeights } = severityWeightedMode ( perModel , weights ) ;
152164 result . push ( {
153- time : times [ i ] ,
165+ time : timeStr ,
154166 value : code ,
155167 stdDev : 0 ,
156168 weights : effectiveWeights ,
@@ -159,7 +171,7 @@ export function aggregateSeries(times: string[], series: Record<string, (number
159171 } else if ( variable === "wind_direction_10m" ) {
160172 const { mean, stdDev, effectiveWeights } = weightedCircularMean ( perModel , weights ) ;
161173 result . push ( {
162- time : times [ i ] ,
174+ time : timeStr ,
163175 value : mean ,
164176 stdDev,
165177 weights : effectiveWeights ,
@@ -168,7 +180,7 @@ export function aggregateSeries(times: string[], series: Record<string, (number
168180 } else {
169181 const { mean, stdDev, effectiveWeights } = weightedMean ( perModel , weights ) ;
170182 result . push ( {
171- time : times [ i ] ,
183+ time : timeStr ,
172184 value : mean ,
173185 stdDev,
174186 weights : effectiveWeights ,
0 commit comments