Newer
Older
package com.application.GUI;
import com.application.DB.Constants;
import com.application.DB.Settings;
Eilert Tunheim
committed
import com.application.GUI.PopUpWindows.NotificationPopUp;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
Eilert Tunheim
committed
import org.apache.commons.math3.distribution.TDistribution;
import org.apache.commons.math3.exception.MathIllegalArgumentException;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.apache.commons.math3.stat.regression.SimpleRegression;
Eilert Tunheim
committed
import java.text.SimpleDateFormat;
Eilert Tunheim
committed
import java.util.*;
Eilert Tunheim
committed
import static com.application.Main.*;
/**
* This class handle all the GUI functionality and statistics calculations
*
* @author Eilert Tunheim, Karin Pettersen, Mads Arnesen
* @version 1.0.0
*/
public class LineChartFunctionality {
Eilert Tunheim
committed
private static LineChart<String, Number> lineChart;
private static XYChart.Series<String, Number> liveDataSeries;
private static XYChart.Series<String, Number> regressionSeries;
private static XYChart.Series<String, Number> regressionSeriesConfidenceInterval;
Eilert Tunheim
committed
private static CategoryAxis xAxis;
private static NumberAxis yAxis;
private static int dataPointsXAxis = 0;
private static int dataPointsYAxis = 0;
private static Map<String, Number> liveData;
private static ArrayList<XYChart.Series<String, Number>> previousData;
private static boolean printRegression;
private static boolean printRegressionConfidenceInterval;
private static boolean printLiveData;
private static boolean printPreviousData;
Eilert Tunheim
committed
Eilert Tunheim
committed
public LineChartFunctionality() {
Eilert Tunheim
committed
xAxis = new CategoryAxis();
yAxis = new NumberAxis();
Eilert Tunheim
committed
lineChart = new LineChart<>(xAxis, yAxis);
lineChart.setTitle("Drying Processes");
lineChart.setAnimated(false);
Eilert Tunheim
committed
xAxis.setLabel("Data Points");
Eilert Tunheim
committed
yAxis.setLabel("Kwh");
regressionSeries = new XYChart.Series<String, Number>();
regressionSeriesConfidenceInterval = new XYChart.Series<String, Number>();
liveDataSeries = new XYChart.Series<String, Number>();
previousData = new ArrayList<>();
printRegression = true;
printRegressionConfidenceInterval = false;
printLiveData = true;
printPreviousData = true;
Eilert Tunheim
committed
/**
* Prints the selected graphs to the line chart
Eilert Tunheim
committed
*
* Note: Something wrong when exceeding 8 series, think the next series choose a random series color
*/
public static void printGraphs() {
Eilert Tunheim
committed
getLineChart().getData().clear();
if(isPrintRegressionConfidenceInterval()){
updateLineChart(getRegressionSeriesConfidenceInterval());
getMenuViewRegressionShadow().setSelected(true);
getRegressionConfidenceIntervalBox().setSelected(true);
Eilert Tunheim
committed
} else {
updateLineChart(new XYChart.Series<>());
getMenuViewRegressionShadow().setSelected(false);
getRegressionConfidenceIntervalBox().setSelected(false);
Eilert Tunheim
committed
updateLineChart(getRegressionSeries());
getMenuViewRegression().setSelected(true);
getRegressionBox().setSelected(true);
Eilert Tunheim
committed
} else {
updateLineChart(new XYChart.Series<>());
getMenuViewRegression().setSelected(false);
getRegressionBox().setSelected(false);
Eilert Tunheim
committed
updateLineChart(getLiveDataSeries());
getMenuViewLiveData().setSelected(true);
getLiveDataBox().setSelected(true);
Eilert Tunheim
committed
} else {
updateLineChart(new XYChart.Series<>());
getMenuViewLiveData().setSelected(false);
getLiveDataBox().setSelected(false);
Eilert Tunheim
committed
if(isPrintPreviousData()){
getPreviousBox().setSelected(true);
getMenuViewPreviousData().setSelected(true);
Eilert Tunheim
committed
// If there are more than 5 series, adds 3 new empty series to keep the color scheme
for (int i = 0; i < getPreviousData().size(); i++) {
Eilert Tunheim
committed
int index = getLineChart().getData().size();
if (index % 8 == 0 ){
for (int j = 0; j < 3; j++) {
updateLineChart(new XYChart.Series<>());
}
}
updateLineChart(getPreviousData().get(i));
}
} else {
getPreviousBox().setSelected(false);
getMenuViewPreviousData().setSelected(false);
/**
* This function calculates the confidence interval
*
* @param multiMap input containing the drying cycle data
* @param CIShadow input to indicate if the confidence interval shadow is stored or not
* @return a map containing the processed data
*/
private static Map<Integer, ArrayList<Double>> statistics(Map<Integer, ArrayList<Double>> multiMap, boolean CIShadow){
Eilert Tunheim
committed
for (Map.Entry<Integer, ArrayList<Double>> entry : multiMap.entrySet()) {
SummaryStatistics data = new SummaryStatistics();
Eilert Tunheim
committed
for (double val : entry.getValue()) {
Eilert Tunheim
committed
}
// Calculate the confidence interval
double confidenceInterval = calcMeanConfidenceInterval(data);
double lower = data.getMean() - confidenceInterval;
double upper = data.getMean() + confidenceInterval;
Eilert Tunheim
committed
// Deletes entries if they are out of bounds with the confidence interval
entry.getValue().removeIf(value -> Double.compare(value, lower) < 0 || Double.compare(value, upper) > 0);
// Checks if the confidence interval shall be returned or not
if(CIShadow){
ArrayList<Double> lowerUpperBounds = new ArrayList<>();
lowerUpperBounds.add(lower);
lowerUpperBounds.add(upper);
multiMap.replace(entry.getKey(), lowerUpperBounds);
}
Eilert Tunheim
committed
}
return multiMap;
}
/**
* This function calculates the confidence interval mean value
*
* @param data the data to base the calculation on
* @return returns the confidence interval mean value
*/
private static double calcMeanConfidenceInterval(SummaryStatistics data) {
Eilert Tunheim
committed
try {
// Create a T distribution
TDistribution tDistribution = new TDistribution(data.getN() - 1);
// Calculate the critical value
double criticalValue = tDistribution.inverseCumulativeProbability(1.0 - (1 - Settings.CONFIDENCE_INTERVAL) / 2);
// Calculate the confidence interval
return criticalValue * data.getStandardDeviation() / Math.sqrt(data.getN());
Eilert Tunheim
committed
} catch (MathIllegalArgumentException e) {
return Double.NaN;
/**
* This function serves as the main function that controls all the functionality.
* This function takes the data inputted and plots it and calculates the statistics.
*
* @param userInput data regarding the drying cycles
* @throws Exception throws exception if an error occurs
*/
public static void loadSingleSeries(Map<Integer, Map<String, Number>> userInput) throws Exception {
// Clears the linechart if there are previous graphs plotted
Eilert Tunheim
committed
Map<Integer, ArrayList<Double>> multiMap = new HashMap<>();
for (Map.Entry<Integer, Map<String, Number>> entryKwh : userInput.entrySet()) {
Map data = entryKwh.getValue();
Eilert Tunheim
committed
int index = 0;
for (Object entryData : data.entrySet()) {
String entryString = entryData.toString();
String[] arr = entryString.split("=");
Eilert Tunheim
committed
Double kwhValue = Double.parseDouble(arr[1]);
Eilert Tunheim
committed
// Checks if the index already got an arraylist, if not one is created
multiMap.computeIfAbsent(index, k -> new ArrayList<Double>());
multiMap.get(index).add(kwhValue);
index++;
Eilert Tunheim
committed
// Finds the end datapoint at the end of each graph
int numberOfGraphs = 0;
ArrayList<Double> dataArraylistXAxis = new ArrayList<>();
ArrayList<Double> dataArraylistYAxis = new ArrayList<>();
Map<Integer, ArrayList<Double>> endOfGraphPointsXAxis = new HashMap<>();
Map<Integer, ArrayList<Double>> endOfGraphPointsYAxis = new HashMap<>();
for (int i = 0; i < multiMap.size(); i++) {
ArrayList<Double> list = multiMap.get(i);
for (int j = 0; j < list.size(); j++) {
if (numberOfGraphs < list.size()) {
numberOfGraphs = list.size();
}
if (list.size() < numberOfGraphs) {
dataArraylistXAxis.add((double) i);
dataArraylistYAxis.add(multiMap.get(i).get(j));
numberOfGraphs = list.size();
}
}
}
dataArraylistXAxis.add((double) multiMap.size());
dataArraylistYAxis.add(multiMap.get(multiMap.size()-1).get(0));
endOfGraphPointsXAxis.put(0,dataArraylistXAxis);
endOfGraphPointsYAxis.put(0,dataArraylistYAxis);
Map<Integer, ArrayList<Double>> endOfGraphPointsConfidenceXAxis = statistics(endOfGraphPointsXAxis,false);
Map<Integer, ArrayList<Double>> endOfGraphPointsConfidenceYaxis = statistics(endOfGraphPointsYAxis,false);
dataPointsXAxis = 0;
for (Map.Entry<Integer, ArrayList<Double>> entry : endOfGraphPointsConfidenceXAxis.entrySet()) {
for (int i = 0; i < entry.getValue().size(); i++) {
dataPointsXAxis += entry.getValue().get(i);
}
}
dataPointsXAxis = dataPointsXAxis /endOfGraphPointsConfidenceXAxis.get(0).size();
dataPointsYAxis = 0;
for (Map.Entry<Integer, ArrayList<Double>> entry : endOfGraphPointsConfidenceYaxis.entrySet()) {
for (int i = 0; i < entry.getValue().size(); i++) {
dataPointsYAxis += entry.getValue().get(i);
}
}
dataPointsYAxis = dataPointsYAxis /endOfGraphPointsConfidenceYaxis.get(0).size();
// Stores the data from the confidence interval in a new map
Map<Integer, ArrayList<Double>> confidenceIntervalData = statistics(multiMap,false);
Eilert Tunheim
committed
// Checks the max size for the arraylists needed for the data array later
int jMaxSize = 0;
for (int i = 0; i < confidenceIntervalData.size(); i++) {
if(confidenceIntervalData.get(i).size() > jMaxSize){
jMaxSize = confidenceIntervalData.get(i).size();
}
}
// Defines an array to be used for the linear regression
double[][] data = new double[confidenceIntervalData.size()*jMaxSize][2];
int index = 0;
for (int i = 0; i < confidenceIntervalData.size(); i++) {
ArrayList<Double> list = confidenceIntervalData.get(i);
data[index][0] = i;
index++;
// Adds the data to the confidence interval shadow
getRegressionSeriesConfidenceInterval().getData().clear();
Map<Integer, ArrayList<Double>> confidenceIntervalShadow = statistics(multiMap,true);
for ( Map.Entry<Integer, ArrayList<Double>> entry : confidenceIntervalShadow.entrySet()) {
for (int i = 0; i < entry.getValue().size(); i++) {
Double doubleData = entry.getValue().get(i);
getRegressionSeriesConfidenceInterval().getData().add(new XYChart.Data<String, Number>(String.valueOf(entry.getKey()), doubleData.intValue()));
}
}
SimpleRegression simpleRegression = new SimpleRegression();
simpleRegression.addData(data);
getRegressionSeries().getData().clear();
for (int i = 0; i <= getDataPointsXAxis(); i++) {
getRegressionSeries().getData().add(new XYChart.Data<String, Number>(
String.valueOf(i),
getNonLinearRegression(
confidenceIntervalData,
Math.sqrt(Math.pow(simpleRegression.getIntercept(),2)),
simpleRegression.getSlope(),
i,
getDataPointsXAxis()
)));
getPreviousData().clear();
for (Map.Entry<Integer, Map<String, Number>> entryKwh : userInput.entrySet()) {
XYChart.Series<String, Number> newSeries = new XYChart.Series<String, Number>();
index = 0;
for (Object entryData : entryKwh.getValue().entrySet()) {
String entryString = entryData.toString();
Double kwhValue = Double.parseDouble(entryString.split("=")[1]);
// Connect the data to a series
newSeries.getData().add(new XYChart.Data<String, Number>(String.valueOf(index), kwhValue));
index++;
}
addPreviousData(newSeries);
/**
* This function loads the live data and plots the data in the graph
*
* @param userInput the input parameter for the live data
* @throws Exception throws an exception if an error occur
*/
public static void loadLiveData(Map<String, Number> userInput) throws Exception {
// Clears any data already there
getLiveDataSeries().getData().clear();
// Sets the livedata in series
// Updates time left
for (Map.Entry<String, Number> entryKwh : userInput.entrySet()) {
// Finds the index value
int index = (int) (findDifference(Constants.START_TIME, entryKwh.getKey()) / 10);
// Gets kwh value
Double kwhValue = entryKwh.getValue().doubleValue();
// Connect the data to a series
getLiveDataSeries().getData().add(new XYChart.Data<String, Number>(String.valueOf(index), kwhValue));
/**
* Finds the difference between two dates
*
* @param start_date input parameter for the first date
* @param end_date input parameter for the second date
* @return returns the differnce in minutes
*/
private static long findDifference(String start_date, String end_date) {
// Defining a simple date format
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try{
// try to convert the string to Date datatype
Date dateStart = dateFormat.parse(start_date);
Date dateEnd = dateFormat.parse(end_date);
// Finds the difference in millis
long differenceMillis = dateEnd.getTime() - dateStart.getTime();
// Finds the difference in minutes
return (differenceMillis / (1000 * 60 )) % 60;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return 0;
}
/**
* This function calculates the remaining time of the current drying cycle
*
* @param liveData input parameter for the data to base the calculations on
*/
public static void getTimeLeft(int liveData){
int minutes = 0;
int hours = 0;
// There are 10 minutes between each datapoint
minutes = getDataPointsXAxis()*10;
minutes = (getDataPointsXAxis()/liveData)*10;
if(minutes > 60){
hours = minutes/60;
Main.setTimeLeftText(hours + " h");
Eilert Tunheim
committed
if(hours == 3){
NotificationPopUp.displayNotificationWindow("3 Hours Left!");}
if(minutes == 60){NotificationPopUp.displayNotificationWindow("1 Hours Left!");}
Main.setTimeLeftText(minutes + " min");
if(minutes == 0 && getDataPointsXAxis() != 0){
NotificationPopUp.displayNotificationWindow("Drying Process Finished!");
* This function handles the logistic regression
* @param confidenceIntervalData the input parameter for the data to base the regression on
* @param y0 the input parameter for the intercept from the linear regression
* @param alpha the input parameter for the slope value from the linear regression
* @param j the input parameter for current x-axis datapoint to predict the value for
* @param n the input parameter for the total number of points along the x-axis
* @return the predicted value at a given point
public static double getNonLinearRegression(Map<Integer, ArrayList<Double>> confidenceIntervalData, double y0, double alpha, double j, double n) {
double beta = getDataPointsYAxis()+y0;
return (((beta * y0))/(y0 +((beta- y0)*Math.exp(-alpha*j/n/((alpha/10))))))-y0;
public static int getDataPointsXAxis() {
return dataPointsXAxis;
}
public static int getDataPointsYAxis() {
return dataPointsYAxis;
}
public static Map<String, Number> getLiveData() {
return liveData;
}
public static void setLiveData(Map<String, Number> input) {
liveData = input;
}
public static XYChart.Series<String, Number> getLiveDataSeries() {
return liveDataSeries;
}
public static XYChart.Series<String, Number> getRegressionSeries() {
return regressionSeries;
}
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
public static boolean isPrintRegression() {
return printRegression;
}
public static void setPrintRegression(boolean printRegression) {
LineChartFunctionality.printRegression = printRegression;
}
public static boolean isPrintRegressionConfidenceInterval() {
return printRegressionConfidenceInterval;
}
public static void setPrintRegressionConfidenceInterval(boolean printRegressionConfidenceInterval) {
LineChartFunctionality.printRegressionConfidenceInterval = printRegressionConfidenceInterval;
}
public static boolean isPrintLiveData() {
return printLiveData;
}
public static void setPrintLiveData(boolean printLiveData) {
LineChartFunctionality.printLiveData = printLiveData;
}
public static boolean isPrintPreviousData() {
return printPreviousData;
}
public static void setPrintPreviousData(boolean printPreviousData) {
LineChartFunctionality.printPreviousData = printPreviousData;
}
public static ArrayList<XYChart.Series<String, Number>> getPreviousData() {
return previousData;
}
public static void addPreviousData(XYChart.Series<String, Number> previousData) {
LineChartFunctionality.previousData.add(previousData);
}
public static XYChart.Series<String, Number> getRegressionSeriesConfidenceInterval() {
return regressionSeriesConfidenceInterval;
}
public static LineChart<String, Number> getLineChart() {
return lineChart;
}
public static void updateLineChart(XYChart.Series<String, Number> series) {
getLineChart().getData().add(series);
series.getNode().setId("dataGraphs");
getLineChart().getStylesheets().add(LineChartFunctionality.class.getResource("/com.application/GUI/graphStyles.css").toExternalForm());
}
public static void clearLineChart() {
getLineChart().getData().clear();