|
13 | 13 | MULTI_COLORS, |
14 | 14 | genDateHighlight, |
15 | 15 | genAnnotationLayer, |
| 16 | + generateCumulativeBarSpec, |
16 | 17 | } from '../specs/lineSpec'; |
17 | 18 | import { toTimeValue } from '../data/utils'; |
18 | 19 | import Toggle from '../components/Toggle.svelte'; |
|
41 | 42 | */ |
42 | 43 | export let sensor; |
43 | 44 | /** |
44 | | - * @type {import("../../stores/params").DataFetcher} |
| 45 | + * @type {import("../../stores/DataFetcher").DataFetcher} |
45 | 46 | */ |
46 | 47 | export let fetcher; |
47 | 48 |
|
|
86 | 87 | }; |
87 | 88 |
|
88 | 89 | $: highlightDate = date.value; |
89 | | - $: timeFrame = showFull && expandableWindow ? date.sensorTimeFrame : date.windowTimeFrame; |
| 90 | + $: timeFrame = showFull && expandableWindow ? sensor.timeFrame : date.windowTimeFrame; |
90 | 91 |
|
91 | 92 | /** |
92 | 93 | * @param {import('../../stores/params').SensorParam} sensor |
93 | 94 | * @param {import('../../stores/params').RegionParam} region |
94 | 95 | * @param {import('../../stores/params').DateParam} date |
95 | 96 | * @param {import('../../stores/params').TimeFrame} timeFrame |
96 | | - * @param {{height: number, zero: boolean, singleRaw: boolean, isMobile: boolean, singleRegionOnly: boolean}} options |
| 97 | + * @param {{height: number, zero: boolean, raw: boolean, isMobile: boolean, singleRegionOnly: boolean, cumulative: boolean}} options |
97 | 98 | */ |
98 | | - function genSpec(sensor, region, date, timeFrame, { height, zero, singleRaw, isMobile, singleRegionOnly, domain }) { |
| 99 | + function genSpec( |
| 100 | + sensor, |
| 101 | + region, |
| 102 | + date, |
| 103 | + timeFrame, |
| 104 | + { height, zero, raw, isMobile, singleRegionOnly, domain, cumulative }, |
| 105 | + ) { |
99 | 106 | const options = { |
100 | 107 | initialDate: highlightDate || date.value, |
101 | 108 | height, |
102 | 109 | color, |
103 | 110 | domain: domain || timeFrame.domain, |
104 | 111 | zero, |
105 | 112 | xTitle: sensor.xAxis, |
106 | | - title: joinTitle([sensor.name, `in ${region.displayName}`], isMobile), |
| 113 | + title: joinTitle([(cumulative ? 'Cumulative ' : '') + sensor.name, `in ${region.displayName}`], isMobile), |
107 | 114 | subTitle: sensor.unit, |
108 | 115 | highlightRegion: true, |
109 | 116 | }; |
110 | | - if (singleRaw) { |
| 117 | + if (cumulative) { |
| 118 | + options.paddingLeft = 52; // more space for larger numbers |
| 119 | + } |
| 120 | + if (raw) { |
| 121 | + if (cumulative) { |
| 122 | + return generateCumulativeBarSpec(options); |
| 123 | + } |
111 | 124 | return generateLineAndBarSpec(options); |
112 | 125 | } |
113 | 126 | if (singleRegionOnly) { |
|
167 | 180 | * @param {import("../../stores/params").DateParam} date |
168 | 181 | * @param {import("../../stores/params").RegionParam} region |
169 | 182 | */ |
170 | | - function loadSingleData(sensor, region, timeFrame) { |
| 183 | + function loadSingleData(sensor, region, timeFrame, { cumulative }) { |
171 | 184 | if (!region.value) { |
172 | 185 | return null; |
173 | 186 | } |
174 | | - const selfData = fetcher.fetch1Sensor1RegionNDates(sensor, region, timeFrame); |
| 187 | +
|
| 188 | + const selfData = fetcher.fetch1Sensor1RegionNDates(sensor.value, region, timeFrame); |
175 | 189 | const rawData = fetcher.fetch1Sensor1RegionNDates(sensor.rawValue, region, timeFrame); |
176 | 190 |
|
177 | | - return Promise.all([selfData, rawData]).then((data) => { |
178 | | - return combineSignals(data, data[0], ['smoothed', 'raw']); |
179 | | - }); |
| 191 | + if (cumulative) { |
| 192 | + // raw and cumulative |
| 193 | + const cumulativeData = fetcher.fetch1Sensor1RegionNDates(sensor.rawCumulativeValue, region, timeFrame); |
| 194 | + return Promise.all([selfData, rawData, cumulativeData]).then((data) => { |
| 195 | + return combineSignals( |
| 196 | + data, |
| 197 | + data[0].map((d) => ({ ...d })), |
| 198 | + ['smoothed', 'raw', 'cumulative'], |
| 199 | + ); |
| 200 | + }); |
| 201 | + } else { |
| 202 | + return Promise.all([selfData, rawData]).then((data) => { |
| 203 | + return combineSignals( |
| 204 | + data, |
| 205 | + data[0].map((d) => ({ ...d })), |
| 206 | + ['smoothed', 'raw'], |
| 207 | + ); |
| 208 | + }); |
| 209 | + } |
180 | 210 | } |
181 | 211 |
|
182 | 212 | function onSignal(event) { |
|
207 | 237 | * @param {import("../../stores/params").SensorParam} sensor |
208 | 238 | * @param {import("../../stores/params").Region[]} region |
209 | 239 | */ |
210 | | - function generateFileName(sensor, regions, timeFrame, raw) { |
| 240 | + function generateFileName(sensor, regions, timeFrame, raw, cumulative) { |
211 | 241 | const regionName = regions.map((region) => `${region.propertyId}-${region.displayName}`).join(','); |
212 | 242 | let suffix = ''; |
213 | 243 | if (raw) { |
214 | 244 | suffix = '_RawVsSmoothed'; |
215 | 245 | } |
| 246 | + if (cumulative) { |
| 247 | + suffix += '_Cumulative'; |
| 248 | + } |
216 | 249 | return `${sensor.name}_${regionName}_${formatDateISO(timeFrame.min)}-${formatDateISO(timeFrame.max)}${suffix}`; |
217 | 250 | } |
218 | 251 |
|
|
231 | 264 |
|
232 | 265 | let zoom = false; |
233 | 266 | let singleRaw = false; |
| 267 | + let singleCumulative = false; |
234 | 268 |
|
| 269 | + $: raw = singleRaw && sensor.rawValue != null; |
| 270 | + $: cumulative = raw && singleCumulative && sensor.rawCumulativeValue != null; |
235 | 271 | $: regions = raw ? [region.value] : resolveRegions(region.value, singleRegionOnly); |
236 | 272 | $: annotations = $annotationManager.getWindowAnnotations(sensor.value, regions, timeFrame.min, timeFrame.max); |
237 | | - $: raw = singleRaw && sensor.rawValue != null; |
238 | 273 | $: spec = injectRanges( |
239 | 274 | genSpec(sensor, region, date, timeFrame, { |
240 | 275 | height, |
241 | 276 | zero: !zoom, |
242 | | - singleRaw: raw, |
| 277 | + raw, |
243 | 278 | isMobile: $isMobileDevice, |
244 | 279 | singleRegionOnly, |
245 | 280 | domain, |
| 281 | + cumulative, |
246 | 282 | }), |
247 | 283 | timeFrame, |
248 | 284 | annotations, |
249 | 285 | ); |
250 | | - $: data = raw ? loadSingleData(sensor, region, timeFrame) : loadData(sensor, region, timeFrame, singleRegionOnly); |
251 | | - $: fileName = generateFileName(sensor, regions, timeFrame, raw); |
| 286 | + $: data = raw |
| 287 | + ? loadSingleData(sensor, region, timeFrame, { cumulative }) |
| 288 | + : loadData(sensor, region, timeFrame, singleRegionOnly); |
| 289 | + $: fileName = generateFileName(sensor, regions, timeFrame, raw, cumulative); |
252 | 290 |
|
253 | 291 | function findValue(region, data, date, prop = 'value') { |
254 | 292 | if (!date) { |
|
292 | 330 | <Toggle bind:checked={zoom}>Rescale Y-axis</Toggle> |
293 | 331 | {#if sensor.rawValue != null} |
294 | 332 | <Toggle bind:checked={singleRaw}>Raw Data</Toggle> |
| 333 | + {#if raw && sensor.rawCumulativeValue != null} |
| 334 | + <Toggle bind:checked={singleCumulative}>Cumulative Data</Toggle> |
| 335 | + {/if} |
295 | 336 | {/if} |
296 | 337 | {#if expandableWindow} |
297 | 338 | <Toggle bind:checked={showFull}>Show All Dates</Toggle> |
298 | 339 | {/if} |
299 | 340 | <div class="spacer" /> |
300 | | - <DownloadMenu {fileName} {vegaRef} {data} {sensor} {raw} /> |
| 341 | + <DownloadMenu {fileName} {vegaRef} {data} {sensor} {raw} {cumulative} /> |
301 | 342 | </div> |
302 | 343 |
|
303 | | -<div class="{!(singleRaw && sensor.rawValue != null) && regions.length > 1 ? 'mobile-two-col' : ''} legend"> |
| 344 | +<div class="{!raw && regions.length > 1 ? 'mobile-two-col' : ''} legend"> |
304 | 345 | {#each regions as r, i} |
305 | 346 | <div |
306 | 347 | class="legend-elem" |
|
324 | 365 | {#await data then d} |
325 | 366 | <span class="legend-value"> |
326 | 367 | <SensorValue {sensor} value={findValue(r, d, highlightDate)} medium /> |
327 | | - {#if singleRaw && sensor.rawValue != null} |
| 368 | + {#if raw} |
328 | 369 | (raw: |
329 | | - <SensorValue {sensor} value={findValue(r, d, highlightDate, 'raw')} medium />) |
| 370 | + {#if cumulative} |
| 371 | + <SensorValue {sensor} value={findValue(r, d, highlightDate, 'raw')} medium />, cumulative: |
| 372 | + <SensorValue {sensor} value={findValue(r, d, highlightDate, 'cumulative')} medium />) |
| 373 | + {:else} |
| 374 | + <SensorValue {sensor} value={findValue(r, d, highlightDate, 'raw')} medium />) |
| 375 | + {/if} |
330 | 376 | {/if} |
331 | 377 | </span> |
332 | 378 | {/await} |
|
0 commit comments