'use strict'; // NEA's PM2.5 PSI specifications var PM25_PSI = [ { label: 'Good', loIndex: 0, hiIndex: 50, concLL: 0, concUL: 12 }, { label: 'Moderate', loIndex: 51, hiIndex: 100, concLL: 13, concUL: 55 }, { label: 'Unhealthy', loIndex: 101, hiIndex: 200, concLL: 56, concUL: 150 }, { label: 'Very Unhealthy', loIndex: 201, hiIndex: 300, concLL: 151, concUL: 250 }, { label: 'Hazardous', loIndex: 301, hiIndex: 400, concLL: 251, concUL: 350 }, { label: 'Hazardous', loIndex: 401, hiIndex: 500, concLL: 351, concUL: 500 } ], // PSI charting limits PSI_LIMITS = [ { label: 'Good (0-50)', showThreshold: Infinity, value: 0 }, { label: 'Moderate (51-100)', showThreshold: 25, value: 51 }, { label: 'Unhealthy (101-200)', showThreshold: 75, value: 101 }, { label: 'Very Unhealthy (201-300)', showThreshold: 175, value: 201 }, { label: 'Hazardous (301-500)', showThreshold: 275, value: 301 } ], // PM2.5 AQI specifications PM25_AQI = [ { label: 'Good', loIndex: 0, hiIndex: 50, concLL: 0, concUL: 12 }, { label: 'Moderate', loIndex: 51, hiIndex: 100, concLL: 12.1, concUL: 35.4 }, { label: 'Unhealthy for Sensitive Groups', loIndex: 101, hiIndex: 150, concLL: 35.5, concUL: 55.4 }, { label: 'Unhealthy', loIndex: 151, hiIndex: 200, concLL: 55.5, concUL: 150.4 }, { label: 'Very Unhealthy', loIndex: 201, hiIndex: 300, concLL: 150.5, concUL: 250.4 }, { label: 'Hazardous', loIndex: 301, hiIndex: 400, concLL: 250.5, concUL: 350.4 }, { label: 'Hazardous', loIndex: 401, hiIndex: 500, concLL: 350.5, concUL: 500.4 } ], // AQI charting limits AQI_LIMITS = [ { label: 'Good (<=12.0)', showThreshold: Infinity, value: 0 }, { label: 'Moderate (12.1-35.4)', showThreshold: 6, value: 12.1 }, { label: 'Unhealthy for Sensitive Groups (35.5-55.4)', showThreshold: 23, value: 35.5 }, { label: 'Unhealthy (55.5-150.4)', showThreshold: 45, value: 55.5 }, { label: 'Very Unhealthy (150.5-250.4)', showThreshold: 125, value: 150.5 }, { label: 'Hazardous (>= 250.5)', showThreshold: 200, value: 250.5 } ], MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], CHART_COLORS = { regions: { north : '#3366cc', south : '#dc3912', east : '#ff9900', west : '#109618', central: '#990099' }, aqi: [ 'green', '#ffde33', 'orange', 'red', 'purple', 'maroon'], psi: ['green', '#ffde33', 'red', 'purple', 'maroon'] }, charts = { concChart: null, concData: null, concOptions: null, psiChart: null, psiData: null, psiOptions: null }; // Object to keep a copy of the pollutant data. This reading should not be tampered with function pollutantData() { this.validRegions = ['north', 'south', 'east', 'west', 'central']; // for reference only this.regions = []; this.data = { time: [], north: [], south: [], east: [], west: [], central: [] }; this.stats = { north : { count: 0, sum: 0, average: 0, min: Infinity, max: -Infinity }, south : { count: 0, sum: 0, average: 0, min: Infinity, max: -Infinity }, east : { count: 0, sum: 0, average: 0, min: Infinity, max: -Infinity }, west : { count: 0, sum: 0, average: 0, min: Infinity, max: -Infinity }, central: { count: 0, sum: 0, average: 0, min: Infinity, max: -Infinity }, lastReadTime: null, min: Infinity, max: -Infinity }; } // function to calculate PSI function calcIndex(INDEX_SPEC, conc) { if(isNaN(conc)) { return NaN; } var i = -1, j; for(j = INDEX_SPEC.length - 1; j >= 0; j--) { if(INDEX_SPEC[j].concLL <= conc && conc <= INDEX_SPEC[j].concUL) { i = j; break; } } if(i != -1) { return Math.round( (INDEX_SPEC[i].hiIndex - INDEX_SPEC[i].loIndex) * (conc - INDEX_SPEC[i].concLL) / (INDEX_SPEC[i].concUL - INDEX_SPEC[i].concLL) + INDEX_SPEC[i].loIndex); } else { return Math.max(0, Math.round(conc)); //INDEX_SPEC[INDEX_SPEC.length - 1].hiIndex; } } function formatLabel(colName) { return colName.charAt(0).toUpperCase() + colName.substring(1).toLowerCase(); } function formatDate(dateObj) { return dateObj.getDate() + ' ' + MONTHS[dateObj.getMonth()] + ' ' + dateObj.getFullYear(); } function formatChartAxisDate(dateObj) { if(dateObj === null) { return ''; } var hrs = dateObj.getHours(); return dateObj.getDate() + '/' + (dateObj.getMonth() + 1) + (hrs < 10 ? ' 0' : ' ') + hrs + ':00'; } function formatChartTitleDate(dateObj) { if(dateObj === null) { return ''; } var hrs = dateObj.getHours(); return formatDate(dateObj) + ' ' + (hrs%12 === 0 ? 12 : hrs%12) + (hrs < 12 ? 'am' : 'pm'); } // function to pull data into our own object function extractGSData(gvizDataTable, dataObj) { var colLength = gvizDataTable.getNumberOfColumns(), rowLength = gvizDataTable.getNumberOfRows(), colName, cellValue, readTime, i, j; for(i = 0; i < colLength; i++) { colName = gvizDataTable.getColumnLabel(i).toLowerCase(); if(i !== 0) { dataObj.regions.push(colName); } for(j = 0; j < rowLength; j++) { cellValue = gvizDataTable.getValue(j, i); if(i === 0) { dataObj.data[colName][j] = cellValue; } else { dataObj.data[colName][j] = (cellValue === null ? NaN : cellValue); if(cellValue < dataObj.stats.min) { dataObj.stats.min = cellValue; } if(cellValue > dataObj.stats.max) { dataObj.stats.max = cellValue; } if(cellValue !== null) { dataObj.stats[colName].count++; dataObj.stats[colName].sum += cellValue; dataObj.stats[colName].average = dataObj.stats[colName].sum / dataObj.stats[colName].count; if(dataObj.stats[colName].min > cellValue) { dataObj.stats[colName].min = cellValue; } if(cellValue > dataObj.stats[colName].max) { dataObj.stats[colName].max = cellValue; } readTime = gvizDataTable.getValue(j, 0); if(dataObj.stats.lastReadTime === null) { dataObj.stats.lastReadTime = readTime; } else if(dataObj.stats.lastReadTime < readTime){ dataObj.stats.lastReadTime = readTime; } } } } } } function drawConcChart(dataObj) { charts.concData = new google.visualization.DataTable(); var rCnt = dataObj.data['time'].length, seriesOptions = [], vAxisMax = -1, rowData, r, i, cIdx, rIdx; // Handle the PM2.5 readings charts.concData.addColumn('string', 'Time'); for(r = 0; r < dataObj.regions.length; r++) { charts.concData.addColumn('number', formatLabel(dataObj.regions[r])); charts.concData.addColumn({ type: 'string', role: 'tooltip', p: { 'html': true } }); } for(i = 0; i < rCnt; i++) { rowData = [formatChartAxisDate(dataObj.data['time'][i])]; for(r = 0; r < dataObj.regions.length; r++) { rowData.push(dataObj.data[dataObj.regions[r]][i]); rowData.push( '
An error has occured while retrieving data, please refresh and try again
'); $('#1hrconc_div').html('An error has occured while retrieving data, please refresh and try again
'); $('#datadiv').html('An error has occured while retrieving data, please refresh and try again
'); } function refreshChart(cnt) { cnt = (typeof cnt === 'number' ? cnt : 1); if(cnt > 3) { showAjaxError(); return; } if(cnt === 1) { $('#1hrpsi_ymin').spinner('value', null); $('#1hrpsi_ymax').spinner('value', null); $('#1hrconc_ymin').spinner('value', null); $('#1hrconc_ymax').spinner('value', null); $('#1hrpsi_div').html(''); $('#1hrconc_div').html(''); $('#datadiv').html(''); } var sDate = $("#start_datepicker").datepicker('getDate'), eDate = new Date($("#end_datepicker").datepicker('getDate').getTime() + 24 * 60 * 60 * 1000), sDateStr = sDate.getFullYear() + '-' + (sDate.getMonth() < 10 ? '0' : '') + (sDate.getMonth() + 1) + '-' + (sDate.getDate() < 10 ? '0' : '') + sDate.getDate() + '%2000%3A00%3A00.000', eDateStr = eDate.getFullYear() + '-' + (eDate.getMonth() < 10 ? '0' : '') + (eDate.getMonth() + 1) + '-' + (eDate.getDate() < 10 ? '0' : '') + eDate.getDate() + '%2000%3A00%3A00.000', selectedRegions = '', neaData = new pollutantData(), query, dTable; if($('#north_checkbox').is(':checked')) { selectedRegions += '%2C%20B'; } if($('#south_checkbox').is(':checked')) { selectedRegions += '%2C%20C'; } if($('#east_checkbox').is(':checked')) { selectedRegions += '%2C%20D'; } if($('#west_checkbox').is(':checked')) { selectedRegions += '%2C%20E'; } if($('#central_checkbox').is(':checked')) { selectedRegions += '%2C%20F'; } query = new google.visualization.Query('https://docs.google.com/a/google.com/spreadsheets/d/1rQYcYLuXWheFn2mDDGipS2_qedYE2HuOb1-ePf4KKwc/gviz/tq?tq=select%20A' + selectedRegions + '%20where%20A%20%3E%20datetime%20%22' + sDateStr + '%22%20and%20A%20%3C%3D%20datetime%20%22' + eDateStr + '%22'); query.send(function(response) { if(response.isError()) { showAjaxError(); alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage()); return; } dTable = response.getDataTable(); extractGSData(dTable, neaData); drawConcChart(neaData); drawPsiChart(neaData); fillDataTable(neaData); }); } $(document).ready(function() { var multidayCheckbox = $('#multiday_checkbox'), startDatepicker = $('#start_datepicker'), endDatepicker = $('#end_datepicker'), messageSpan = $('#message_span'), defaultRange = 6*24*60*60*1000, startDate; multidayCheckbox.change(function(){ endDatepicker.datepicker('setDate', startDatepicker.datepicker('getDate')); $('#enddate_span').toggle('fade'); if(messageSpan.is(':visible')) { messageSpan.toggle(); } }); startDatepicker .datepicker({ dateFormat: 'dd-mm-yy', minDate: '01-10-2014', maxDate: 0 }) .datepicker('setDate', new Date()) .change(function() { endDatepicker.datepicker('option', 'minDate', startDatepicker.datepicker('getDate')); if(!multidayCheckbox.is(':checked')) { endDatepicker.datepicker('setDate', new Date(startDatepicker.datepicker('getDate').getTime())); } if(endDatepicker.datepicker('getDate').getTime() - startDatepicker.datepicker('getDate').getTime() > defaultRange) { endDatepicker.datepicker('setDate', new Date(startDatepicker.datepicker('getDate').getTime() + defaultRange)); if(multidayCheckbox.is(':checked')) { $('#message_body').text('End date default to one week. If intentional, please reselect the end date.'); if(messageSpan.is(':visible')) { messageSpan.effect('pulsate', { times: 2 }, 1200); } else { messageSpan.toggle('pulsate', { times: 2 }, 1200); } } } refreshChart(); }); startDate = startDatepicker.datepicker('getDate'); if((new Date()).getHours() <= 6) { multidayCheckbox.click(); startDatepicker.datepicker('setDate', new Date(startDate.getTime() - 24 * 60 * 60 * 1000)); $('#message_body').text('Sparse data points, including previous day\'s reading for better chart.'); messageSpan.toggle('pulsate', { times: 2 }, 1200); } endDatepicker .datepicker({ dateFormat: 'dd-mm-yy', minDate: 0, maxDate: 0 }) .datepicker('setDate', new Date()) .focus(function(){ if(messageSpan.is(':visible')){ messageSpan.toggle(); }}) .change(refreshChart); $('#refresh').click(refreshChart); $('#1hrconc_ymin') .spinner({ min: 0, max: 500, step: 10 }) .on('spinchange, spinstop', function(event, ui) { charts.concOptions.vAxis.viewWindow.min = $('#1hrconc_ymin').spinner('value'); charts.concChart.draw(charts.concData, charts.concOptions); }); $('#1hrconc_ymax') .spinner({ min: 10, max: 500, step: 10 }) .on('spinchange, spinstop', function(event, ui) { charts.concOptions.vAxis.viewWindow.max = $('#1hrconc_ymax').spinner('value'); charts.concChart.draw(charts.concData, charts.concOptions); }); $('#1hrpsi_ymin') .spinner({ min: 0, max: 500, step: 10 }) .on('spinchange, spinstop', function(event, ui) { charts.psiOptions.vAxis.viewWindow.min = $('#1hrpsi_ymin').spinner('value'); charts.psiChart.draw(charts.psiData, charts.psiOptions); }); $('#1hrpsi_ymax') .spinner({ min: 10, max: 500, step: 10 }) .on('spinchange, spinstop', function(event, ui) { charts.psiOptions.vAxis.viewWindow.max = $('#1hrpsi_ymax').spinner('value'); charts.psiChart.draw(charts.psiData, charts.psiOptions); }); //refreshChart(); google.charts.setOnLoadCallback(refreshChart); });