Skip to content

Commit 3da8de4

Browse files
Update local_forecasts.js
1 parent cd70420 commit 3da8de4

File tree

1 file changed

+79
-55
lines changed

1 file changed

+79
-55
lines changed

local_forecasts.js

Lines changed: 79 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -772,13 +772,11 @@ function readCompressedJsonAndAddBanners(fileUrl, selectedSpecies) {
772772
const forecastEnd = new Date(forecastStart.getTime() + 3 * 60 * 60 * 1000);
773773
// Current site-local time
774774
const nowLocal = new Date(now.toLocaleString("en-US", { timeZone: site.timezone }));
775-
// Check if nowLocal is within [forecastStart, forecastEnd)
776775
return nowLocal >= forecastStart && nowLocal < forecastEnd;
777776
});
778777

779778

780779
} else {
781-
// For other species, match exact hour
782780
const currentLocalStr = `${localYear}-${localMonth}-${localDate} ${pad(localHour)}`;
783781
matchingForecast = (site.forecasts || []).find(forecast => {
784782
if (!forecast.local_time) return false;
@@ -1138,7 +1136,8 @@ function readApiBaker(options = {}) {
11381136
{ column: "master_predicted_aqi", name: "Corrected AQI", color: "blue", width: 2 }
11391137
],
11401138
displayAQI: true,
1141-
displayMetrics: true
1139+
displayMetrics: true,
1140+
enableAqiColors: true
11421141
},
11431142
{
11441143
id: "plot_pm25_aqi",
@@ -1155,7 +1154,8 @@ function readApiBaker(options = {}) {
11551154
{ column: "master_pm25_aqi", name: "PM2.5 AQI", color: "green", width: 2 }
11561155
],
11571156
displayAQI: true,
1158-
displayMetrics: true
1157+
displayMetrics: true,
1158+
enableAqiColors: true
11591159
},
11601160
// Supporting concentration plots
11611161
{
@@ -1171,7 +1171,8 @@ function readApiBaker(options = {}) {
11711171
{ column: "master_predicted", name: "Corrected", color: "blue", width: 2 }
11721172
],
11731173
displayAQI: false,
1174-
displayMetrics: false
1174+
displayMetrics: false,
1175+
enableAqiColors: false
11751176
},
11761177
{
11771178
id: "plot_pm25_conc",
@@ -1186,7 +1187,8 @@ function readApiBaker(options = {}) {
11861187
{ column: "master_pm25", name: "PM2.5", color: "green", width: 2 }
11871188
],
11881189
displayAQI: false,
1189-
displayMetrics: false
1190+
displayMetrics: false,
1191+
enableAqiColors: false
11901192
},
11911193
{
11921194
id: "plot_o3_conc",
@@ -1201,9 +1203,10 @@ function readApiBaker(options = {}) {
12011203
{ column: "master_o3", name: "O3", color: "orange", width: 2 }
12021204
],
12031205
displayAQI: false,
1204-
displayMetrics: false
1206+
displayMetrics: false,
1207+
enableAqiColors: false
12051208
},
1206-
// Pandora obs plot (unchanged)
1209+
// Pandora obs plot
12071210
{
12081211
id: "plot_pandora",
12091212
title: "Pandora NO<sub>2</sub> Observations",
@@ -1217,7 +1220,8 @@ function readApiBaker(options = {}) {
12171220
{ column: "master_observation", name: "Pandora", color: "black", width: 2 }
12181221
],
12191222
displayAQI: false,
1220-
displayMetrics: false
1223+
displayMetrics: false,
1224+
enableAqiColors: false
12211225
},
12221226
{
12231227
id: "plot_no2_model",
@@ -1232,17 +1236,16 @@ function readApiBaker(options = {}) {
12321236
{ column: "master_no2", name: "NO2", color: "red", width: 2 }
12331237
],
12341238
displayAQI: false,
1235-
displayMetrics: false
1239+
displayMetrics: false,
1240+
enableAqiColors: false
12361241
}
12371242
];
12381243

12391244
const tabMap = {};
12401245
plots.forEach((plot, index) => {
1241-
// Check if data for this plot is valid and not empty
12421246
const colKey = plot.columns[0]?.column;
12431247
const dataArr = plot.data && colKey && Array.isArray(plot.data[colKey]) ? plot.data[colKey] : [];
12441248
if (!dataArr.length) {
1245-
// Skip this plot if no data
12461249
return;
12471250
}
12481251

@@ -1283,29 +1286,28 @@ function readApiBaker(options = {}) {
12831286
let currentValue = 'N/A';
12841287
let nextValue = 'N/A';
12851288

1286-
// ...inside readApiBaker, after defining currentValue and nextValue for each plot...
12871289

12881290
if (plot.param === "pm25") {
1289-
// For PM2.5, use 3-hour window logic for current and next values
1291+
12901292
currentValue = 'N/A';
12911293
nextValue = 'N/A';
12921294
for (let i = 0; i < masterData.master_datetime.length; i++) {
12931295
const dtStr = masterData.master_datetime[i];
12941296
if (!dtStr) continue;
12951297
const forecastStart = new Date(dtStr.replace(' ', 'T'));
12961298
const forecastEnd = new Date(forecastStart.getTime() + 3 * 60 * 60 * 1000);
1297-
// Current: is now within this 3-hour window?
1299+
12981300
if (siteLocalNow >= forecastStart && siteLocalNow < forecastEnd) {
12991301
currentValue = masterData[plot.columns[0].column][i];
13001302
}
1301-
// Next: is 1 hour from now within this 3-hour window?
1303+
13021304
const nextLocalDate = new Date(siteLocalNow.getTime() + 1 * 60 * 60 * 1000);
13031305
if (nextLocalDate >= forecastStart && nextLocalDate < forecastEnd) {
13041306
nextValue = masterData[plot.columns[0].column][i];
13051307
}
13061308
}
13071309
} else {
1308-
// For other species, match by hour
1310+
13091311
currentValue = 'N/A';
13101312
nextValue = 'N/A';
13111313
for (let i = 0; i < masterData.master_datetime.length; i++) {
@@ -1321,7 +1323,6 @@ function readApiBaker(options = {}) {
13211323
}
13221324
}
13231325

1324-
// Remove the extra for-loop that matches by hour for all species (it would overwrite the correct 3-hour logic for PM2.5)
13251326

13261327
if (plot.displayAQI) {
13271328
const currentAqi = currentValue;
@@ -1446,7 +1447,8 @@ function readApiBaker(options = {}) {
14461447
false, // enableFading
14471448
"", // text
14481449
"bar", // plotType
1449-
timezone // timezone
1450+
timezone,
1451+
plot.enableAqiColors
14501452
);
14511453
} else {
14521454
console.error(`No DOM element with id '${plot.id}' exists on the page.`);
@@ -2257,7 +2259,16 @@ function cleanAndSortData(datetime_data, combined_dataset) {
22572259

22582260
return cleanedData;
22592261
}
2260-
2262+
function getAqiBarColor(aqiValue, pollutant, alpha = 1) {
2263+
const aqiLevel = getAqiLevel(aqiValue, pollutant);
2264+
if (!aqiLevel.color) return `rgba(128,128,128,${alpha})`;
2265+
const hex = aqiLevel.color.replace('#', '');
2266+
const bigint = parseInt(hex, 16);
2267+
const r = (bigint >> 16) & 255;
2268+
const g = (bigint >> 8) & 255;
2269+
const b = bigint & 255;
2270+
return `rgba(${r},${g},${b},${alpha})`;
2271+
}
22612272

22622273
function validateData(data, requiredKeys = [], minLength = 1) {
22632274

@@ -2286,7 +2297,8 @@ function draw_plot(
22862297
enableFading = false,
22872298
text = "Forecasts",
22882299
plotType = "scatter",
2289-
timezone = "UTC"
2300+
timezone = "UTC",
2301+
enableAqiColors = false
22902302
) {
22912303

22922304
const datetime_data = combined_dataset["master_datetime"];
@@ -2305,40 +2317,52 @@ function draw_plot(
23052317
const lastIndex = cleanedData.master_datetime.length - 1;
23062318
let currentX = null;
23072319
let currentY = null;
2308-
2309-
2310-
const traces = plot_columns
2311-
.filter(column => column && column.name && column.column)
2312-
.map(({ column, name, color, width, dash }, index) => {
2313-
const lineColor = color || 'rgba(7, 23, 16, 0.65)';
2314-
const rgbaMatch = lineColor.match(/\d+/g);
2315-
const fadingColor = rgbaMatch
2316-
? `rgba(${rgbaMatch[0]}, ${rgbaMatch[1]}, ${rgbaMatch[2]}, 0.6)`
2317-
: 'rgba(0, 0, 0, 0.6)';
2318-
2319-
const barColors = cleanedData.master_datetime.map((datetime) => {
2320+
2321+
const traces = plot_columns
2322+
.filter(column => column && column.name && column.column)
2323+
.map(({ column, name, color, width, dash }, index) => {
2324+
let barColors;
2325+
if (plotType === "bar" && enableAqiColors) {
2326+
barColors = cleanedData.master_datetime.map((datetime, i) => {
2327+
const value = cleanedData[column][i];
2328+
const dataTime = new Date(datetime);
2329+
const alpha = dataTime <= localNow ? 1 : 0.5;
2330+
return getAqiBarColor(value, param, alpha);
2331+
});
2332+
} else {
2333+
barColors = cleanedData.master_datetime.map((datetime) => {
23202334
const dataTime = new Date(datetime);
23212335
return dataTime < localNow ? '#2196f3' : '#2196f3c2';
23222336
});
2337+
}
2338+
2339+
const lineColor = color || 'rgba(7, 23, 16, 0.65)';
2340+
const rgbaMatch = lineColor.match(/\d+/g);
2341+
const fadingColor = rgbaMatch
2342+
? `rgba(${rgbaMatch[0]}, ${rgbaMatch[1]}, ${rgbaMatch[2]}, 0.6)`
2343+
: 'rgba(0, 0, 0, 0.6)';
2344+
2345+
return {
2346+
type: plotType === "bar" ? "bar" : "scatter",
2347+
mode: plotType === "bar" ? undefined : "lines",
2348+
connectgaps: plotType === "bar" ? undefined : false,
2349+
x: cleanedData.master_datetime,
2350+
y: cleanedData[column],
2351+
line: plotType === "bar" ? undefined : {
2352+
color: enableAqiColors ? barColors[0] : lineColor,
2353+
width: width || 1,
2354+
dash: dash || 'solid'
2355+
},
2356+
marker: plotType === "bar"
2357+
? { color: barColors }
2358+
: undefined,
2359+
fill: plotType === "bar" ? undefined : enableFading && index === 0 ? 'tozeroy' : 'none',
2360+
fillcolor: plotType === "bar" ? undefined : enableFading && index === 0 ? fadingColor : 'none',
2361+
hoverinfo: 'x+y',
2362+
name: name
2363+
};
2364+
});
23232365

2324-
return {
2325-
type: plotType === "bar" ? "bar" : "scatter",
2326-
mode: plotType === "bar" ? undefined : "lines",
2327-
connectgaps: plotType === "bar" ? undefined : false,
2328-
x: cleanedData.master_datetime,
2329-
y: cleanedData[column],
2330-
line: plotType === "bar" ? undefined : {
2331-
color: lineColor,
2332-
width: width || 1,
2333-
dash: dash || 'solid'
2334-
},
2335-
marker: plotType === "bar" ? { color: barColors } : undefined,
2336-
fill: plotType === "bar" ? undefined : enableFading && index === 0 ? 'tozeroy' : 'none',
2337-
fillcolor: plotType === "bar" ? undefined : enableFading && index === 0 ? fadingColor : 'none',
2338-
hoverinfo: 'x+y',
2339-
name: name
2340-
};
2341-
});
23422366

23432367
for (let i = 0; i < cleanedData.master_datetime.length; i++) {
23442368
const datetime = new Date(cleanedData.master_datetime[i]);
@@ -2354,10 +2378,10 @@ function draw_plot(
23542378

23552379
const layout = {
23562380
margin: {
2357-
l: 0, // left
2358-
r: 0, // right
2359-
t: 0, // top
2360-
b: 20, // bottom
2381+
l: 0,
2382+
r: 0,
2383+
t: 0,
2384+
b: 20,
23612385
pad: 0
23622386
},
23632387
annotations: [

0 commit comments

Comments
 (0)