guiDrought_Model.java [src/java/cfa] Revision: 4daefd1ac3a5cce6d2af07d219b133db7ce0b7a4  Date: Thu Sep 26 16:17:42 MDT 2013
package cfa;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.chart.annotations.XYDrawableAnnotation;
import org.jfree.chart.annotations.XYPointerAnnotation;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.annotations.XYTitleAnnotation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.TickUnits;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.time.Year;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.TextAnchor;

/**
* Last Updated: 26-August-2013
* @author Tyler Wible
* @since 10-July-2012
*/
public class guiDrought_Model {
    //Inputs
    String mainFolder 	= "C:/Projects/TylerWible/CodeDirectories/NetBeans/CSIP/data/CFA";
    String organizationName = "USGS";//"UserData";//
    String stationID 	= "06752000";//"ARTest2_7";//"06741510";//"ARExample";//"06752260";//
    String stationName 	= "CACHE LA POUDRE RIV AT MO OF CN, NR FT COLLINS, CO";
    String beginDate 	= "";//"1900-01-01";//
    String endDate 	= "";//"2002-01-01";//
    String lambdaString	= "optimize";//"1";//
    String action	= "all";//"optimizeModel";//"optimizeParameters";//"useParameters";//
    String phiValues	= "1";
    String thetaValues	= "";
    double droughtLimit	= -1;
    String userData = "";//"Date\tFlow\n1997-04-29\t8.3\n1998-05-09\t60.2\n1999-05-29\t20.1";
    boolean mergeDatasets = false;//true;//
    String mergeMethod = "user";//"public";//"max";//"average";//"min";//
    
    //Outputs
    String len = "-1";
    String start = "?";
    String end = "?";
    
    //Gets
    public File getResult() {
        return new File(mainFolder, "drought_summary.txt");
    }
    public String[] get_all_output() {
        String[] graphs = new String[5];
        graphs[0] = "drought_graph1.jpg";
        graphs[1] = "drought_graph2.jpg";
        graphs[2] = "drought_graph3.jpg";
        graphs[3] = "drought_graph4.jpg";
        graphs[4] = "drought_graph5.jpg";
        return graphs;
    }
    public String[] get_optimizeModel_output() {
        if(thetaValues.equalsIgnoreCase("")){
            //The drought model used is AR(p) so only 1 graph
            String[] graphs = new String[1];
            graphs[0] = "optimize_drought_graph1.jpg";
            return graphs;
        }else{
            //The drought model used is ARMA(p,q) so only 2 graph
            String[] graphs = new String[2];
            graphs[0] = "optimize_drought_graph3.jpg";
            graphs[1] = "optimize_drought_graph4.jpg";
            return graphs;
        }
    }
    public String get_parameter_output(){
        String[] graphNames = get_all_output();
        //Return only the name of the fitted data graph
        return graphNames[2];
    }
    public String getLen() {
        return len;
    }
    public String getStart() {
        return start;
    }
    public String getEnd() {
        return end;
    }
    //These 'gets' are to help decide which return function to perform
    public String getAction() {
        return action;
    }
    public String getThetaValues() {
        return thetaValues;
    }
    
    
    //Sets
    public void setMainFolder(String mainFolder) {
        this.mainFolder = mainFolder;
    }
    public void setOrganizationName(String organizationName) {
        this.organizationName = organizationName;
    }
    public void setStationID(String stationID) {
        this.stationID = stationID;
    }
    public void setStationName(String stationName) {
        this.stationName = stationName;
    }
    public void setBeginDate(String beginDate) {
        this.beginDate = beginDate;
    }
    public void setEndDate(String endDate) {
        this.endDate = endDate;
    }
    public void setLambdaString(String lambdaString) {
        this.lambdaString = lambdaString;
    }
    public void setAction(String action) {
        this.action = action;
    }
    public void setPhiValues(String phiValues) {
        this.phiValues = phiValues;
    }
    public void setThetaValues(String thetaValues) {
        this.thetaValues = thetaValues;
    }
    public void setDroughtLimit(double droughtLimit) {
        this.droughtLimit = droughtLimit;
    }
    public void setUserData(String userData) {
        this.userData = userData;
    }
    public void setMergeDatasets(boolean mergeDatasets) {
        this.mergeDatasets = mergeDatasets;
    }
    public void setMergeMethod(String mergeMethod) {
        this.mergeMethod = mergeMethod;
    }
    /**
     * Performs a drought analysis on the provided sortedData (daily average stream flow value) and calculates the long-term 
     * average water supply to be taken as the drought threshold.  First it calculates total annual river flows based on the 
     * provided daily stream flow averages, then calculates the long-term average of these annual flow values, then calculates 
     * the properties of the droughts (deficit, duration, intensity) compared against the drought threshold.  Finally graphs a 
     * timeseries of the annual flow data against the water demand.
     * @param mainFolder  the file path to the location of the graph (ex. "C:/temp/myFolder")
     * @param sortedData  a string[][] containing daily average stream flow values in the following format: column1 = dates (yyyy-mm-dd format), 
     * column2 = daily average stream flow values (cfs)
     * @param lambdaString  contains either the user provided value of lambda or "optimize" which tells the program to optimize lambda based on the dataset
     * @param action  if "optimize", this only optimizes the lambda and phi values for the model and then reports these back to the user, 
     * if "all" then it generates data based on the calculated phi values and finishes performing the drought analysis, 
     * if "useParameters" then it takes the previously generated parameters and uses these to perform the regression
     * @param phiValues
     * @param thetaValues
     * @param graphTitle  the title of the graph to be created (should contain station ID and station Name for user's benefit)
     * @return  a String[] containing a tab-delimited summary of the droughts (water supply < water demand)
     * @throws IOException 
     */
    public String[] generalDroughtAnalysis(String mainFolder, 
                                                                            String[][] sortedData,
                                                                            String lambdaString,
                                                                            String action,
                                                                            String phiValues,
                                                                            String thetaValues,
                                                                            String graphTitle) throws IOException{
            DoubleMath doubleMath = new DoubleMath();
            DoubleArray doubleArray = new DoubleArray();
            //Calculate total annual flows
            double[][] annualData = sumAnnualFlows(sortedData);


            //Get the flow values and calculate the long-term (total) average of them
            double longTermAverage = doubleMath.Average(doubleArray.getColumn(annualData, 1));


            //Call built in function to perform the drough analysis and graph the resulting data
            String[] droughtInfo = generalDroughtAnalysis(mainFolder, sortedData, longTermAverage, lambdaString, action, phiValues, thetaValues, graphTitle);


            return droughtInfo;
    }
    /**
     * Performs a drought analysis on the provided sortedData (daily average stream flow value) and the provided long-term 
     * average water demand specified by the user.  First it calculated total annual river flows based on the provided daily 
     * stream flow averages, then calculates the properties of the droughts (deficit, duration, intensity) compared against 
     * the water demand.  Finally graphs a timeseries of the annual flow data against the water demand.
     * @param mainFolder  the file path to the location of the graph (ex. "C:/temp/myFolder")
     * @param sortedData  a string[][] containing daily average stream flow values in the following format: column1 = dates (yyyy-mm-dd format), 
     * column2 = daily average stream flow values (cfs)
     * @param droughtLimit  a double representing the annual drought limit (water demand) on the watershed as specified by the user (in units of acre-ft/year)
     * @param lambdaString  contains either the user provided value of lambda or "optimize" which tells the program to optimize lambda based on the dataset
     * @param action  if "optimize", this only optimizes the phi values for the model and then reports these back to the user, 
     * if "all" then it generates data based on the calculated phi values and finishes performing the drought analysis, 
     * if "useParameters" then it takes the previously generated parameters and uses these to perform the regression
     * @param phiValues
     * @param thetaValues
     * @param graphTitle  the title of the graph to be created (should contain station ID and station Name for user's benefit)
     * @return  a String[] containing a tab-delimited summary of the droughts (water supply < water demand)
     * @throws IOException 
     */
    @SuppressWarnings("unchecked")
    public String[] generalDroughtAnalysis(String mainFolder, 
                                                                            String[][] sortedData,
                                                                            double droughtLimit,
                                                                            String lambdaString,
                                                                            String action,
                                                                            String phiValues,
                                                                            String thetaValues,
                                                                            String graphTitle) throws IOException{
//	//Test AR(p) model on sample data
//	double[][] annualData = sumAnnualFlows(sortedData);
//	Object[] returnArray = autoRegression.AR(p, annualData);
//	annualData = (double[][]) returnArray[0];
//	double[][] generatedData = (double[][]) returnArray[1];
//	autoRegression.graphAR1Double(annualData, generatedData);
            DoubleMath doubleMath = new DoubleMath();
            DoubleArray doubleArray = new DoubleArray();
            AutoRegression autoRegression = new AutoRegression();

            //Calculate total annual flows
            double[][] annualData = sumAnnualFlows(sortedData);


            //Convert the data to only the stoicastic portion to modeled using AR(1) or ARMA(1,1)
            double average = doubleMath.Average(doubleArray.getColumn(annualData, 1));
            double stDev = doubleMath.StandardDeviationSample(doubleArray.getColumn(annualData, 1));
            double[][] stocaisticData = autoRegression.StoicasticConversion(annualData, average, stDev, true);


            //Perform a transformation on the stoicastic data to change its distribution to a normal distribution
            double lambda = 1.0;
            if(lambdaString.equalsIgnoreCase("optimize")){
                    lambda = autoRegression.BoxCox(stocaisticData);			
            }else{
                    lambda = Double.parseDouble(lambdaString);
            }
            double[][] transformedData = autoRegression.BoxCox(lambda, stocaisticData, true);
//	double[][] transformedData = autoRegression.SalasExampleTransformation(stocaisticData, true);



            //Run the user specified auto-regressive model on the data
            double[][] fittedData = null;
            double[][] predictedData = null;
            String methodType = "";
            Object[] returnArray = autoRegression.AutoregressiveModel(mainFolder, action, phiValues, thetaValues, transformedData);
            if(action.equalsIgnoreCase("optimizeParameters")){
                    //Find the parameters of the regression only and report these back
                    String[] stringArray = (String[]) returnArray;


                    //Perform regression with data and graph the resulting data
                    Object[] dataArray = autoRegression.AutoregressiveModel(mainFolder, "useParameters", stringArray[0], stringArray[1], transformedData);

                    //Keep the data from the regression and graph it
                    methodType = (String) dataArray[0];
                    transformedData = (double[][]) dataArray[1];
                    fittedData = (double[][]) dataArray[2];
                    predictedData = (double[][]) dataArray[3];

            }else if(action.equalsIgnoreCase("optimizeModel")){
                    if(thetaValues.equalsIgnoreCase("")){
                            //Calculate optimal AR(p) model and return the optimal phi values
                            ArrayList<Double> aic = (ArrayList<Double>) returnArray[0];
                            ArrayList<Double> bic = (ArrayList<Double>) returnArray[1];
                            String phi = (String) returnArray[2];
                            String theta = (String) returnArray[3];

                            //Graph the AIC/BIC curve for the user
                            graphAR_AIC_BIC(mainFolder, aic, bic);

                            String[] optimalValues = {phi + "$$", theta};
                            return optimalValues;

                    }else{
                            //Calculate optimal ARMA(p,q) model and return the optimal phi/theta values
                            double[][] results = (double[][]) returnArray[0];
                            String phi = (String) returnArray[1];
                            String theta = (String) returnArray[2];

                            //Graph the AIC/BIC curve for the user
                            graphARMA_AIC_BIC(mainFolder, "AIC", results, 3);
                            graphARMA_AIC_BIC(mainFolder, "BIC" , results, 4);

                            String[] optimalValues = {phi + "$$", theta};
                            return optimalValues;
                    }

            }else{
                    //Keep the data from the regression and graph it
                    methodType = (String) returnArray[0];
                    transformedData = (double[][]) returnArray[1];
                    fittedData = (double[][]) returnArray[2];
                    predictedData = (double[][]) returnArray[3];
            }

            //Un-transform the stoicastic historic and stoicastic generated data so as to graph real stream flow values
            transformedData = autoRegression.BoxCox(lambda, transformedData, false);
            fittedData = autoRegression.BoxCox(lambda, fittedData, false);
            predictedData = autoRegression.BoxCox(lambda, predictedData, false);
//	transformedData = autoRegression.SalasExampleTransformation(transformedData, false);
//	fittedData = autoRegression.SalasExampleTransformation(fittedData, false);
//	predictedData = autoRegression.SalasExampleTransformation(predictedData, false);


            //Convert back from the stoicastic component of the data to the entire original data
            double[][] originalData = autoRegression.StoicasticConversion(transformedData, average, stDev, false);
            fittedData = autoRegression.StoicasticConversion(fittedData, average, stDev, false);
            predictedData = autoRegression.StoicasticConversion(predictedData, average, stDev, false);


            //Calculate the droughts, their accumulated deficits, durations, and intensities
            DroughtProperties historicDroughtProps = new DroughtProperties(annualData, droughtLimit);
            DroughtProperties predictedDroughtProps = new DroughtProperties(predictedData, droughtLimit);



            //Determine the return periods for the droughts on record
            double[][] historicDroughts = recurrenceInterval(historicDroughtProps, droughtLimit, annualData);
            double[][] predictedDroughts = recurrenceInterval(predictedDroughtProps, droughtLimit, predictedData);


            //Graph the drought data
            graphTimeseries(mainFolder, annualData, droughtLimit, graphTitle);			//graph1
            graphNegativeBarChart(mainFolder, annualData, droughtLimit, graphTitle);			//graph2
            graphFittedData(mainFolder, originalData, fittedData, methodType, graphTitle);		//graph3
            graphPredictedData(mainFolder, originalData, predictedData, methodType, graphTitle);	//graph4
            graphReturnPeriod(mainFolder, historicDroughts, predictedDroughts, graphTitle);		//graph5




            //Populate the return array with the information gained from the drought analysis
            String[] droughtInfo = new String[historicDroughtProps.numberOfDroughts() + 1];
            droughtInfo[0] = "Deficit [acre-ft]\tLambda\tDuration [years]\tIntensity\tFinal Year";
            for(int i=0; i<historicDroughtProps.numberOfDroughts(); i++){
                    droughtInfo[i+1] = String.valueOf(historicDroughtProps.getDeficit(i)) + "\t" + 
                                    String.valueOf(historicDroughtProps.getDeficit(i)/droughtLimit) + "\t" + 
                                    String.valueOf(historicDroughtProps.getDuration(i)) + "\t" + 
                                    String.valueOf(historicDroughtProps.getIntensity(i)) + "\t" + 
                                    String.valueOf(historicDroughtProps.getEndYear(i));
            }

            if(action.equalsIgnoreCase("optimizeParameters")){
                    //Return only the parameters of the regression
                    String[] stringArray = (String[]) returnArray;
                    stringArray[0] = stringArray[0] + "$$";
                    return stringArray;
            }else if(action.equalsIgnoreCase("updateParameters")){
                    //Return only the parameters of the regression
                    String[] stringArray = {phiValues + "$$", thetaValues};
                    return stringArray;
            }

            return droughtInfo;
    }
    /**
     * Calculates the average recurrence interval for each drought on record and the lambda coefficient of each drought (deficit = lambda * droughtLimit)
     * @param currentDroughtProps  the DroughtProperties class containing the droughts to be analyzed
     * @param droughtLimit  the value of the water demand in acre-ft to calculate lambda from (lambda = deficit/water demand)
     * @param flowData  the double[][] array containing the annual flow data with column1 = years, column2 = flow values in acre-ft
     * @return  a double[][] array containing: column1 = lambda sets of eleven lambda values {0. 0.5, 0.75, 1.0, 2.0}, column2 = average recurrence interval (return period), column3 = length of drought (from 1 to 11
     */
    private double[][] recurrenceInterval(DroughtProperties currentDroughtProps, double droughtLimit, double[][] flowData){
            DoubleMath doubleMath = new DoubleMath();

            double[] lambda = {0.0, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0};
            ArrayList<Double> droughtLambda = new ArrayList<Double>();
            ArrayList<Double> droughtRecurrence = new ArrayList<Double>();
            ArrayList<Double> droughtLength = new ArrayList<Double>();
            //Loop through lambda values
            for(int i=0; i<lambda.length; i++){
                    //Loop drought year length
                    for(int j=1; j<11; j++){
                            ArrayList<Double> recurrence = new ArrayList<Double>();
                            int endYear = (int) flowData[0][0];

                            //Loop through droughts
                            for(int k=0; k<currentDroughtProps.numberOfDroughts(); k++){
                                    //Compare the properties of the current drought to the provided
                                    if(currentDroughtProps.getDeficit(k) > lambda[i]*droughtLimit && currentDroughtProps.getDuration(k) == j){
                                            //Measure recurrence from start of drought to start of drought (Salas, 2005 pg 385)
                                            recurrence.add((double) (currentDroughtProps.getEndYear(k) - endYear));
                                            endYear = currentDroughtProps.getEndYear(k);
                                    }
                            }

                            //There were observed droughts larger than this one, calculate the average recurrence interval
                            if(recurrence.size() != 0){
                                    droughtLambda.add(lambda[i]);//lambda value
                                    droughtRecurrence.add(doubleMath.Average(recurrence));//average recurrence = return period
                                    droughtLength.add((double) j);//length of drought
                            }
                    }
            }

            //Return the drought properties in a single double[][]
            double[][] newDroughts = new double[droughtLambda.size()][3];
            for(int i=0; i<droughtLambda.size(); i++){
                    newDroughts[i][0] = droughtLambda.get(i);//lambda value
                    newDroughts[i][1] = droughtRecurrence.get(i);//average recurrence = return period
                    newDroughts[i][2] = droughtLength.get(i);//length of drought
            }


            return newDroughts;
    }
    /**
     * Calculated the annual flow values given the average daily data provided.  This sums the daily cfs values and 
     * converts it to acre-ft/day adding each day's worth of flow to the annual total
     * @param sortedData  a String[][] containing the flow data in the following format: column1 = dates (yyyy-mm-dd format), 
     * column2 = average daily streamflow values (cubic feet per second, cfs)
     * @return
     */
    private double[][] sumAnnualFlows(String[][] sortedData){
            //Check for size issues and return early
            if(sortedData.length<=1){
                    return new double[0][2];
            }


            //Get a unique list of years along with their accumulated flow values
            ArrayList<String> uniqueYears = new ArrayList<String>();
            ArrayList<Double> uniqueFlows = new ArrayList<Double>();
            ArrayList<Integer> uniqueDays = new ArrayList<Integer>();
            uniqueYears.add(sortedData[0][0].substring(0,4));
            uniqueFlows.add((Double.parseDouble(sortedData[0][1])*24*3600)/43560);
            uniqueDays.add(1);
            int ctr = 0;

            for(int i=1; i<sortedData.length; i++){
                    String previousYear = sortedData[i-1][0].substring(0,4);
                    String currentYear = sortedData[i][0].substring(0,4);

                    if(previousYear.equalsIgnoreCase(currentYear)){
                            //If the current year matches the previous year then add its flow value to the current total and continue
                            double currentFlowTotal = uniqueFlows.remove(ctr);
                            int currentNumberOfDays = uniqueDays.remove(ctr);

                            //convert daily cfs into acre-ft contributed by one day
                            //acre-ft =  cfs * 1day * 24hr/1day * 3600sec/1hr * 1acre-ft/43560 ft^3
                            double currentFlow = (Double.parseDouble(sortedData[i][1])*24*3600)/43560;
                            uniqueFlows.add(ctr, currentFlowTotal + currentFlow);
                            uniqueDays.add(ctr, currentNumberOfDays + 1);
                    }else{
                            //If the current year does not match the previous year, then we are in a new year so add the year to the 
                            //uniqueYear list and reset the annual flow total 
                            uniqueYears.add(currentYear);
                            uniqueDays.add(1);

                            //convert daily cfs into acre-ft for one day
                            //acre-ft =  cfs * 1day * 24hr/1day * 3600sec/1hr * 1acre-ft/43560 ft^3
                            double currentFlow = (Double.parseDouble(sortedData[i][1])*24*3600)/43560;
                            uniqueFlows.add(currentFlow);
                            ctr++;
                    }
            }

            System.out.println("Total Years: " + uniqueYears.size() + "\tTotal Flows: " + uniqueFlows.size());


//	//Compute average flow/day for each year in acre-ft/day and then annualize this value for a total flow per year value
//	//This average allows some years to have minimal data but still be used in the drought analysis
//	for(int i=0; i<uniqueYears.size(); i++){
//		int totalDays = uniqueDays.remove(i);
//		if(totalDays < 365){
//			//If less than a full year's worth of flows has been added, annualize each year's flow based on an average of the available data
//			double totalFlow = uniqueFlows.remove(i);
//			String year = uniqueYears.get(i);
//			System.out.println(year + "\tOld flow: " + totalFlow + "acre-ft/day\t" + totalDays + " days");
//			
//			double averageFlowDay = totalFlow/totalDays;
//			double annualFlow = averageFlowDay*365;
//			
//			uniqueFlows.add(i, annualFlow);
//			uniqueDays.add(i, 365);
//			System.out.println(year + "\tNew flow: " + annualFlow + "acre-ft/day\t365 days");
//			
//		}else{
//			//If it is a full year of data, then don't change anything
//			uniqueDays.add(i, totalDays);
//		}
//	}



            double[][] uniqueData = new double[uniqueYears.size()][2];
            for(int i=0; i<uniqueYears.size(); i++){
                    uniqueData[i][0] = Double.parseDouble(uniqueYears.get(i));//years
                    uniqueData[i][1] = uniqueFlows.get(i);//annual flow value
            }

            return uniqueData;
    }
    /**
     * Graph the timeseries data and save the resulting graph to the specified location
     * @param mainFolder  the file path to the location of the graph (ex. "C:/temp/myFolder")
     * @param sortedData  the String[][] containing sorted data for the timeseries (column 1 = dates (yyyy-mm-dd) column 2 = values)
     * @param droughtLimit  the calculated drought limit (if supply < drought limit then that year is a drought)
     * @param graphTitle  the graph title
     */
    public void graphTimeseries(String mainFolder,
                                double[][] sortedData,
                                double droughtLimit,
                                String graphTitle){
        DurationCurve durationCurve = new DurationCurve();

        //Round the long term average to fit the data better
        double tempRounding = droughtLimit*1000;
        tempRounding = Math.round(tempRounding)/1000;

        //Create TimeSeries graph
        TimeSeries series = new TimeSeries("Annual Flow Rate");
        TimeSeries averageSeries = new TimeSeries("Drought Limit: " + String.valueOf(tempRounding) + " [acre-ft]");

        for(int i=0; i < sortedData.length; i++) {
            Year year = new Year((int) sortedData[i][0]);
            series.add(year, sortedData[i][1]);
            averageSeries.add(year,droughtLimit);
        }

        XYPlot plotTime = new XYPlot();

        //Add flow data to renderer
        TimeSeriesCollection dataset = new TimeSeriesCollection(series);
        XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
        renderer.setSeriesPaint(0, Color.blue);

        //Add droughtLimit data to renderer
        dataset.addSeries(averageSeries);
        renderer.setSeriesPaint(1, Color.black);
        renderer.setSeriesStroke(1, 
            new BasicStroke(
                3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
                1.0f, new float[] {6.0f, 0.0f}, 0.0f
            ));

        //Add data and renderer to graph
        plotTime.setDataset(0, dataset);
        plotTime.setRenderer(0, renderer);

        //Create Axes for graph
        DateAxis domainTime = new DateAxis("Year");
        ValueAxis rangeTime = new NumberAxis("Annual Flow Rate [acre-ft]");
        domainTime.setLowerMargin(0.05);
        domainTime.setUpperMargin(0.05);
        plotTime.setDomainAxis(0, domainTime);
        plotTime.setRangeAxis(0, rangeTime);
        plotTime.mapDatasetToDomainAxis(0, 0);
        plotTime.mapDatasetToRangeAxis(0, 0);

        //Set extra plot preferences
        plotTime.setOutlinePaint(Color.black);
        plotTime.setDomainGridlinePaint(Color.black);
        plotTime.setRangeGridlinePaint(Color.black);
        plotTime.setRangeMinorGridlinesVisible(true);
        plotTime.setRangeMinorGridlinePaint(Color.gray);
        setTimeAxisFonts(plotTime);

        //Create the chart with the plot and a legend
        JFreeChart chart = new JFreeChart(graphTitle, durationCurve.titleFont, plotTime, true);

        //Set legend Font
        LegendTitle legendTitle = chart.getLegend();
        legendTitle.setItemFont(durationCurve.masterFont);

        //Save resulting graph for use later
        try{
            String[] graphNames = get_all_output();
            String path = mainFolder + File.separator + graphNames[0];
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("Graph located at:\t" + path);
        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph the bar chart of the timeseries data where the values graphed ar those in sortedData and save the resulting graph to the specified location
     * @param mainFolder  the file path to the location of the graph (ex. "C:/temp/myFolder")
     * @param sortedData  the String[][] containing sorted data for the timeseries (column 1 = dates (yyyy-mm-dd) column 2 = values)
     * @param droughtLimit  the calculated drought limit (if supply < drought limit then that year is a drought)
     * @param graphTitle  the graph title
     */
    public void graphNegativeBarChart(String mainFolder, 
                                        double[][] sortedData, 
                                        double droughtLimit, 
                                        String graphTitle){
        DurationCurve durationCurve = new DurationCurve();

        //Round the long term average to fit the data better
        double tempRounding = droughtLimit*1000;
        tempRounding = Math.round(tempRounding)/1000;

        //Create TimeSeries graph
        TimeTableXYDataset dataset = new TimeTableXYDataset();
        boolean deficitFirst = false;

        for(int i=0; i < sortedData.length; i++) {
            Year year = new Year((int) sortedData[i][0]);

            if(sortedData[i][1] < droughtLimit){
                dataset.add(year, sortedData[i][1] - droughtLimit, "Deficit");
                if(i == 0){
                    //If the first year is a deficit mark it as true to get the colors for the graph right
                    deficitFirst = true;
                }
            }else{
                dataset.add(year, sortedData[i][1] - droughtLimit, "Surplus");
            }
        }

        //Define axis and properties
        NumberAxis numberaxis = new NumberAxis("Annual Deficit/Surplus of Drought Limit (" + tempRounding + " acre-ft)");
        DateAxis dateaxis = new DateAxis("Year");
        dateaxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
        dateaxis.setLowerMargin(0.01D);
        dateaxis.setUpperMargin(0.01D);


        //Define renderer properties for stacked graph
        StackedXYBarRenderer stackedxybarrenderer = new StackedXYBarRenderer(0);
        stackedxybarrenderer.setDrawBarOutline(false);
        stackedxybarrenderer.setShadowVisible(false);
        if(deficitFirst){
            stackedxybarrenderer.setSeriesPaint(0, Color.red);
            stackedxybarrenderer.setSeriesPaint(1, Color.blue);
        }else{
            stackedxybarrenderer.setSeriesPaint(0, Color.blue);
            stackedxybarrenderer.setSeriesPaint(1, Color.red);
        }

        //Graph the dataset using the renderer and create a chart
        XYPlot plot = new XYPlot(dataset, dateaxis, numberaxis, stackedxybarrenderer);

        //Set extra plot preferences
        plot.setOutlinePaint(Color.black);
        plot.setDomainGridlinePaint(Color.black);
        plot.setRangeGridlinePaint(Color.black);
        plot.setRangeMinorGridlinesVisible(true);
        plot.setRangeMinorGridlinePaint(Color.gray);
        setTimeAxisFonts(plot);

        //Create the chart with the plot
        JFreeChart chart = new JFreeChart(graphTitle, durationCurve.titleFont, plot, false);

        //Save resulting graph for use later
        try{
            String[] graphNames = get_all_output();
            String path = mainFolder + File.separator + graphNames[1];
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("Graph located at:\t" + path);
        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph the orignalData against the generatedData on a timeseries graph.  Also graphs a scatter plot of the errors between the originalData and generatedData
     * @param mainFolder  the file path to the location of the graph (ex. "C:/temp/myFolder")
     * @param orignalData  double array of the original data.  Column1 = years, column2 = annual flow values
     * @param fittedData  double array of the fitted data during the same time period as the original data.  Column1 = years, column2 = annual flow values
     * @param methodType  the name of the method used to generate and project the data to be used in the legend of the graph
     * @param graphTitle  the graph title
     */
    public void graphFittedData(String mainFolder, 
                                double[][] orignalData, 
                                double[][] fittedData, 
                                String methodType, 
                                String graphTitle){
        DurationCurve durationCurve = new DurationCurve();

        //Convert graph x = original data, y = fitted data
        XYSeries series1 = new XYSeries("Regression of Data");
        double max = 0;
        for(int i=0; i < orignalData.length; i++) {
                series1.add(orignalData[i][1], fittedData[i][1]);

                if(Double.compare(orignalData[i][1], max) > 0){
                        max = orignalData[i][1];
                }

                if(Double.compare(fittedData[i][1], max) > 0){
                        max = fittedData[i][1];
                }
        }
        XYPlot plot = new XYPlot();
        max = 1.02*max;

        //Add first series to graph
        XYDataset dataset1 = new XYSeriesCollection(series1);
        XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(false, true);
        currentRenderer.setSeriesPaint(0, Color.black);
        plot.setDataset(0, dataset1);
        plot.setRenderer(0, currentRenderer);

        //Add line of slope 1 to graph
        XYSeries series2 = new XYSeries("One to one line, Don't show in legend");
        series2.add(0,0);
        series2.add(max, max);
        XYDataset dataset2 = new XYSeriesCollection(series2);
        XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, false);
        currentRenderer2.setSeriesPaint(0, Color.lightGray);
        plot.setDataset(1, dataset2);
        plot.setRenderer(1, currentRenderer2);

        //Create the X Axis
        ValueAxis xAxis = new NumberAxis("Original Data [acre-ft/yr]");
        xAxis.setRange(0, max);
        xAxis.setVerticalTickLabels(true);
        plot.setDomainAxis(0, xAxis);

        //Create the Y Axis
        ValueAxis yAxis = new NumberAxis(methodType + " Data [acre-ft/yr]");
        yAxis.setRange(0, max);
        plot.setRangeAxis(0, yAxis);

        //Set extra plot preferences
        plot.setOutlinePaint(Color.black);
        plot.setDomainGridlinePaint(Color.black);
        plot.setRangeGridlinePaint(Color.black);
        plot.setRangeMinorGridlinesVisible(true);
        plot.setRangeMinorGridlinePaint(Color.gray);
        setAxisFonts(plot);


        //Create the chart with the plot
        JFreeChart chart = new JFreeChart("Data Correlelation for " + graphTitle, durationCurve.titleFont, plot, false);

        try{
            String[] graphNames = get_all_output();
            String path = mainFolder + File.separator + graphNames[2];
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("Graph located at:\t" + path);
        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph the orignalData against the future predictedData on a timeseries graph.
     * @param mainFolder  the file path to the location of the graph (ex. "C:/temp/myFolder")
     * @param orignalData  double array of the original data.  Column1 = years, column2 = annual flow values
     * @param predictedData  double array of the future predicted data.  Column1 = years, column2 = annual flow values
     * @param methodType  the name of the method used to generate and project the data to be used in the legend of the graph
     * @param graphTitle  the graph title
     */
    public void graphPredictedData(String mainFolder, 
                                    double[][] orignalData, 
                                    double[][] predictedData, 
                                    String methodType, 
                                    String graphTitle){
        DurationCurve durationCurve = new DurationCurve();

        //Convert series into Dates
        XYSeries series1 = new XYSeries("Original Data");
        XYSeries series2 = new XYSeries(methodType + " warm up period");
        XYSeries series3 = new XYSeries(methodType + " Predicted Data");
        for(int i=0; i < orignalData.length; i++) {
            series1.add(orignalData[i][0], orignalData[i][1]);
        }
        for(int i=0; i < predictedData.length; i++) {
            if(i<100){
                //Keep the warm up series
                series2.add(predictedData[i][0], predictedData[i][1]);
            }else{
                //Keep the final dataseries
                series3.add(predictedData[i][0], predictedData[i][1]);
            }
        }
        XYPlot plot = new XYPlot();

        //Add first series to graph
        XYDataset dataset1 = new XYSeriesCollection(series1);
        XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(true, false);
        currentRenderer.setSeriesPaint(0, Color.black);
        plot.setDataset(0, dataset1);
        plot.setRenderer(0, currentRenderer);

        //Add second series to graph
        XYDataset dataset2 = new XYSeriesCollection(series2);
        XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, false);
        currentRenderer2.setSeriesPaint(0, Color.gray);
        plot.setDataset(1, dataset2);
        plot.setRenderer(1, currentRenderer2);

        //Add second series to graph
        XYDataset dataset3 = new XYSeriesCollection(series3);
        XYItemRenderer currentRenderer3 = new XYLineAndShapeRenderer(true, false);
        currentRenderer3.setSeriesPaint(0, Color.red);
        plot.setDataset(2, dataset3);
        plot.setRenderer(2, currentRenderer3);

        //Create the X Axis
        NumberAxis xAxis = new NumberAxis("Year");
        xAxis.setRange(orignalData[0][0], predictedData[predictedData.length - 1][0]/35);
        DecimalFormat formatter = new DecimalFormat("#0");
        xAxis.setNumberFormatOverride(formatter);
        plot.setDomainAxis(0, xAxis);

        //Create the Y Axis
        ValueAxis yAxis = new NumberAxis("Annual Discharge [acre-ft/yr]");
        plot.setRangeAxis(0, yAxis);

        //Set extra plot preferences
        plot.setOutlinePaint(Color.black);
        plot.setDomainGridlinePaint(Color.black);
        plot.setRangeGridlinePaint(Color.black);
        plot.setRangeMinorGridlinesVisible(true);
        plot.setRangeMinorGridlinePaint(Color.gray);
        setAxisFonts(plot);

        //Create the chart with the plot
        JFreeChart chart = new JFreeChart("Projected Data for " + graphTitle, durationCurve.titleFont, plot, true);

        //Set legend Font
        LegendTitle legendTitle = chart.getLegend();
        legendTitle.setItemFont(durationCurve.masterFont);

        try{
            String[] graphNames = get_all_output();
            String path = mainFolder + File.separator + graphNames[3];
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("Graph located at:\t" + path);
        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graphs the drought length verses the return period for each lambda (drought deficit = lambda * droughtLimit) value on a scatter plot
     * @param mainFolder  the file path to the location of the graph (ex. "C:/temp/myFolder")
     * @param orignalData  double array of the original data.  Column1 = years, column2 = annual flow values
     * @param predictedData  double array of the future predicted data.  Column1 = years, column2 = annual flow values
     * @param graphTitle  the graph title
     */
    public void graphReturnPeriod(String mainFolder, 
                                    double[][] originalData, 
                                    double[][] predictedData, 
                                    String graphTitle){
        DoubleMath doubleMath = new DoubleMath();
        DoubleArray doubleArray = new DoubleArray();
        DurationCurve durationCurve = new DurationCurve();

        //Create xy scatter plot
        XYPlot plot = new XYPlot();

        //Add fist series to graph
        double[] lambda = {0.0};//, 0.5, 0.75, 1.0};
        Color[] colorMatrix2 = {Color.black, Color.darkGray, Color.gray, Color.lightGray};
        int ctr = 0;
        for(int i=0; i<lambda.length; i++){
            //Create and populate the lambda series
            XYSeries series2 = new XYSeries("Original Data: Lambda = " + lambda[i]);
            for(int j=0; j < originalData.length; j++){
                if(Double.compare(originalData[j][0], lambda[i]) == 0){
                    series2.add(originalData[j][2], originalData[j][1]);
                }
            }
            //Add the new series to the dataset
            XYDataset dataset2 = new XYSeriesCollection(series2);
            XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, true);
            currentRenderer2.setSeriesPaint(0, colorMatrix2[i]);
            plot.setDataset(ctr, dataset2);
            plot.setRenderer(ctr, currentRenderer2);
            ctr++;
        }


        //Add second series to graph
        double[] lambda2 = {0.0, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0};
        //Colors in the color matrix below are: purple, blue, cyan, green, gold, red, pink
        Color[] colorMatrix1 = {new Color(105, 0, 255), Color.blue, Color.cyan, Color.green, new Color(255, 135, 0), new Color(255, 0, 50), new Color(255, 0, 255, 100)};
        for(int i=0; i<lambda2.length; i++){
            //Create and populate the lambda series
            XYSeries series2 = new XYSeries("Lambda = " + lambda2[i]);
            for(int j=0; j < predictedData.length; j++){
                if(Double.compare(predictedData[j][0], lambda2[i]) == 0){
                    series2.add(predictedData[j][2], predictedData[j][1]);
                }
            }
            //Add the new series to the dataset
            XYDataset dataset2 = new XYSeriesCollection(series2);
            XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, true);
            currentRenderer2.setSeriesPaint(0, colorMatrix1[i]);
            plot.setDataset(ctr, dataset2);
            plot.setRenderer(ctr, currentRenderer2);
            ctr++;
        }

        //Create the X Axis
        ValueAxis xAxis = new NumberAxis("Drought Length [years]");
        xAxis.setRange(0, 11);
        TickUnits unit1 = new TickUnits();
        unit1.add(new NumberTickUnit(1, NumberFormat.getNumberInstance()));
        xAxis.setStandardTickUnits(unit1);
        plot.setDomainAxis(0, xAxis);

        //Create the Y Axis
        LogarithmicAxis yAxis = new LogarithmicAxis("Recurrence Interval [years]");
        yAxis.setAllowNegativesFlag(true);
        double maxValue = 1;
        try{
            maxValue = Math.log10(doubleMath.max(doubleArray.getColumn(predictedData, 1)));
            maxValue = Math.ceil(maxValue);
        }catch(ArrayIndexOutOfBoundsException e){
            maxValue = 1;
            System.err.print("No predicted data lambda values\n");
        }
        yAxis.setRange(1, Math.pow(10, maxValue));
        plot.setRangeAxis(0, yAxis);

        //Add equation explaining the term lambda
        XYTextAnnotation equation = new XYTextAnnotation(" Lambda = Drought Deficit / Drought Limit  ", 9, Math.pow(10, maxValue)*0.5);
        equation.setFont(durationCurve.masterFont);
        equation.setBackgroundPaint(Color.white);
        equation.setOutlinePaint(Color.black);
        equation.setOutlineVisible(true);
        plot.addAnnotation(equation);

        //Set extra plot preferences
        plot.setOutlinePaint(Color.black);
        plot.setDomainGridlinePaint(Color.black);
        plot.setRangeGridlinePaint(Color.black);
        plot.setRangeMinorGridlinesVisible(true);
        plot.setRangeMinorGridlinePaint(Color.gray);
        setAxisFonts(plot);

        //Create the chart with the plot
        JFreeChart chart = new JFreeChart("Drought Recurrence Intervals for " + graphTitle, durationCurve.titleFont, plot, true);

        //Set legend Font
        LegendTitle legendTitle = chart.getLegend();
        legendTitle.setItemFont(durationCurve.masterFont);

        try{
            String[] graphNames = get_all_output();
            String path = mainFolder + File.separator + graphNames[4];
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("Graph located at:\t" + path);
        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph the AR(p) AIC and BIC verses the p order of the AR(p) model
     * @param mainFolder  the folder location of the graph
     * @param aic  an array list of aic values corresponding to p=1, 2, etc.
     * @param bic  an array list of bic values corresponding to p=1, 2, etc.
     */
    public void graphAR_AIC_BIC(String mainFolder, ArrayList<Double> aic, ArrayList<Double> bic){
        DoubleMath doubleMath = new DoubleMath();
        DurationCurve durationCurve = new DurationCurve();

        //Graph AIC and BIC data vs. the p parameter of the AR(p) model
        XYSeries series1 = new XYSeries("AIC");
        XYSeries series2 = new XYSeries("BIC");
        for(int i=0; i < aic.size(); i++) {
            series1.add(i+1, aic.get(i));
            series2.add(i+1, bic.get(i));
        }
        XYPlot plot = new XYPlot();

        //Add first series to graph
        XYDataset dataset1 = new XYSeriesCollection(series1);
        XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(true, true);
        currentRenderer.setSeriesPaint(0, Color.black);
        plot.setDataset(0, dataset1);
        plot.setRenderer(0, currentRenderer);

        //Add second series to graph
        XYDataset dataset2 = new XYSeriesCollection(series2);
        XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, true);
        currentRenderer2.setSeriesPaint(0, Color.cyan);
        plot.setDataset(1, dataset2);
        plot.setRenderer(1, currentRenderer2);

        //Create the X Axis
        ValueAxis xAxis = new NumberAxis("Number of Model Parameters");
        TickUnits unit1 = new TickUnits();
        unit1.add(new NumberTickUnit(1, NumberFormat.getNumberInstance()));
        xAxis.setStandardTickUnits(unit1);
        xAxis.setRange(0.01, aic.size() + 0.99);
        plot.setDomainAxis(0, xAxis);

        //Create the Y Axis
        ValueAxis yAxis = new NumberAxis("AIC/BIC Value");
        double max = doubleMath.max(aic);
        if(max < doubleMath.max(bic)){
                max = doubleMath.max(bic);
        }
        double min = doubleMath.min(aic);
        if(min > doubleMath.min(bic)){
                min = doubleMath.min(bic);
        }
        yAxis.setRange(0.98*min, 1.02*max);
        plot.setRangeAxis(0, yAxis);

        //Create the legend inside of the graph
        LegendTitle lt = new LegendTitle(plot);
        lt.setItemFont(new Font("Dialog", Font.PLAIN, 12));
        lt.setBackgroundPaint(new Color(255, 255, 255, 255));
        lt.setFrame(new BlockBorder(Color.black));
        lt.setPosition(RectangleEdge.RIGHT);
        lt.setPosition(RectangleEdge.LEFT);
        XYTitleAnnotation ta = new XYTitleAnnotation(1, 1, lt, RectangleAnchor.TOP_RIGHT);
        plot.addAnnotation(ta);

        //Set extra plot preferences
        plot.setOutlinePaint(Color.black);
        plot.setDomainGridlinePaint(Color.black);
        plot.setRangeGridlinePaint(Color.black);
        plot.setRangeMinorGridlinesVisible(true);
        plot.setRangeMinorGridlinePaint(Color.gray);
        setAxisFonts(plot);

        //Create the chart with the plot
        JFreeChart chart = new JFreeChart("AR(p) Parameter Optimization", durationCurve.titleFont, plot, false);

        try{
            String[] graphNames = get_optimizeModel_output();
            String path = mainFolder + File.separator + graphNames[0];
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("Graph located at:\t" + path);
        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph the ARMA(p,q) AIC or BIC values verses the total number of ARMA model parameters (p+q)
     * @param mainFolder  the folder location for the graph
     * @param method  the name of the method either AIC or BIC
     * @param results  a double[][] containing the points to be plotted
     * @param graphNumber  the index of the graph either 3 (AIC) or 4 (BIC)
     */
    public void graphARMA_AIC_BIC(String mainFolder, String method, double[][] results, int graphNumber){
        DoubleMath doubleMath = new DoubleMath();
        DoubleArray doubleArray = new DoubleArray();
        DurationCurve durationCurve = new DurationCurve();

        //Add each point of the aic/bic to the graph
        XYPlot plot = new XYPlot();
        XYSeries series1 = new XYSeries(method + " Points");
        for(int i=0; i < results.length; i++){
            series1.add(results[i][0] + results[i][1], results[i][graphNumber - 1]);
        }

        //Determine what the optimal values of AIC/BIC are for each p+q combination
        XYSeries series2 = new XYSeries(method + " Optimal Front");
        ArrayList<Double> aicBIC = new ArrayList<Double>();
        ArrayList<Double> pValues = new ArrayList<Double>();
        ArrayList<Double> qValues = new ArrayList<Double>();
        for(int i=2; i<=8; i++){
            //For each "value"=p+q find all of the p+q combinations which add to this 
            //"value" and retrieve their corresponding AIC/BIC value
            ArrayList<Double> reArrangedData = new ArrayList<Double>();
            for(int j=0; j<results.length; j++){
                if(results[j][0] + results[j][1] == i){
                    reArrangedData.add(results[j][graphNumber - 1]);
                }
            }

            double currentMinimum = doubleMath.min(reArrangedData);
            series2.add(i, currentMinimum);
            for(int j=0; j<results.length; j++){
                if(Double.compare(currentMinimum,results[j][graphNumber - 1]) == 0){
                    aicBIC.add(results[j][graphNumber - 1]);
                    pValues.add(results[j][0]);
                    qValues.add(results[j][1]);
                }
            }
        }

        //Add first series to graph
        XYDataset dataset1 = new XYSeriesCollection(series1);
        XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(false, true);
        currentRenderer.setSeriesPaint(0, Color.black);
        plot.setDataset(0, dataset1);
        plot.setRenderer(0, currentRenderer);

        //Add second series to graph
        XYDataset dataset2 = new XYSeriesCollection(series2);
        XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, false);
        currentRenderer2.setSeriesStroke(0, 
            new BasicStroke(
                1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
                1.0f, new float[] {6.0f, 6.0f}, 0.0f
            ));
        currentRenderer2.setSeriesPaint(0, Color.blue);
        plot.setDataset(1, dataset2);
        plot.setRenderer(1, currentRenderer2);

        //Add highlight to best AIC/BIC value (lowest)
        XYSeries series3 = new XYSeries("Error");
        double minAICbic = doubleMath.min(aicBIC);
        String p = "", q = "";
        for(int i=0; i<aicBIC.size(); i++){
            if(Double.compare(aicBIC.get(i), minAICbic) == 0){
                p = String.valueOf(pValues.get(i));
                q = String.valueOf(qValues.get(i));
                series3.add(pValues.get(i) + qValues.get(i), minAICbic);
                break;
            }
        }

        //Create label for optimal ARMA model
        p = p.substring(0,p.indexOf("."));
        q = q.substring(0,q.indexOf("."));
        XYDataset dataset3 = new XYSeriesCollection(series3);
        XYItemRenderer currentRenderer3 = new XYLineAndShapeRenderer(false, true);
        currentRenderer3.setSeriesPaint(0, Color.black);
        currentRenderer3.setSeriesVisibleInLegend(0, false);

                //Add an outline around the point
                CircleDrawer cd = new CircleDrawer(Color.black, new BasicStroke(1.0f), null);
                double x =  series3.getX(0).doubleValue();
                double y = series3.getY(0).doubleValue();
                XYAnnotation bestBid = new XYDrawableAnnotation(x, y, 11, 11, cd);
                plot.addAnnotation(bestBid);

                //Add an arrow pointing to the point with the year of that flood
                final XYPointerAnnotation pointer = new XYPointerAnnotation("Optimal ARMA Model: p= " + p + " q= " + q, x, y, Math.PI - 0.3);
                pointer.setBaseRadius(30.0);
                pointer.setTipRadius(10.0);
                pointer.setFont(durationCurve.masterFont);
                pointer.setPaint(Color.black);
                pointer.setBackgroundPaint(Color.white);
                pointer.setOutlinePaint(Color.black);
                pointer.setTextAnchor(TextAnchor.CENTER_RIGHT);
                plot.addAnnotation(pointer);

        //Put the LDC line data, renderer, and axis into plot
        plot.setDataset(3, dataset3);
        plot.setRenderer(3, currentRenderer3);

        //Create the X Axis
        ValueAxis xAxis = new NumberAxis("Number of Model Parameters");
        TickUnits unit1 = new TickUnits();
        unit1.add(new NumberTickUnit(1, NumberFormat.getNumberInstance()));
        xAxis.setStandardTickUnits(unit1);
        xAxis.setRange(0.01, 8.99);
        plot.setDomainAxis(0, xAxis);

        //Create the Y Axis
        ValueAxis yAxis = new NumberAxis(method + " Value");
        double max = doubleMath.max(doubleArray.getColumn(results, graphNumber - 1));
        double min = doubleMath.min(doubleArray.getColumn(results, graphNumber - 1));
        yAxis.setRange(0.8*min, 1.02*max);
        plot.setRangeAxis(0, yAxis);

        //Set extra plot preferences
        plot.setOutlinePaint(Color.black);
        plot.setDomainGridlinePaint(Color.black);
        plot.setRangeGridlinePaint(Color.black);
        plot.setRangeMinorGridlinesVisible(true);
        plot.setRangeMinorGridlinePaint(Color.gray);
        setAxisFonts(plot);

        //Create the chart with the plot
        JFreeChart chart = new JFreeChart("ARMA(p, q) " + method + " Parameter Optimization", durationCurve.titleFont, plot, false);

        try{
            String[] graphNames = get_optimizeModel_output();
            String path = mainFolder + File.separator + graphNames[graphNumber-3];
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("Graph located at:\t" + path);
        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * This subfunction takes the provided plot and changes the fonts of the axis to a standardized new font
     * @param plot  the XYPlot containing the graph on which the fonts will be changed
     * @return the original plot with the modifications to the axis fonts
     */
    private XYPlot setAxisFonts(XYPlot plot){
        DurationCurve durationCurve = new DurationCurve();

        //Set Y axis fonts
        ValueAxis yAxis = plot.getRangeAxis();
        yAxis.setLabelFont(durationCurve.masterFont);
        yAxis.setTickLabelFont(durationCurve.masterFont);

        //Set X axis fonts
        ValueAxis xAxis = plot.getDomainAxis();
        xAxis.setLabelFont(durationCurve.masterFont);
        xAxis.setTickLabelFont(durationCurve.masterFont);	

        return plot;
    }
    /**
     * This subfunction takes the provided plot and changes the fonts of the axis (with the 
     * x axis being a date axis) to a standardized new font
     * @param plot  the XYPlot containing the graph on which the fonts will be changed
     * @return the original plot with the modifications to the axis fonts
     */
    private XYPlot setTimeAxisFonts(XYPlot plot){
        DurationCurve durationCurve = new DurationCurve();

        //Set Y axis fonts
        ValueAxis yAxis = plot.getRangeAxis();
        yAxis.setLabelFont(durationCurve.masterFont);
        yAxis.setTickLabelFont(durationCurve.masterFont);

        //Set X axis fonts
        DateAxis xAxis = (DateAxis) plot.getDomainAxis();
        xAxis.setLabelFont(durationCurve.masterFont);
        xAxis.setTickLabelFont(durationCurve.masterFont);	

        return plot;
    }
    /**
     * Writes out the dynamically created summary table to be displayed to the user along with the flood graph
     * @param dynamicSummary  string[] array to be written as each line of the text file
     * @param partialpath  the partial folder path of the file to be written
     * @throws IOException
     */
    private void writeSummary(String[] dynamicSummary, String partialpath) throws IOException{
            //Intialize the file writer
            String path = partialpath + File.separator + "drought_summary.txt";
            FileWriter writer =  new FileWriter(path, false);
            PrintWriter print_line = new PrintWriter(writer);
            
            //Write the summary to the file
            for(int i=0; i < dynamicSummary.length; i++) {
                    print_line.printf("%s" + "%n", 	dynamicSummary[i]);
            }
            print_line.close();
            writer.close();
            System.out.println("Text File located at:\t" + path);
    }
    /**
     * Writes out the error message, if any, for finding the file and then exits the program
     * @param error  string array to be written as each line of an error message
     * @throws IOException
     */
    public void writeError(String[] error) throws IOException{
        //Output data to text file
        String errorContents = error[0];
        for(int i=1; i<error.length; i++){
            errorContents = errorContents + "\n" + error[i];
        }
        throw new IOException("Error encountered. Please see the following message for details: \n" + errorContents);
    }
    public void run() throws IOException, InterruptedException {
        //If no date input, make it the maximum of available data
        if(beginDate == null || beginDate.equalsIgnoreCase("")){
            beginDate = "1900-01-01";
        }
        if(endDate == null || endDate.equalsIgnoreCase("")){
            // Pull current date for upper limit of data search
            DateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            Date currentDate = new Date();
            endDate = desiredDateFormat.format(currentDate);
        }

        String[][] sortableData = new String[0][2];
        if(organizationName.equalsIgnoreCase("USGS")){
            //Search for USGS flow data
            USGS_Data usgs_Data = new USGS_Data();
            sortableData = usgs_Data.USGS_read_FDC(stationID, beginDate, endDate);

            //If there is minimal flow data try getting WQ-flow data
            USGS_Data usgs_data = new USGS_Data();
            if(sortableData.length < 10){
                //Retrieve WQ data from USGS website
                String[][] WQData = usgs_data.USGS_read_LDC(stationID);
                //Extract USGS water quality code 00061 for dischage in cfs
                String[][] WQFlow1 = usgs_data.minimizeUSGSWQdata(WQData, "00061", beginDate, endDate);
                //Extract USGS water quality code 30209 for discharge test in m^3/s (cms)
                String[][] WQFlow2 = usgs_data.minimizeUSGSWQdata(WQData, "30209", beginDate, endDate);

                //Convert the m^3/s to ft^3/s
                for(int i=0; i<WQFlow2.length; i++){
                    WQFlow2[i][1] = Double.toString((Double.parseDouble(WQFlow2[i][1])*(3.2808399*3.2808399*3.2808399)));
                }

                //combine the WQ flows (cfs and converted cms) into a single variable to be used with the Flowdata
                String[][] WQDataflows = usgs_data.mergeMinimizedWQdata(WQFlow1, WQFlow2);
                //Combine flow data and WQ flow data into a variable of dates and flow values to be sorted
                sortableData = usgs_data.mergeMinimizedWQdata(sortableData, WQDataflows);
            }

        }else if(organizationName.equalsIgnoreCase("UserData")){
            //Find the user uploaded data file and uses this for a timeseries graph
            User_Data user_Data = new User_Data();
            sortableData = user_Data.readUserFile(userData, beginDate, endDate);

        }else{
            //Search for STORET peak flow data
            STORET_Data storet_Data = new STORET_Data();
            String zip_location = storet_Data.downloadSTORET(mainFolder, organizationName, stationID, "flow", beginDate, endDate);
            //Unzip results file and extract all flow data
            sortableData = storet_Data.Unzip_STORETDownloadFiles(zip_location, "flow", true);
        }
        
        //If the user wants the datasets (public and user) merged then retrieve the second dataset (user)
        String[][] sortableData_user = new String[0][0];
        if(mergeDatasets){
            User_Data user_Data = new User_Data();
            sortableData_user = user_Data.readUserFile(userData, beginDate, endDate);
            if(sortableData_user.length==0){
                String[] errorMessage = {"There is no available uploaded data for station '" + stationID + "' and the specified date range"};
                writeError(errorMessage);
            }
        }
        
        //Check if any data exists
        if (sortableData.length==0){
            String[] errorMessage = {"There is no available flow data for station '" + stationID + "' and the specified date range.", "Error: DROUGHT0001"};
            writeError(errorMessage);
        }
        
        //Sort the Data by date to remove duplicate date entries
        DurationCurve durationCurve = new DurationCurve();
        String[][] sortedData = durationCurve.removeDuplicateDates(sortableData);
        String[][] sortedData_user = durationCurve.removeDuplicateDates(sortableData_user);
        
        //Merge the two datasets (if user data is empty nothing will be merged)
        DoubleArray doubleArray = new DoubleArray();
        String[][] sortedData_combined = doubleArray.mergeData(sortedData, sortedData_user, mergeMethod);
        
        //Perform drought analysis
        String graphTitle = "Station: " + stationID + "-" + stationName + " By: USGS";// + organizationName;
        String[] droughtInfo = null;

        if(Double.compare(droughtLimit, 0) < 0){
            //If no drought limit is provided (aka -1 is provided) then calculate the drought limit = average(annual flows)
            droughtInfo = generalDroughtAnalysis(mainFolder, sortedData_combined, lambdaString, action, phiValues, thetaValues, graphTitle);
        }else{
            //If a drought limit is provided (aka greater than or equal to zero) then use this as the drought limit
            droughtInfo = generalDroughtAnalysis(mainFolder, sortedData_combined, droughtLimit, lambdaString, action, phiValues, thetaValues, graphTitle);			
        }

        //Append summary of inputs to the data summary
        this.len = String.valueOf(sortedData_combined.length);
        this.start = String.valueOf(sortedData_combined[0][0]);
        this.end = String.valueOf(sortedData_combined[sortedData_combined.length - 1][0]);
        
        writeSummary(droughtInfo, mainFolder);
    }
    public static void main(String[] args) throws IOException, InterruptedException, Exception {
        guiDrought_Model drought_Model = new guiDrought_Model();
        
        //Set Inputs
//        assert args.length > 0;
//        drought_Model.setMainFolder(args[0]);   //The output location of the graph
//        drought_Model.setFileName(args[1]);     //The name of the output graph and summary text file
//        drought_Model.setOrganizationName(args[2]); //Supervising organization of the station (only used for STORET stations)
//        drought_Model.setStationID(args[3]);    //The station ID used to retrieve the station's flow data
//        drought_Model.setStationName(args[4]);  //The station Name for the current station, used to title the graph
//        drought_Model.setBeginDate(args[5]);    //Begin date of analysis
//        drought_Model.setEndDate(args[6]);      //End date of analysis
//        drought_Model.setLambdaString(args[7]); //the value of lambda for the Box-Cox transformation or the word "optimize" to optimize this value
//        drought_Model.setAction(args[8]);       //The action to be taken for the drought analysis, either "all" for a full model, "optimizeParameters" to return only the phi and theta values for the AR/ARMA model fit, or useParameters to use the provided phi and theta parameters
//        drought_Model.setPhiValues(args[9]);    //the number of phi values desired for the AR/ARMA model or a tab-delimited string of the previously calculated phi values
//        drought_Model.setThetaValues(args[10]); //the number of theta values desired for the AR/ARMA model or a tab-delimited string of the previously calculated theta values (blank is acceptable and results in an AR(p) model instead of an ARMA(p,q) model)
//        drought_Model.setDroughtLimit(Double.parseDouble(args[11]));    //If not <0 then this represents the user specified drought limit so use it instead of calculating the long term average as the drought limit
        
        //Run Model
        drought_Model.run();
    }
}