@@ -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
22622273function 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