Skip to content
Snippets Groups Projects
main.js 10.1 KiB
Newer Older
jiangxu's avatar
jiangxu committed
/*
	Authors: Tristan Wright, July 2012, Lillian Gray 2017-2020
jiangxu's avatar
jiangxu committed
	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)
jiangxu's avatar
jiangxu committed
*/

//init globals
//chart data source is global so we don't have to reload every time.

var lineChartData
var dashboard
var controlWrapper
var chartWrapper
jiangxu's avatar
jiangxu committed

const buildingNum = 8
var whichBuildings = new Array()
jiangxu's avatar
jiangxu committed
for(i=0; i < buildingNum; i++){
	whichBuildings[i] = true
jiangxu's avatar
jiangxu committed
}

//change colors here
var buildingColors = new Array( '#9c1c34', '#0A22af', '#ff381a','#00ff00', '#009944','#50D8F1','#e9af32', '#E07628') 
jiangxu's avatar
jiangxu committed

//variables for range of control under chart
var endDate	  
var startDate
jiangxu's avatar
jiangxu committed

//split url on '?' for permalink creation and parsing
var URLComponents = document.URL.split('?')
jiangxu's avatar
jiangxu committed

/*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
	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'))
jiangxu's avatar
jiangxu committed

	var latestEntryDate = new Date(lineChartData.getValue(lineChartData.getNumberOfRows()-1, 0))
	endDate = new Date(latestEntryDate)
	startDate = new Date(latestEntryDate.setMonth(latestEntryDate.getMonth() - 1))

	parsePermalink()
jiangxu's avatar
jiangxu committed
	//settings for control bar below chart
	controlWrapper = new google.visualization.ControlWrapper({
Lillian Gray's avatar
Lillian Gray committed
		controlType: 'ChartRangeFilter',
		containerId: 'control',
		options: {
			//filter by the date axis.
Lillian Gray's avatar
Lillian Gray committed
			filterColumnIndex: 0,
			ui: {
				chartType: 'LineChart',
				chartOptions: {
                	chartArea: {width: '100%'},
                	colors: buildingColors.slice(1),
					hAxis: {baselineColor: 'none'}
				},
jiangxu's avatar
jiangxu committed
				//1 day in milliseconds = 24 * 60 * 60 * 1000 = 86,400,000
Lillian Gray's avatar
Lillian Gray committed
				minRangeSize: 86400000
			}//close ui
		},//close options
Lillian Gray's avatar
Lillian Gray committed
		state: {range: {start: startDate, end: endDate}}
jiangxu's avatar
jiangxu committed

	//chart settings
	chartWrapper = new google.visualization.ChartWrapper({
		chartType: 'LineChart',
		containerId: 'lineChart',
		options: {
Lillian Gray's avatar
Lillian Gray committed
			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: ['']}
Lillian Gray's avatar
Lillian Gray committed
				minorGridlines: {
					units: {
						months: {format: ['MMM']},
						days: {format: ['d']},
						hours: {format: ['']}
Lillian Gray's avatar
Lillian Gray committed
			colors: buildingColors,
			legend: 'none'
jiangxu's avatar
jiangxu committed
		},//close options
	}) //close chart_wrapper
jiangxu's avatar
jiangxu committed

	//event handler saves date
	google.visualization.events.addListener(controlWrapper, 'statechange',
		function(){
			startDate = controlWrapper.getState().range.start
			endDate   = controlWrapper.getState().range.end
jiangxu's avatar
jiangxu committed
		}
jiangxu's avatar
jiangxu committed

	dashboard.bind(controlWrapper, chartWrapper) //bind control to chart
jiangxu's avatar
jiangxu committed

	if(whichBuildings[0]===true){
		handleTotalOptions(whichBuildings[0])
jiangxu's avatar
jiangxu committed
	}

	google.visualization.events.addOneTimeListener(dashboard, 'ready', disableLoadingScreen);

jiangxu's avatar
jiangxu committed
}

//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
jiangxu's avatar
jiangxu committed
function parseDate(urlArg){
	var dateArr = urlArg.split("-")
jiangxu's avatar
jiangxu committed
	//month is zero-indexed
	return new Date(parseInt(dateArr[0]), parseInt(dateArr[1]) - 1, parseInt(dateArr[2]))
jiangxu's avatar
jiangxu committed
}

//takes the string of integers from a permalink and returns them as an array of integers
jiangxu's avatar
jiangxu committed
function parseInts(strArray){
	var a = new Array()
jiangxu's avatar
jiangxu committed
	for(i=0; i < strArray.length; i++)
		a.push(parseInt(strArray[i]))
	return a
jiangxu's avatar
jiangxu committed
}

//generates a permalink and copies it, called in index.html when the Get Permalink button is clicked
jiangxu's avatar
jiangxu committed
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 = ""
jiangxu's avatar
jiangxu committed
	for(i=0; i < buildingNum; i++){
		if(!whichBuildings[i])
		disabledBuildings += i
jiangxu's avatar
jiangxu committed
	}
	var permalink = URLComponents[0]+"?"+startStr+"+"+endStr+"+"+disabledBuildings
jiangxu's avatar
jiangxu committed

	//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')
jiangxu's avatar
jiangxu committed

//prepares and then downloads a csv file based on what is currently shown in the chart
function downloadCSV(el) {
jiangxu's avatar
jiangxu committed
	//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)
jiangxu's avatar
jiangxu committed

	//generates header to append to beginning of csvString
	var csvHeader = ""
jiangxu's avatar
jiangxu committed
	for(i=0; i < lineChartData.getNumberOfColumns(); i++){
		csvHeader += lineChartData.getColumnLabel(i)
		csvHeader += ","
jiangxu's avatar
jiangxu committed
	}
	csvString = csvHeader + "\n" + csvString
jiangxu's avatar
jiangxu committed

	//downloads csv
	var encodedUri = 'data:application/csv;charset=utf-8,' + encodeURIComponent(csvString)
	el.href = encodedUri
	el.download = 'energy-monitoring-data.csv'
	el.target = '_blank'