/* Authors: Tristan Wright, July 2012, Lillian Gray 2017-2020 This reads the json formatted data generated by data_gen.py the data is piped into the Google Charts API and a graph with controls are made If more buildings are added increment buildingNum and add a new color to buildingColors Benchmark functions or sections by wrapping them in: console.log(var a = new Date()-0) console.log(var b = new Date()-0 - a) */ //init globals //chart data source is global so we don't have to reload every time. //variables for Google Charts API chart components var lineChartData var dashboard var controlWrapper var chartWrapper const buildingNum = 8 var whichBuildings = new Array() for(i=0; i < buildingNum; i++){ whichBuildings[i] = true } //change colors here var buildingColors = new Array( '#9c1c34', '#0A22af', '#ff381a','#00ff00', '#009944','#50D8F1','#e9af32', '#E07628') //variables for range of control under chart var endDate var startDate //split url on '?' for permalink creation and parsing var URLComponents = document.URL.split('?') /*called when GoogleAPI finishes loading in index.html calls getData and then passes the data into initializeLineChart when finished*/ function onAPILoad(){ getData().done(initializeLineChart) } //fetches data for chart from text file generated by data_gen.py function getData(){ return $.ajax({ url: "raw_line_chart.txt", //location of data file dataType: "json", }) } /*creates chart with initial options options that change such as visible columns are declared in drawLineChart*/ function initializeLineChart(data){ lineChartData = new google.visualization.DataView(new google.visualization.DataTable(data)) dashboard = new google.visualization.Dashboard(document.getElementById('dashboard')) var latestEntryDate = new Date(lineChartData.getValue(lineChartData.getNumberOfRows()-1, 0)) endDate = new Date(latestEntryDate) startDate = new Date(latestEntryDate.setMonth(latestEntryDate.getMonth() - 1)) parsePermalink() //settings for control bar below chart controlWrapper = new google.visualization.ControlWrapper({ controlType: 'ChartRangeFilter', containerId: 'control', options: { //filter by the date axis. filterColumnIndex: 0, ui: { chartType: 'LineChart', chartOptions: { chartArea: {width: '100%'}, colors: buildingColors.slice(1), hAxis: {baselineColor: 'none'} }, //1 day in milliseconds = 24 * 60 * 60 * 1000 = 86,400,000 minRangeSize: 86400000 }//close ui },//close options state: {range: {start: startDate, end: endDate}} }) //chart settings chartWrapper = new google.visualization.ChartWrapper({ chartType: 'LineChart', containerId: 'lineChart', options: { fontName: 'Georgia', vAxis: {title: 'Individual Building Usage in Kilowatts'}, hAxis: { textStyle: {fontSize: 14}, gridlines: { count: -1, color: '#fff', units: { years: {format: ['yyyy', "''yy"]}, months: {format: ['MMM yyyy', "MMM ''yy",'MMM']}, days: {format: ['EEE MMM d, yyyy', "MMM d, ''yy", 'MMM d']}, hours: {format: ['']} } }, minorGridlines: { units: { months: {format: ['MMM']}, days: {format: ['d']}, hours: {format: ['']} } } },//close hAxis colors: buildingColors, legend: 'none' },//close options }) //close chart_wrapper //event handler saves date google.visualization.events.addListener(controlWrapper, 'statechange', function(){ startDate = controlWrapper.getState().range.start endDate = controlWrapper.getState().range.end } ) dashboard.bind(controlWrapper, chartWrapper) //bind control to chart if(whichBuildings[0]===true){ handleTotalOptions(whichBuildings[0]) } google.visualization.events.addOneTimeListener(dashboard, 'ready', disableLoadingScreen); drawLineChart() } //hides loading icon, called once chart is ready function disableLoadingScreen() { document.getElementById("spinner").classList.add('displayNone') } //toggles if a building is shown in the chart and calls the chart to be redrawn function flipAndRedraw(which){ whichBuildings[which] = !whichBuildings[which] if(which===0){ //Adds or removes total options for chart handleTotalOptions(whichBuildings[0]) } drawLineChart() } //adds or removes options needed for Total which involves a second y-axis function handleTotalOptions(totalVisible){ if(totalVisible===true){ //adds options for separate series for total data chartWrapper.setOption('series',{0: {'targetAxisIndex': 1}}) chartWrapper.setOption('vAxes', {1: { 'title': 'Total Campus Usage in Kilowatts', 'titleColor': buildingColors[0], 'textColor': buildingColors[0], 'gridlines': {'color': 'transparent'}, 'baselineColor': 'transparent' }}) } else{ //removes options for total data chartWrapper.setOption('series',{}) } } //sets options for columns and colors in the chart and control bar and then draws the chart function drawLineChart(){ // column 0 is date and should always be included var visibleColumns = [0] var visibleColors = [] for(i=0;i<whichBuildings.length;i++){ if(whichBuildings[i]){ //since column 0 is date, column index is one greater for API visibleColumns.push(i+1) visibleColors.push(buildingColors[i]) } } lineChartData.setColumns(visibleColumns) chartWrapper.setOption('colors', visibleColors) // Total is not displayed in controlWrapper if it is enabled if(whichBuildings[0]===true){ controlWrapper.setOption('ui.chartOptions.colors', visibleColors.slice(1)) //Control wrapper columns are based on indexes of chart columns, include every index but 1 for Total var controlWrapperColumns = [0] for(i=2; i<visibleColumns.length;i++){ controlWrapperColumns.push(i) } controlWrapper.setOption('ui.chartView.columns', controlWrapperColumns) } else{ controlWrapper.setOption('ui.chartOptions.colors', visibleColors) controlWrapper.setOption('ui.chartView.columns', null) } dashboard.draw(lineChartData) } //changes view within dashboard div between chart, how to use page, and about page function changeDashboard(button) { if(button=="close"){ document.getElementById("lineChart").classList.remove('displayNone') document.getElementById("control").classList.remove('displayNone') document.getElementById("howToUse").classList.add('displayNone') document.getElementById("aboutText").classList.add('displayNone') document.getElementById("closeButton").classList.add('displayNone') } else{ document.getElementById("lineChart").classList.add('displayNone') document.getElementById("control").classList.add('displayNone') document.getElementById("closeButton").classList.remove('displayNone') if(button=="about"){ document.getElementById("howToUse").classList.add('displayNone') document.getElementById("aboutText").classList.remove('displayNone') } if(button=="howToUse"){ document.getElementById("aboutText").classList.add('displayNone') document.getElementById("howToUse").classList.remove('displayNone') } } } //if user changes the window size the chart is redrawn to be the right size $(window).resize(function() { dashboard.draw(lineChartData) }) //called if series menu is clicked, toggles menu being shown function showSeriesOptions(el) { el.nextElementSibling.classList.toggle('displayNone') } //if site was loaded using a permalink, modifies startDate, endDate, and whichBuildings function parsePermalink(){ permalinked = URLComponents.length==2 if(permalinked){ var args = URLComponents[1] var argsArray = args.split("+") var startTemp = parseDate(argsArray[0]) var endTemp = parseDate(argsArray[1]) var whichArgs = parseInts(argsArray[2].split("")) for(i=0; i<whichArgs.length; i++){ whichBuildings[whichArgs[i]] = !whichBuildings[whichArgs[i]] } if(startTemp < endTemp){ startDate = startTemp endDate = endTemp } } } //takes the date string from a permalink and returns them as a date object function parseDate(urlArg){ var dateArr = urlArg.split("-") //month is zero-indexed return new Date(parseInt(dateArr[0]), parseInt(dateArr[1]) - 1, parseInt(dateArr[2])) } //takes the string of integers from a permalink and returns them as an array of integers function parseInts(strArray){ var a = new Array() for(i=0; i < strArray.length; i++) a.push(parseInt(strArray[i])) return a } //generates a permalink and copies it, called in index.html when the Get Permalink button is clicked function genPermalink(){ //month is zero-indexed var startStr = startDate.getFullYear()+"-"+(startDate.getMonth() + 1)+"-"+startDate.getDate() var endStr = endDate.getFullYear()+"-"+(endDate.getMonth() + 1)+"-"+endDate.getDate() var disabledBuildings = "" for(i=0; i < buildingNum; i++){ if(!whichBuildings[i]) disabledBuildings += i } var permalink = URLComponents[0]+"?"+startStr+"+"+endStr+"+"+disabledBuildings //Copies permalink and puts permalink in text box next to the button var permalinkTextField = document.getElementById('permalink') permalinkTextField.value = permalink permalinkTextField.select() document.execCommand('copy') //Fades tooltip in and out var tooltip = document.querySelector('.tooltip') tooltip.classList.add('active') tooltip.classList.remove('inactive') setTimeout(function(){ tooltip.classList.remove('active') setTimeout(function(){ tooltip.classList.add('inactive') }, 1000) }, 1000) } //prepares and then downloads a csv file based on what is currently shown in the chart function downloadCSV(el) { //creates csvString using same visible data as chart var dataView = new google.visualization.DataView(lineChartData) var filteredRows = dataView.getFilteredRows([{column: 0, minValue: startDate, maxValue: endDate}]) dataView.setRows(filteredRows) var csvString = google.visualization.dataTableToCsv(dataView) //generates header to append to beginning of csvString var csvHeader = "" for(i=0; i < lineChartData.getNumberOfColumns(); i++){ csvHeader += lineChartData.getColumnLabel(i) csvHeader += "," } csvString = csvHeader + "\n" + csvString //downloads csv var encodedUri = 'data:application/csv;charset=utf-8,' + encodeURIComponent(csvString) el.href = encodedUri el.download = 'energy-monitoring-data.csv' el.target = '_blank' }