guiTimeseries_Model.java [src/java/cfa] Revision: 785a73989e00201d9ce6064ee214199feab731c5  Date: Tue Aug 05 13:04:04 MDT 2014
package cfa;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.time.Day;
import org.jfree.data.time.Month;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.Year;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
* Last Updated: 18-July-2014
* @author Tyler Wible
* @since 24-June-2011
*/
public class guiTimeseries_Model {
    //Inputs
    String mainFolder = "C:/Projects/TylerWible/CodeDirectories/NetBeans/CSIP/data/CFA/Timeseries";
    String database = "USGS";//"CDWR";//"STORET";//"UserData";//
    String organizationName = "USGS";//"Co. Division of Water Resources";//"Colorado Dept. of Public Health & Environment";//
    String stationID = "06764880";//"CLAGRECO";//"000028";//
    String stationName = "South Platte River at Roscoe, Nebr.";//"Cache La Poudre Near Greeley";//"BIG THOMPSON R NEAR MOUTH";//
    String wqTest = "flow";//"00600        Total nitrogen, water, unfiltered, milligrams per liter, mg/L";
    String beginDate = "";
    String endDate = "";
    String timeStep = "Daily";//"Yearly";//"Monthly";//
    String method = "Max";//"Min";//"Average";//"Total";//
    double highPercentile = 0.75;
    double lowPercentile = 0.25;
    int numBins = 10;
    boolean logarithmicTF = true;
    String seasonBegin = "06-01";//"MM-dd"
    String seasonEnd = "09-30";//"MM-dd"
    String period1Begin = "";
    String period1End = "";
    String period2Begin = "";
    String period2End = "";
    String period3Begin = "";
    String period3End = "";
    boolean medianTF = false;
    String userData = "";//"Date\tFlow\n1999-04-29\t8.3\n1999-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 = "?";
    String units = "?";
    String dataSource = "?";
    double max = -1;
    double min = -1;
    double upperQuartile = -1;
    double lowerQuartile = -1;
    double median = -1;
    double mean = -1;
    double standardDeviation = -1;
    double variance = -1;
    double skew = -1;
    double coeffvar = -1;
    
    String len_period1 = "-1";
    double max_period1 = -1;
    double min_period1 = -1;
    double upperQuartile_period1 = -1;
    double lowerQuartile_period1 = -1;
    double median_period1 = -1;
    double mean_period1 = -1;
    double standardDeviation_period1 = -1;
    double variance_period1 = -1;
    double skew_period1 = -1;
    double coeffvar_period1 = -1;
    
    String len_period2 = "-1";
    double max_period2 = -1;
    double min_period2 = -1;
    double upperQuartile_period2 = -1;
    double lowerQuartile_period2 = -1;
    double median_period2 = -1;
    double mean_period2 = -1;
    double standardDeviation_period2 = -1;
    double variance_period2 = -1;
    double skew_period2 = -1;
    double coeffvar_period2 = -1;
    
    String len_period3 = "-1";
    double max_period3 = -1;
    double min_period3 = -1;
    double upperQuartile_period3 = -1;
    double lowerQuartile_period3 = -1;
    double median_period3 = -1;
    double mean_period3 = -1;
    double standardDeviation_period3 = -1;
    double variance_period3 = -1;
    double skew_period3 = -1;
    double coeffvar_period3 = -1;
    
    
    //Gets
    public File getParagraph() {
        return new File(mainFolder, "timeseries_summary.txt");
    }
    public File getFlowStatistics_summary() {
        FlowStatistics flowStats = new FlowStatistics();
        return new File(mainFolder, flowStats.getFlowStatistics_summary());
    }
    public File getTimeseriesOutput(){
        //This output file is for use with JSHighCharts
        return new File(mainFolder, "timeseries_graph.out");
    }
    public File getBoxplotOutput(){
        //This output file is for use with JSHighCharts
        return new File(mainFolder, "timeseries_boxplot.out");
    }
    public File getMonthlyTimeseriesOutput(){
        //This output file is for use with JSHighCharts
        return new File(mainFolder, "timeseries_monthlygraph.out");
    }
    public File getMonthlyBoxplotOutput(){
        //This output file is for use with JSHighCharts
        return new File(mainFolder, "timeseries_monthlyboxplot.out");
    }
    public File getTimeseriesEnvelopeOutput(){
        //This output file is for use with JSHighCharts
        return new File(mainFolder, "timeseries_envelope.out");
    }
    public File getHistogramOutput(){
        //This output file is for use with JSHighCharts
        return new File(mainFolder, "timeseries_histogram.outt");
        //Note that this file has a different extension as a flag for eRAMS to parse the first column as strings not numbers
    }
    public File getCDFoutput(){
        //This output file is for use with JSHighCharts
        return new File(mainFolder, "timeseries_cdf.out");
    }
    public String getGraph() {
        return "timeseries_graph.jpg";
    }
    public String getBoxplot() {
        return "timeseries_boxplot.jpg";
    }
    public String getTimeseriesEnvelope() {
        return "timeseries_envelope.jpg";
    }
    public String getMonthlyGraph() {
        return "timeseries_monthlygraph.jpg";
    }
    public String getHistogram() {
        return "timeseries_histogram.jpg";
    }
    public String getCDF() {
        return "timeseries_cdf.jpg";
    }
    public String getLen(){
        return len;
    }
    public String getStart(){
        return start;
    }
    public String getEnd(){
        return end;
    }
    public String getUnits(){
        return units;
    }
    public String getDataSource(){
        return dataSource;
    }
    public String getMax(){
        return String.valueOf(max);
    }
    public String getMin(){
        return String.valueOf(min);
    }
    public String getUpperQuartile(){
        return String.valueOf(upperQuartile);
    }
    public String getLowerQuartile(){
        return String.valueOf(lowerQuartile);
    }
    public String getMedian(){
        return String.valueOf(median);
    }
    public String getMean(){
        return String.valueOf(mean);
    }
    public String getStandardDeviation(){
        return String.valueOf(standardDeviation);
    }
    public String getVariance(){
        return String.valueOf(variance);
    }
    public String getSkewness(){
        return String.valueOf(skew);
    }
    public String getCoefficientOfVariation(){
        return String.valueOf(coeffvar);
    }
    public String getLen_period1(){
        return len_period1;
    }
    public String getMax_period1(){
        return String.valueOf(max_period1);
    }
    public String getMin_period1(){
        return String.valueOf(min_period1);
    }
    public String getUpperQuartile_period1(){
        return String.valueOf(upperQuartile_period1);
    }
    public String getLowerQuartile_period1(){
        return String.valueOf(lowerQuartile_period1);
    }
    public String getMedian_period1(){
        return String.valueOf(median_period1);
    }
    public String getMean_period1(){
        return String.valueOf(mean_period1);
    }
    public String getStandardDeviation_period1(){
        return String.valueOf(standardDeviation_period1);
    }
    public String getVariance_period1(){
        return String.valueOf(variance_period1);
    }
    public String getSkewness_period1(){
        return String.valueOf(skew_period1);
    }
    public String getCoefficientOfVariation_period1(){
        return String.valueOf(coeffvar_period1);
    }
    public String getLen_period2(){
        return len_period2;
    }
    public String getMax_period2(){
        return String.valueOf(max_period2);
    }
    public String getMin_period2(){
        return String.valueOf(min_period2);
    }
    public String getUpperQuartile_period2(){
        return String.valueOf(upperQuartile_period2);
    }
    public String getLowerQuartile_period2(){
        return String.valueOf(lowerQuartile_period2);
    }
    public String getMedian_period2(){
        return String.valueOf(median_period2);
    }
    public String getMean_period2(){
        return String.valueOf(mean_period2);
    }
    public String getStandardDeviation_period2(){
        return String.valueOf(standardDeviation_period2);
    }
    public String getVariance_period2(){
        return String.valueOf(variance_period2);
    }
    public String getSkewness_period2(){
        return String.valueOf(skew_period2);
    }
    public String getCoefficientOfVariation_period2(){
        return String.valueOf(coeffvar_period2);
    }
    public String getLen_period3(){
        return len_period3;
    }
    public String getMax_period3(){
        return String.valueOf(max_period3);
    }
    public String getMin_period3(){
        return String.valueOf(min_period3);
    }
    public String getUpperQuartile_period3(){
        return String.valueOf(upperQuartile_period3);
    }
    public String getLowerQuartile_period3(){
        return String.valueOf(lowerQuartile_period3);
    }
    public String getMedian_period3(){
        return String.valueOf(median_period3);
    }
    public String getMean_period3(){
        return String.valueOf(mean_period3);
    }
    public String getStandardDeviation_period3(){
        return String.valueOf(standardDeviation_period3);
    }
    public String getVariance_period3(){
        return String.valueOf(variance_period3);
    }
    public String getSkewness_period3(){
        return String.valueOf(skew_period3);
    }
    public String getCoefficientOfVariation_period3(){
        return String.valueOf(coeffvar_period3);
    }
    
    //Sets
    public void setMainFolder(String mainFolder) {
        this.mainFolder = mainFolder;
    }
    public void setDatabase(String database) {
        this.database = database;
    }
    public void setOrganizationName(String organizationName) {
        this.organizationName = organizationName;
    }
    public void setBeginDate(String beginDate) {
        this.beginDate = beginDate;
    }
    public void setEndDate(String endDate) {
        this.endDate = endDate;
    }
    public void setStationName(String stationName) {
        this.stationName = stationName;
    }
    public void setStationID(String stationID) {
        this.stationID = stationID;
    }
    public void setWQtest(String wqTest) {
        this.wqTest = wqTest;
    }
    public void setTimeStep(String timeStep) {
        this.timeStep = timeStep;
    }
    public void setMethod(String method) {
        this.method = method;
    }
    public void setHighPercentile(double highPercentile) {
        this.highPercentile = highPercentile;
    }
    public void setLowPercentile(double lowPercentile) {
        this.lowPercentile = lowPercentile;
    }
    public void setNumberOfBins(int numBins) {
        this.numBins = numBins;
    }
    public void setLogarithmicHistogramBins(boolean logarithmicTF) {
        this.logarithmicTF = logarithmicTF;
    }
    public void setSeasonBegin(String seasonBegin) {
        this.seasonBegin = seasonBegin;
    }
    public void setSeasonEnd(String seasonEnd) {
        this.seasonEnd = seasonEnd;
    }
    public void setPeriod1Begin(String period1Begin) {
        this.period1Begin = period1Begin;
    }
    public void setPeriod1End(String period1End) {
        this.period1End = period1End;
    }
    public void setPeriod2Begin(String period2Begin) {
        this.period2Begin = period2Begin;
    }
    public void setPeriod2End(String period2End) {
        this.period2End = period2End;
    }
    public void setPeriod3Begin(String period3Begin) {
        this.period3Begin = period3Begin;
    }
    public void setPeriod3End(String period3End) {
        this.period3End = period3End;
    }
    public void setMedianTF(boolean medianTF) {
        this.medianTF = medianTF;
    }
    public void setUserData(String userData) {
        this.userData = userData;
    }
    public void setMergeDatasets(boolean mergeDatasets) {
        this.mergeDatasets = mergeDatasets;
    }
    public void setMergeMethod(String mergeMethod) {
        this.mergeMethod = mergeMethod;
    }
    /**
     * Main statistics function calls other functions to calculate each statistic value then stores the results as global variables
     * @param dataList  data on which statistical values are desired
     * @param flag  a flag for which results the statistics will be stored to, either "all", "period1", "period2", or "period3"
     */
    private void CalculateStatistics(ArrayList<Double> dataList, String flag) {
        DoubleMath doubleMath = new DoubleMath();
        
        double temp1 = doubleMath.round(doubleMath.Min_Max(dataList, true),3);//Call Max function
        double temp2 = doubleMath.round(doubleMath.Min_Max(dataList, false),3);//Call Min function
        double temp3 = doubleMath.round(doubleMath.Percentile_function(dataList,0.75),3);//Call Upper Quartile function
        double temp4 = doubleMath.round(doubleMath.Percentile_function(dataList,0.25),3);//Call Lower Quartile function
        double temp5 = doubleMath.round(doubleMath.Median(dataList),3);//Call Median function
        double temp6 = doubleMath.round(doubleMath.Average(dataList),3);//Call Mean function
        double temp7 = doubleMath.round(doubleMath.StandardDeviationSample(dataList),3);//Call standard deviation function
        double temp8 = doubleMath.round(doubleMath.VarianceSample(dataList),3);//Call variance function
        double temp9 = doubleMath.round(doubleMath.SkewnessSample(dataList),8);//Call skewness function
        double temp10 = doubleMath.round(doubleMath.CoefficientOfVariation(dataList),3);//call coefficient of variation function
        
        if(flag.equalsIgnoreCase("all")){
            max = temp1;
            min = temp2;
            upperQuartile = temp3;
            lowerQuartile = temp4;
            median = temp5;
            mean = temp6;
            standardDeviation = temp7;
            variance = temp8;
            skew = temp9;
            coeffvar = temp10;
        }else if(flag.equalsIgnoreCase("period1")){
            max_period1 = temp1;
            min_period1 = temp2;
            upperQuartile_period1 = temp3;
            lowerQuartile_period1 = temp4;
            median_period1 = temp5;
            mean_period1 = temp6;
            standardDeviation_period1 = temp7;
            variance_period1 = temp8;
            skew_period1 = temp9;
            coeffvar_period1 = temp10;
        }else if(flag.equalsIgnoreCase("period2")){
            max_period2 = temp1;
            min_period2 = temp2;
            upperQuartile_period2 = temp3;
            lowerQuartile_period2 = temp4;
            median_period2 = temp5;
            mean_period2 = temp6;
            standardDeviation_period2 = temp7;
            variance_period2 = temp8;
            skew_period2 = temp9;
            coeffvar_period2 = temp10;
        }else if(flag.equalsIgnoreCase("period3")){
            max_period3 = temp1;
            min_period3 = temp2;
            upperQuartile_period3 = temp3;
            lowerQuartile_period3 = temp4;
            median_period3 = temp5;
            mean_period3 = temp6;
            standardDeviation_period3 = temp7;
            variance_period3 = temp8;
            skew_period3 = temp9;
            coeffvar_period3 = temp10;
        }
    }
    /**
     * Graph the time series and user data and save the resulting graph to the specified location
     * @param sortedData  the String[][] containing sorted data for the time series 
     * (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm if 
     * timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
     * @param sortedData_user  the String[][] containing sorted user data for the 
     * time series (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm 
     * if timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
     * @param color  the color of the merged dataset (Java.Color)
     * @param color2  the color of the user dataset (Java.Color)
     * @param showLine  a boolean, if true lines will be shown on the graph, if false only shapes for the data
     * @param yAxisTitle  the String label for the y axis of the graph
     * @param units  the units of the current graph to be used in labeling for the legend
     * @param medianTF  if true then the median value will be plotted for period analyses, if false the mean (average) will be used
     */
    private void createTimeseriesGraph(String[][] sortedData,
                                       String[][] sortedData_user,
                                       Color color,
                                       Color color2,
                                       boolean showLine,
                                       String yAxisTitle,
                                       String units,
                                       boolean medianTF) throws ParseException {
        //Change analysis period dates into Date objects
        SimpleDateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date period1Begin_date = new Date();
        Date period1End_date = new Date();
        Date period2Begin_date = new Date();
        Date period2End_date = new Date();
        Date period3Begin_date = new Date();
        Date period3End_date = new Date();
        
        if(!period1Begin.isEmpty() && !period1End.isEmpty()){
            period1Begin_date = desiredDateFormat.parse(period1Begin);
            period1End_date = desiredDateFormat.parse(period1End);
        }
        if(!period2Begin.isEmpty() && !period2End.isEmpty()){
            period2Begin_date = desiredDateFormat.parse(period2Begin);
            period2End_date = desiredDateFormat.parse(period2End);
        }
        if(!period3Begin.isEmpty() && !period3End.isEmpty()){
            period3Begin_date = desiredDateFormat.parse(period3Begin);
            period3End_date = desiredDateFormat.parse(period3End);
        }
        
        //Create TimeSeries graph of merged data
        TimeSeries series = new TimeSeries(stationID + ": Data");
        ArrayList<Double> period1 = new ArrayList<Double>();
        ArrayList<Double> period2 = new ArrayList<Double>();
        ArrayList<Double> period3 = new ArrayList<Double>();
        for(int i=0; i < sortedData.length; i++) {
            double value = Double.parseDouble(sortedData[i][1]);
            String tmpStr = sortedData[i][0];

            if(timeStep.equalsIgnoreCase("daily")){
                double d = Double.parseDouble(tmpStr.substring(8));
                double m = Double.parseDouble(tmpStr.substring(5,7));
                double y = Double.parseDouble(tmpStr.substring(0,4));
                int day =  (int)d;
                int month = (int)m;
                int year = (int)y;
                if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                    Day date = new Day(day,month,year);//day,month,year
                    series.add(date, value);
                }
                
            }else if(timeStep.equalsIgnoreCase("monthly")){
                desiredDateFormat = new SimpleDateFormat("yyyy-MM");
                double m = Double.parseDouble(tmpStr.substring(5,7));
                double y = Double.parseDouble(tmpStr.substring(0,4));
                int month = (int)m;
                int year = (int)y;
                if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                    Month date = new Month(month,year);//month,year
                    series.add(date, value);
                }
                
            }else if(timeStep.equalsIgnoreCase("yearly")){
                desiredDateFormat = new SimpleDateFormat("yyyy");
                double y = Double.parseDouble(tmpStr.substring(0,4));
                int year = (int)y;
                if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                    Year date = new Year(year);//year
                    series.add(date, value);
                }
            }
            
            //Check periods
            Date newDate = desiredDateFormat.parse(tmpStr);
            if(newDate.compareTo(period1Begin_date) >= 0 && newDate.compareTo(period1End_date) <= 0){
                period1.add(value);
            }else if(newDate.compareTo(period2Begin_date) >= 0 && newDate.compareTo(period2End_date) <= 0){
                period2.add(value);
            }else if(newDate.compareTo(period3Begin_date) >= 0 && newDate.compareTo(period3End_date) <= 0){
                period3.add(value);
            }
        }
        
        //Create Time Series graph of user data
        TimeSeries series2 = new TimeSeries(stationID + ": User Data");
        for(int i=0; i < sortedData_user.length; i++) {
            double value = Double.parseDouble(sortedData_user[i][1]);
            String tmpStr = sortedData_user[i][0];

            if(timeStep.equalsIgnoreCase("daily")){
                double d = Double.parseDouble(tmpStr.substring(8));
                double m = Double.parseDouble(tmpStr.substring(5,7));
                double y = Double.parseDouble(tmpStr.substring(0,4));
                int day =  (int)d;
                int month = (int)m;
                int year = (int)y;
                if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                    Day date = new Day(day,month,year);//day,month,year
                    series2.add(date, value);
                }
            }else if(timeStep.equalsIgnoreCase("monthly")){
                double m = Double.parseDouble(tmpStr.substring(5,7));
                double y = Double.parseDouble(tmpStr.substring(0,4));
                int month = (int)m;
                int year = (int)y;
                if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                    Month date = new Month(month,year);//month,year
                    series2.add(date, value);
                }
            }else if(timeStep.equalsIgnoreCase("yearly")){
                double y = Double.parseDouble(tmpStr.substring(0,4));
                int year = (int)y;
                if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                    Year date = new Year(year);//year
                    series2.add(date, value);
                }
            }
        }
		
        //Create renderer, and axis for timeseries graph
        Graphing graphing = new Graphing();
        XYPlot plotTime = new XYPlot();
        boolean showLegend = false;
        
        //Create user data points
        if(sortedData_user.length != 0){//only show user points if it is not zero
            plotTime = graphing.graphTimeData(plotTime, series, showLine, color, false, false, false, true, 0);
            plotTime = graphing.graphTimeData(plotTime, series2, showLine, color2, false, false, false, true, 1);
            showLegend = true;
        }else{
            plotTime = graphing.graphTimeData(plotTime, series, showLine, color, false, false, false, true, 0);
        }
        
        //Create period analysis average lines
        if(!period1Begin.isEmpty() && !period1End.isEmpty()){
            CalculateStatistics(period1,"period1");
            this.len_period1 = String.valueOf(period1.size());
            double value = mean_period1;
            String label = "Average";
            if(medianTF){
                value = median_period1;
                label = "Median";
            }
            TimeSeries periodSeries = new TimeSeries("Period 1 " + label + ": " + value + " " + units);
            
            //Set Start Point
            double d = Double.parseDouble(period1Begin.substring(8));
            double m = Double.parseDouble(period1Begin.substring(5,7));
            double y = Double.parseDouble(period1Begin.substring(0,4));
            int day =  (int)d;
            int month = (int)m;
            int year = (int)y;
            if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                Day date = new Day(day,month,year);//day,month,year
                periodSeries.add(date, value);
            }
            
            //Set End Point
            d = Double.parseDouble(period1End.substring(8));
            m = Double.parseDouble(period1End.substring(5,7));
            y = Double.parseDouble(period1End.substring(0,4));
            day =  (int)d;
            month = (int)m;
            year = (int)y;
            if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                Day date = new Day(day,month,year);//day,month,year
                periodSeries.add(date, value);
            }
            
            plotTime = graphing.graphTimeData(plotTime, periodSeries, true, Color.RED, false, false, true, true, 2);
            showLegend = true;
        }
        if(!period2Begin.isEmpty() && !period2End.isEmpty()){
            CalculateStatistics(period2,"period2");
            this.len_period2 = String.valueOf(period2.size());
            double value = mean_period2;
            String label = "Average";
            if(medianTF){
                value = median_period2;
                label = "Median";
            }
            TimeSeries periodSeries = new TimeSeries("Period 2 " + label + ": " + value + " " + units);
            
            //Set Start Point
            double d = Double.parseDouble(period2Begin.substring(8));
            double m = Double.parseDouble(period2Begin.substring(5,7));
            double y = Double.parseDouble(period2Begin.substring(0,4));
            int day =  (int)d;
            int month = (int)m;
            int year = (int)y;
            if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                Day date = new Day(day,month,year);//day,month,year
                periodSeries.add(date, value);
            }
            
            //Set End Point
            d = Double.parseDouble(period2End.substring(8));
            m = Double.parseDouble(period2End.substring(5,7));
            y = Double.parseDouble(period2End.substring(0,4));
            day =  (int)d;
            month = (int)m;
            year = (int)y;
            if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                Day date = new Day(day,month,year);//day,month,year
                periodSeries.add(date, value);
            }
            
            plotTime = graphing.graphTimeData(plotTime, periodSeries, true, Color.BLACK, false, false, true, true, 3);
            showLegend = true;
        }
        if(!period3Begin.isEmpty() && !period3End.isEmpty()){
            CalculateStatistics(period3,"period3");
            this.len_period3 = String.valueOf(period3.size());
            double value = mean_period3;
            String label = "Average";
            if(medianTF){
                value = median_period3;
                label = "Median";
            }
            TimeSeries periodSeries = new TimeSeries("Period 3 " + label + ": " + value + " " + units);
            
            //Set Start Point
            double d = Double.parseDouble(period3Begin.substring(8));
            double m = Double.parseDouble(period3Begin.substring(5,7));
            double y = Double.parseDouble(period3Begin.substring(0,4));
            int day =  (int)d;
            int month = (int)m;
            int year = (int)y;
            if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                Day date = new Day(day,month,year);//day,month,year
                periodSeries.add(date, value);
            }
            
            //Set End Point
            d = Double.parseDouble(period3End.substring(8));
            m = Double.parseDouble(period3End.substring(5,7));
            y = Double.parseDouble(period3End.substring(0,4));
            day =  (int)d;
            month = (int)m;
            year = (int)y;
            if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                Day date = new Day(day,month,year);//day,month,year
                periodSeries.add(date, value);
            }
            
            plotTime = graphing.graphTimeData(plotTime, periodSeries, true, Color.LIGHT_GRAY, false, false, true, true, 4);
            showLegend = true;
        }
      
        //Create Y Axis
        ValueAxis rangeTime = new NumberAxis(yAxisTitle);
        plotTime.setRangeAxis(0, rangeTime);
        
        //Create X Axis
        DateAxis domainTime = new DateAxis("Date");
        domainTime.setLowerMargin(0.05);
        domainTime.setUpperMargin(0.05);
        plotTime.setDomainAxis(0, domainTime);

        //Set extra plot preferences
        plotTime = graphing.setTimeAxisPreferences(plotTime);

        //Create the chart with the plot and a legend
        String graphTitle = "Time Series for " + database + " Station: " + stationID + "; " + stationName;
        JFreeChart chart = new JFreeChart(graphTitle, graphing.titleFont, plotTime, showLegend);
        
        //Set legend Font
        if(showLegend){
            LegendTitle legendTitle = chart.getLegend();
            legendTitle.setItemFont(graphing.masterFont);
        }
        
        //Save resulting graph for use later
        try{
            String path = mainFolder + File.separator + getGraph();
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("JFreeChart created properly at: " + path);

        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Creates a boxplot of the timeseries data to be displayed next to a summary of the statistics of the timeseries data
     * @param yAxisTitle  a String of the title for the Y axis of the boxplot
     */
    private void createTimeseriesBoxplot(String[][] data, String yAxisTitle){    	
    	Graphing graphing = new Graphing();
        //Create boxplot of the timeseries data
        XYPlot plot = new XYPlot();

        //Create X Axis
        ValueAxis xAxis = new NumberAxis("");
        xAxis.setRange(0, 10);
        xAxis.setLabelFont(graphing.masterFont);
        xAxis.setTickLabelFont(graphing.masterFont);
        xAxis.setTickLabelsVisible(false);
        plot.setDomainAxis(0, xAxis);
        
    	ValueAxis yAxis = new NumberAxis(yAxisTitle);
        yAxis.setLabelFont(graphing.masterFont);
        yAxis.setTickLabelFont(graphing.masterFont);
        plot.setRangeAxis(0, yAxis);

    	//Calculate and add Median to dataset
        XYSeries median_series = new XYSeries("Median");
        median_series.add(5, median);

        //Create median Line
        XYDataset median_scatter = new XYSeriesCollection(median_series);
        XYItemRenderer renderer_median = new XYLineAndShapeRenderer(false, true);
        renderer_median.setSeriesShape(0, new Rectangle2D.Double(-4.0, 0.0, 8.0, 1));//new Ellipse2D.Double(-4, -4, 8, 8));
        renderer_median.setSeriesPaint(0, Color.red);
        renderer_median.setSeriesVisibleInLegend(0, false);
        plot.setDataset(0, median_scatter);
        plot.setRenderer(0, renderer_median);


        //Create quartile Box shapes for the box plot
        //Create XYSeries for the box shape
        XYSeries shapeSeries = new XYSeries("Shape");
        shapeSeries.add(5, lowerQuartile);
        shapeSeries.add(5, upperQuartile);

        //Create the quartile rectangle shape
        XYDataset shapeDataset = new XYSeriesCollection(shapeSeries);
        XYItemRenderer renderer_shape = new XYLineAndShapeRenderer(true, false);
        Stroke thickness = new BasicStroke(10, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
        renderer_shape.setSeriesStroke(0, thickness);
        renderer_shape.setSeriesPaint(0, Color.blue);
        renderer_shape.setSeriesVisibleInLegend(0, false);
        plot.setDataset(1, shapeDataset);
        plot.setRenderer(1, renderer_shape);

        
        //Creates 1.5 * Interquartile Range (IQR) lines
        //Create XYSeries for the min-max lines
        double IQR = upperQuartile - lowerQuartile;
        double lowerLimit = lowerQuartile - 1.5*IQR;
        double upperLimit = upperQuartile + 1.5*IQR;
        if(lowerLimit < min){
            lowerLimit = min;
        }
        if(upperLimit > max){
            upperLimit = max;
        }
        XYSeries lineSeries = new XYSeries("Line");
        lineSeries.add(5, lowerLimit);
        lineSeries.add(5, upperLimit);

        //Create the 1.5*IQR lines
        XYDataset lineDataset = new XYSeriesCollection(lineSeries);
        XYItemRenderer lineRenderer = new XYLineAndShapeRenderer(true, true);
        Stroke thickness2 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
        lineRenderer.setSeriesStroke(0, thickness2);
        lineRenderer.setSeriesShape(0, new Rectangle2D.Double(-10.0, 0.0, 20.0, 1));
        lineRenderer.setSeriesPaint(0, Color.black);
        lineRenderer.setSeriesVisibleInLegend(0, false);
        plot.setDataset(2, lineDataset);
        plot.setRenderer(2, lineRenderer);
        
        //Calculate and create Outliers (# < lowerQuartile - 1.5*IQR or # > upperQuartile + 1.5*IQR)
        //Calculate and create Extreme Outliers (# < lowerQuartile - 3*IQR or # > upperQuartile + 3*IQR)
        XYSeries outliers = new XYSeries("Outliers");
        XYSeries extremeOutliers = new XYSeries("Extreme Outliers");
        for(int i=0; i<data.length; i++){
            double value = Double.parseDouble(data[i][1]);
            //Lower outliers
            if(value < (lowerQuartile - 1.5*IQR) && value > (lowerQuartile - 3*IQR)){
                outliers.add(5, value);
            }
            //Upper outliers
            if(value > (upperQuartile + 1.5*IQR) && value < (lowerQuartile + 3*IQR)){
                outliers.add(5, value);
            }

            //Extreme Lower outliers
            if(value < (lowerQuartile - 3*IQR)){
                extremeOutliers.add(5, value);
            }
            //Extreme Upper outliers
            if(value > (lowerQuartile + 3*IQR)){
                extremeOutliers.add(5, value);
            }
        }

        //Create outlier scatter
        XYDataset outlier_scatter = new XYSeriesCollection(outliers);
        XYItemRenderer renderer_outlier = new XYLineAndShapeRenderer(false, true);
        renderer_outlier.setSeriesShape(0, new Ellipse2D.Double(-2.0, 2.0, 4.0, 4.0));
        renderer_outlier.setSeriesPaint(0, Color.DARK_GRAY);
        if(outliers.isEmpty()){
            renderer_outlier.setSeriesVisibleInLegend(0, false);        	
        }
        plot.setDataset(3, outlier_scatter);
        plot.setRenderer(3, renderer_outlier);
        
        //Create extreme outlier scatter
        XYDataset extremeOutlier_scatter = new XYSeriesCollection(extremeOutliers);
        XYItemRenderer renderer_ExtremeOutlier = new XYLineAndShapeRenderer(false, true);
        renderer_ExtremeOutlier.setSeriesShape(0, new Ellipse2D.Double(-2.0, 2.0, 4.0, 4.0));
        renderer_ExtremeOutlier.setSeriesPaint(0, Color.red);
        if(extremeOutliers.isEmpty()){
            renderer_ExtremeOutlier.setSeriesVisibleInLegend(0, false);        	
        }
        plot.setDataset(4, extremeOutlier_scatter);
        plot.setRenderer(4, renderer_ExtremeOutlier);

        //Put the line on the first Domain and first Range
        plot.mapDatasetToDomainAxis(0, 0);
        plot.mapDatasetToRangeAxis(0, 0);

        //Set extra plot preferences
        plot.setOutlinePaint(Color.black);
        plot.setDomainGridlinePaint(Color.white);
        plot.setRangeGridlinePaint(Color.black);
        
        
        //Create the chart with the plot and a legend
        JFreeChart chart = new JFreeChart("Boxplot of Timeseries Data", graphing.titleFont, plot, true);

        //Save resulting graph for use later
        try{
            String path = mainFolder + File.separator + getBoxplot();
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 200, 400);
            System.out.println("JFreeChart created properly at: " + path);

        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph a histogram of the time series and user data and save the resulting graph to the specified location
     * @param sortedData  the String[][] containing sorted data for the time series 
     * (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm if 
     * timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
     * @param sortedData_user  the String[][] containing sorted user data for the 
     * time series (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm 
     * if timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
     * @param color  the color of the graph for flow (blue) or water quality (magenta)
     * @param xAxisTitle  the String label for the y axis of the graph
     */
    private void createTimeseriesHistogram(String[][] sortedData,
                                           String[][] sortedData_user,
                                           Color color,
                                           String xAxisTitle) throws IOException{
        //Initialize other classes
        Graphing graphing = new Graphing();
        DoubleMath doubleMath = new DoubleMath();
        
        //Retrieve Data
        double[] data = new double[sortedData.length];
        for(int i=0; i < sortedData.length; i++) {
            data[i] = Double.parseDouble(sortedData[i][1]);
        }
        double[] data_user = new double[sortedData_user.length];
        for(int i=0; i < sortedData_user.length; i++) {
            data_user[i] = Double.parseDouble(sortedData_user[i][1]);
        }
        
        //Determine bin type (arithmetic/logarithmic) and then range
        double[] lowerLimit = new double[numBins];
        double[] upperLimit = new double[numBins];
        if(logarithmicTF){
            if(min != 0){
                double exponent = (Math.log10(max) - Math.log10(min)) / numBins;
                for(int i=0; i<numBins; i++){
                    if(i == 0){
                        lowerLimit[i] = 0;
                    }else{
                        lowerLimit[i] = upperLimit[i-1];
                    }
                    upperLimit[i] =  min * Math.pow(10, exponent * (i+1));
                }
            }else{
                double exponent = Math.log10(max) / numBins;
                for(int i=0; i<numBins; i++){
                    if(i == 0){
                        lowerLimit[i] = 0;
                    }else{
                        lowerLimit[i] = upperLimit[i-1];
                    }
                    upperLimit[i] =  Math.pow(10, exponent * (i+1));
                }
            }
        }else{
            double interval = (max - min) / numBins;
            for(int i=0; i<numBins; i++){
                lowerLimit[i] = min + i*interval;
                upperLimit[i] = min + (i+1)*interval;
            }
        }
        
        
        //Create TimeSeries graph
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        String[][] histogramData = new String[numBins][2];
        for(int j=0; j<numBins; j++){
            //Count data within the bin range
            int count = 0;
            for(int i=0; i < sortedData.length; i++) {
                if(data[i] > lowerLimit[j] && data[i] <= upperLimit[j]){
                    count++;
                }
            }
            String categoryTitle = String.valueOf(doubleMath.round(lowerLimit[j],1)) + " to " + String.valueOf(doubleMath.round(upperLimit[j],1));
            dataset.addValue(count, "All Data", categoryTitle);
            histogramData[j][0] = categoryTitle;
            histogramData[j][1] = String.valueOf(count);
            
            if(sortedData_user.length > 0){
                //Count user data within the bin range
                count = 0;
                for(int i=0; i < sortedData_user.length; i++) {
                    if(data_user[i] > lowerLimit[j] && data[i] <= upperLimit[j]){
                        count++;
                    }
                }
                dataset.addValue(count, "User Data", categoryTitle);
            }
        }
        //Save histogram data for JHighCharts
        DoubleArray doubleArray = new DoubleArray();
        doubleArray.writeXYseries(mainFolder, histogramData, getHistogramOutput().getName());
        
        //Define renderer properties for bar graph
        BarRenderer renderer = new BarRenderer();
        renderer.setDrawBarOutline(false);
        renderer.setSeriesPaint(0, color);
        if(sortedData_user.length > 0){
            renderer.setSeriesPaint(1, Color.GRAY);
        }
        
        //Define axis and properties
        NumberAxis yAxis = new NumberAxis("Count");
        CategoryAxis xAxis = new CategoryAxis(xAxisTitle);
        xAxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN_45);
        
        //Graph the dataset using the renderer and create a chart
        CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer);
        
        //Set extra plot preferences
        graphing.setCategoryAxisPreferences(plot);
        
        //Create the chart with the plot
        String graphTitle = "Histogram for " + database + " Station: " + stationID + "; " + stationName;
        JFreeChart chart = new JFreeChart(graphTitle, graphing.titleFont, plot, false);
        
        //Save resulting graph for use later
        try{
            String path = mainFolder + File.separator + getHistogram();
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("JFreeChart created properly at: " + path);
        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph the time series verses the month/day for each year and long term average/median 
     * and user data and save the resulting graph to the specified location.
     * @param sortedData  the String[][] containing sorted data for the time series 
     * (column 1 = dates (yyyy-mm-dd), column 2 = value
     * @param yAxisTitle  the String label for the y axis of the graph
     */
    private void createTimeseriesEnvelopeGraph(String[][] sortedData,
                                               String yAxisTitle) throws IOException {
        DoubleArray doubleArray = new DoubleArray();
        DoubleMath doubleMath = new DoubleMath();
        Graphing graphing = new Graphing();
        
        //Initialize variables
        String currentYear = start.substring(0,4);
        String finalYear = end.substring(0,4);
        boolean leapYearTF = doubleArray.getLeapYearTF(Integer.parseInt(finalYear));
        String[][] graphData = new String[366][2];
        String[] dayList = new String[366];
        int seriesIndex = 0;
        
        //Plot each year and the long term average and the most recent year
        XYPlot plot = new XYPlot();
        String longTermSeriesName = "Average";
        if(medianTF) longTermSeriesName = "Median";
        TimeSeries timeSeries = new TimeSeries(longTermSeriesName);
        for(int j=1; j<=366; j++){
            ArrayList<Double> dateData = new ArrayList<Double>();
            dayList[j-1] = String.valueOf(j);
            
            for(int i=0; i<sortedData.length; i++){
                double value = Double.parseDouble(sortedData[i][1]);
                double d = Double.parseDouble(sortedData[i][0].substring(8));
                double m = Double.parseDouble(sortedData[i][0].substring(5,7));
                double y = Double.parseDouble(sortedData[i][0].substring(0,4));
                int day =  (int)d;
                int month = (int)m;
                int year = (int)y;
                Calendar currentDate = new GregorianCalendar(year,month - 1, day);
                if(currentDate.get(Calendar.DAY_OF_YEAR) == j){
                    dateData.add(value);
                }
            }
            //Calculate average/median
            double dayValue = 0;
            if(medianTF){
                dayValue = doubleMath.Median(dateData);
            }else{
                dayValue = doubleMath.Average(dateData);
            }
            
            //Save the results for this day of the year
            if(leapYearTF){
                Calendar currentDate = new GregorianCalendar(2000, 0, 1);
                currentDate.set(Calendar.DAY_OF_YEAR, j);
                Day graphDay = new Day(currentDate.get(Calendar.DAY_OF_MONTH), currentDate.get(Calendar.MONTH) + 1, Integer.parseInt(finalYear));
                timeSeries.add(graphDay, dayValue);
                graphData[j-1][0] = String.valueOf(j);
                graphData[j-1][1] = String.valueOf(dayValue);
            }else{
                if(j !=366){
                    Calendar currentDate = new GregorianCalendar(2001, 0, 1);
                    currentDate.set(Calendar.DAY_OF_YEAR, j);
                    Day graphDay = new Day(currentDate.get(Calendar.DAY_OF_MONTH), currentDate.get(Calendar.MONTH) + 1, Integer.parseInt(finalYear));
                    timeSeries.add(graphDay, dayValue);
                    graphData[j-1][0] = String.valueOf(j);
                    graphData[j-1][1] = String.valueOf(dayValue);
                }
            }
        }
        
        //Create renderer, and axis for timeseries graph
        TimeSeriesCollection currentDataset = new TimeSeriesCollection(timeSeries);
        XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
        renderer.setSeriesPaint(seriesIndex, Color.BLACK);
        renderer.setSeriesStroke(seriesIndex, 
                new BasicStroke(
                    3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
                    1.0f, new float[] {6.0f, 0.0f}, 0.0f
                ));
        
        //Set the line data, renderer, and axis into plot
        plot.setDataset(seriesIndex, currentDataset);
        plot.setRenderer(seriesIndex, renderer);
        seriesIndex++;
        seriesIndex++;//Increase series so the most recent month can be plotted second

        //Map the line to the first Domain and first Range
        plot.mapDatasetToDomainAxis(0, 0);
        plot.mapDatasetToRangeAxis(0, 0);
        
        //Graph a line for each year of monthly data in time period
        boolean moreYears = sortedData.length > 0;
        while(moreYears){
            //Get current year's data and graph it
            String[][] partialData = doubleArray.getYearsData(sortedData, currentYear);
            for(int i=0; i<partialData.length; i++){
                String month_day = partialData[i][0].substring(5);
                partialData[i][0] = finalYear + "-" + month_day;
            }
            graphing.graphSeries(plot, partialData, "daily", "", Color.lightGray, seriesIndex, false, leapYearTF);
            seriesIndex++;
            
            //Save results for output for JHighCharts
            String[] partialDayData = new String[dayList.length];
            int ctr = 0;
            for(int i=0; i<dayList.length; i++){
                partialDayData[i] = "-1";//value
                try{
                    double d = Double.parseDouble(partialData[ctr][0].substring(8));
                    double m = Double.parseDouble(partialData[ctr][0].substring(5,7));
                    double y = Double.parseDouble(partialData[ctr][0].substring(0,4));
                    int day =  (int)d;
                    int month = (int)m;
                    int year = (int)y;
                    Calendar currentDate = new GregorianCalendar(year,month - 1, day);
                    if(currentDate.get(Calendar.DAY_OF_YEAR) == (i+1)){
                        partialDayData[i] = partialData[ctr][1];//value
                        ctr++;
                    }
                }catch(IndexOutOfBoundsException e){
                    //do nothing as it already has a -1 value
                }
            }
            graphData = doubleArray.appendcolumn_Matrix(graphData, dayList);//Add list of days (aka x points)
            graphData = doubleArray.appendcolumn_Matrix(graphData, partialDayData);//Add day values (aka y points)
            
            //Determine the next data year to continue looping over
            int nextYear = Integer.parseInt(currentYear) + 1;
            if(finalYear.compareToIgnoreCase(String.valueOf(nextYear)) >= 0){
                currentYear = String.valueOf(nextYear);
            }else{
                //Re-add the most recent year to the graph so it renders on top
                graphing.graphSeries(plot, partialData, "daily", finalYear, Color.red, 1, true, leapYearTF);
                moreYears = false;
            }
        }
        
        //Output monthly boxplot and timeseries data for use with JHighCharts
        doubleArray.writeXYseries(mainFolder, graphData, getTimeseriesEnvelopeOutput().getName());
        
        //Create Y Axis
//        ValueAxis rangeAxis = new NumberAxis(yAxisTitle);
        LogarithmicAxis rangeAxis = new LogarithmicAxis("Discharge [cfs]");
        rangeAxis.setAllowNegativesFlag(true); 
        plot.setRangeAxis(0, rangeAxis);
        
        //Create X Axis
        DateAxis domainTime = new DateAxis("Date");
        domainTime.setLowerMargin(0.05);
        domainTime.setUpperMargin(0.05);
        SimpleDateFormat xlabelDateFormat = new SimpleDateFormat("MMM");
        domainTime.setDateFormatOverride(xlabelDateFormat);
        plot.setDomainAxis(0, domainTime);
        
        //Set extra plot preferences
        plot = graphing.setAxisPreferences(plot);

        //Create the charts out of the plots
        String graphTitle = "Time Series Range for " + database + " Station: " + stationID + "; " + stationName;
        JFreeChart chart = new JFreeChart(graphTitle, graphing.titleFont, plot, true);
        
        //Set legend Font
        LegendTitle legendTitle = chart.getLegend();
        legendTitle.setItemFont(graphing.masterFont);
        
        //Save monthly timeseries graph for use later
        try{
            String path = mainFolder + File.separator + getTimeseriesEnvelope();
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("JFreeChart created properly at: " + path);

        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph the monthly average time series and user data and save the resulting graph to the specified location.
     * Also creates a boxplot for each month's data in a new graph
     * @param sortedData  the String[][] containing sorted data for the time series 
     * (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm if 
     * timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
     * @param color  the color of the merged dataset (Java.Color)
     * @param yAxisTitle  the String label for the y axis of the graph
     */
    private void createTimeseriesMonthlyGraph(String[][] sortedData,
                                               String yAxisTitle) throws IOException {
        //Convert sortedData to monthly data
        DoubleArray doubleArray = new DoubleArray();
        sortedData = doubleArray.computeFlowMethod(sortedData, "Monthly", "Average", false);
        
        //Calculate monthly averages for the entire dataset
        XYPlot plot = new XYPlot();
        Graphing graphing = new Graphing();
        XYSeries series = new XYSeries(stationID + ": Data");
        ArrayList<ArrayList<Double>> boxplotOutliers = new ArrayList<ArrayList<Double>>();
        double[][] boxplotData = new double[5][12];
        String[][] monthXYdata = new String[12][2];
        boolean showOutliers = false, showExtremeOutliers = false;
        int seriesIndex = 0;
        
        for(int j=1; j<=12; j++){
            ArrayList<Double> monthlyData = new ArrayList<Double>();
            double flow = 0;
            double ctr = 0;
            
            for(int i=0; i<sortedData.length; i++){
                double month = Double.parseDouble(sortedData[i][0].substring(5));
                double value = Double.parseDouble(sortedData[i][1]);
                int month_int = (int) month;
                if(month_int == j){
                    monthlyData.add(value);
                    flow = flow + value;
                    ctr = ctr + 1;
                }
            }
            //Calculate monthly average
            if(ctr == 0){ctr = 1;}// prevent divide by zero problems
            series.add(j, flow/ctr);
            monthXYdata[j-1][0] = String.valueOf(j);
            monthXYdata[j-1][1] = String.valueOf(flow/ctr);
            
            //Create Monthly boxplot
            if(monthlyData.size() > 4){
                //Create quartile rectangle, min-max line, and median line
                Object[] returnArray = graphing.boxplot_shapes(plot, j, monthlyData, seriesIndex, showOutliers, showExtremeOutliers);
                plot = (XYPlot) returnArray[0];
                showOutliers = (boolean) returnArray[1];
                showExtremeOutliers = (boolean) returnArray[2];
                seriesIndex = (int) returnArray[3];
                ArrayList<Double> outliers = (ArrayList<Double>) returnArray[4];
                double[] currentBoxplotData = (double[]) returnArray[5];
                
                //Store additional results for use with JHighCharts
                boxplotOutliers.add(outliers);
                boxplotData[0][j-1] = currentBoxplotData[0];
                boxplotData[1][j-1] = currentBoxplotData[1];
                boxplotData[2][j-1] = currentBoxplotData[2];
                boxplotData[3][j-1] = currentBoxplotData[3];
                boxplotData[4][j-1] = currentBoxplotData[4];
            }
        }
        
        //Check to show legend on boxplot graph
        boolean showBoxplotLegend = false;
        if(showOutliers || showExtremeOutliers){
            showBoxplotLegend = true;
        }
		
        //Create renderer, and axis for timeseries graph
        XYDataset xyDataset = new XYSeriesCollection(series);
        XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
        renderer.setSeriesPaint(seriesIndex, Color.BLACK);
        renderer.setSeriesVisibleInLegend(seriesIndex, false);
        renderer.setSeriesStroke(seriesIndex, 
                new BasicStroke(
                    3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
                    1.0f, new float[] {6.0f, 0.0f}, 0.0f
                ));
        
        //Set the line data, renderer, and axis into plot
        plot.setDataset(seriesIndex, xyDataset);
        plot.setRenderer(seriesIndex, renderer);
        seriesIndex++;

        //Map the line to the first Domain and first Range
        plot.mapDatasetToDomainAxis(0, 0);
        plot.mapDatasetToRangeAxis(0, 0);
        
        //Graph a line for each year of monthly data in time period
        String[] monthList = {"1","2","3","4","5","6","7","8","9","10","11","12"};
        String currentYear = start.substring(0,4);
        String finalYear = end.substring(0,4);
        boolean moreYears = sortedData.length > 0;
        while(moreYears){
            //Get current year's data and graph it
            String[][] partialData = doubleArray.getYearsData(sortedData, currentYear);
            double[][] currentYearData = new double[partialData.length][2];
            for(int i=0; i<partialData.length; i++){
                currentYearData[i][0] = Double.parseDouble(partialData[i][0].substring(5));//month
                currentYearData[i][1] = Double.parseDouble(partialData[i][1]);//value
            }
            graphing.graphSeries(plot, currentYearData, Color.lightGray, seriesIndex);
            seriesIndex++;
            
            //Save results for output for JHighCharts
            String[] partialMonthData = new String[12];
            int ctr = 0;
            for(int i=0; i<12; i++){
                partialMonthData[i] = "-1";//value
                try{
                    double month = Double.parseDouble(partialData[ctr][0].substring(5));//month
                    int month_int = (int) month;
                    if(month_int == (i+1)){
                        partialMonthData[i] = partialData[ctr][1];//value
                        ctr++;
                    }
                }catch(IndexOutOfBoundsException e){
                    //do nothing as it already has a -1 value
                }
            }
            monthXYdata = doubleArray.appendcolumn_Matrix(monthXYdata, monthList);//Add list of months (aka x points)
            monthXYdata = doubleArray.appendcolumn_Matrix(monthXYdata, partialMonthData);//Add month values (aka y points)
            
            //Determine the next data year to continue looping over
            int nextYear = Integer.parseInt(currentYear) + 1;
            if(finalYear.compareToIgnoreCase(String.valueOf(nextYear)) >= 0){
                currentYear = String.valueOf(nextYear);
            }else{
                moreYears = false;
            }
        }
        
        //Output monthly boxplot and timeseries data for use with JHighCharts
        doubleArray.writeXYseries(mainFolder, monthXYdata, getMonthlyTimeseriesOutput().getName());
        doubleArray.writeBoxplot(mainFolder, boxplotOutliers, boxplotData, getMonthlyBoxplotOutput().getName());
        
        //Create Y Axis
        ValueAxis rangeAxis = new NumberAxis(yAxisTitle);
        plot.setRangeAxis(0, rangeAxis);
        
        //Create X Axis
        NumberTickUnit temp = new NumberTickUnit(1);
        NumberAxis domainAxis = new NumberAxis("Month");
        domainAxis.setRange(0.75, 12.25);
        domainAxis.setTickUnit(temp);
        plot.setDomainAxis(0, domainAxis);
        
        //Set extra plot preferences
        plot = graphing.setAxisPreferences(plot);

        //Create the charts out of the plots
        String graphTitle = "Monthly Averages for " + database + " Station: " + stationID + "; " + stationName;
        JFreeChart chart = new JFreeChart(graphTitle, graphing.titleFont, plot, false);
        
        //Save monthly timeseries graph for use later
        try{
            String path = mainFolder + File.separator + getMonthlyGraph();
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("JFreeChart created properly at: " + path);

        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * Graph the quantile plot CDF of the time series data and save the resulting graph to the specified location.
     * @param sortedData  the String[][] containing sorted data for the time series 
     * (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm if 
     * timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
     * @param sortedData_user  the String[][] containing sorted user data for the 
     * time series (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm 
     * if timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
     * @param color  the color of the merged dataset (Java.Color)
     * @param color2  the color of the user dataset (Java.Color)
     * @param xAxisTitle  the String label for the x axis of the graph
     */
    private void createTimeseriesCDF(String[][] sortedData,
                                     String[][] sortedData_user,
                                     Color color,
                                     Color color2,
                                     String xAxisTitle) throws ParseException, IOException {
        DoubleArray doubleArray = new DoubleArray();
        double[][] xyRanks = doubleArray.weibullPlottingPosition(sortedData);
        double[][] xyRanks_user = doubleArray.weibullPlottingPosition(sortedData_user);
        
        //Calculate monthly averages for the entire dataset
        XYSeries series = new XYSeries(stationID + ": Data");
        String[][] cdfData = new String[xyRanks.length][2];
        for(int i=0; i<xyRanks.length; i++){
            double x = xyRanks[i][1]; //flow
            double y = 1 - (xyRanks[i][0]/100); //rank
            series.add(x, y);
            cdfData[i][0] = String.valueOf(x);
            cdfData[i][1] = String.valueOf(y);
        }
        //Save cdf data for JHighCharts
        doubleArray.writeXYseries(mainFolder, cdfData, getCDFoutput().getName());
        
        
        XYSeries series_user = new XYSeries(stationID + ": User Data");
        for(int i=0; i<xyRanks_user.length; i++){
            double x = xyRanks_user[i][1]; //flow
            double y = 1 - (xyRanks_user[i][0]/100); //rank
            series_user.add(x, y);
        }
		
        //Create renderer, and axis for timeseries graph
        XYPlot plot = new XYPlot();
        XYDataset xyDataset = new XYSeriesCollection(series);
        XYDataset xyDataset_user = new XYSeriesCollection(series_user);
        XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
        XYItemRenderer renderer_user = new XYLineAndShapeRenderer(true, false);
        renderer.setSeriesPaint(0, color);
        renderer_user.setSeriesPaint(0, color2);
        
        //Set the line data, renderer, and axis into plot
        plot.setDataset(0, xyDataset);
        plot.setDataset(1, xyDataset_user);
        plot.setRenderer(0, renderer);
        plot.setRenderer(1, renderer_user);

        //Map the line to the first Domain and first Range
        plot.mapDatasetToDomainAxis(0, 0);
        plot.mapDatasetToRangeAxis(0, 0);
        
        //Create Y Axis
        ValueAxis rangeAxis = new NumberAxis("Cumulative Frequency");
        rangeAxis.setRange(0, 1);
        plot.setRangeAxis(0, rangeAxis);
        
        //Create X Axis
        LogarithmicAxis xAxis = new LogarithmicAxis(xAxisTitle);
        xAxis.setAllowNegativesFlag(true); 
        plot.setDomainAxis(0, xAxis);
        
        //Set extra plot preferences
        Graphing graphing = new Graphing();
        plot = graphing.setLogXaxisPreferences(plot);

        //Create the charts out of the plots
        String graphTitle = "CDF for " + database + " Station: " + stationID + "; " + stationName;
        JFreeChart chart = new JFreeChart(graphTitle, graphing.titleFont, plot, false);
        
        //Save monthly timeseries graph for use later
        try{
            String path = mainFolder + File.separator + getCDF();
            ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
            System.out.println("JFreeChart created properly at: " + path);

        }catch(IOException e){
            System.err.println("A problem occurred while trying to creating the chart.");
        }
    }
    /**
     * 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(ArrayList<String> error) throws IOException{
        //Output data to text file
        String errorContents = error.get(0);
        for(int i=1; i<error.size(); i++){
            errorContents = errorContents + "\n" + error.get(i);
        }
        throw new IOException("Error encountered. Please see the following message for details: \n" + errorContents);
    }
    /**
     * Primary TimeSeries
     * It calls the subfunctions based on user selection/inputs.
     * Calls STORET or USGS database queries and their respective subfunctions
     * @throws IOException 
     * @throws InterruptedException 
     */
    public void run() throws IOException, InterruptedException, ParseException, Exception {
        //Inputs
        //assert args.length > 0;
        //String mainFolder 		= args[0];
        //String fileName 		= args[1];
        //String organizationName = args[2];
        //String stationID 		= args[3];
        //String stationName		= args[4];
        //String wqTest 			= args[5];
        //String beginDate		= args[6];
        //String endDate	 		= args[7];
        //String timeStep			= args[8];
        //String method			= args[9];
        
        //If no date input, make it the maximum of available data
        if(beginDate == null || beginDate.equalsIgnoreCase("")){
            beginDate = "1850-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);
        }
        
        //Initialize graph variables
        String yAxisTitle = "y axis";
        String monthlyYaxisTitle = "y axis";
        String WQlabel = "??";
        String graphUnits = "??";
        Color color = Color.black, color2 = Color.black;
        boolean showLine = true;
        
        Data data = new Data();
        String[][] sortableData = new String[0][2];
        if(wqTest.equalsIgnoreCase("flow")){
            //Check if any flow data exists
            sortableData = data.extractFlowData(mainFolder, database, organizationName, stationID, beginDate, endDate, userData);

            //Define other graph information
            graphUnits = "cfs";
            if(timeStep.equalsIgnoreCase("Daily")){
                yAxisTitle = timeStep + " Flow [" + graphUnits + "]";
            }else{
                yAxisTitle = timeStep + " " + method + " Flow [" + graphUnits + "]";
            }
            monthlyYaxisTitle = "Monthly Average Flow [" + graphUnits + "]";
            color = Color.blue;
            color2 = Color.DARK_GRAY;
            showLine = true;
        }else{
            //Search for WQ data
            Object[] returnArray = data.extractWQdata(mainFolder, database, organizationName, stationID, beginDate, endDate, userData, wqTest);
            sortableData = (String[][]) returnArray[0];
            graphUnits = (String) returnArray[1];
            WQlabel = (String) returnArray[2];

            //Define other graph information
            if(timeStep.equalsIgnoreCase("Daily")){
                yAxisTitle = timeStep + " " + WQlabel + " [" + graphUnits + "]";
            }else{
                yAxisTitle = timeStep + " " + method + " " + WQlabel + " [" + graphUnits + "]";
            }
            monthlyYaxisTitle = "Monthly Average " + WQlabel + " [" + graphUnits + "]";
            color = Color.magenta;
            color2 = Color.BLUE;
            showLine = false;
        }
        
        //Check if merging the datasets is desired, if so get the user data
        String[][] sortableData_user = new String[0][0];
        if(mergeDatasets){
            User_Data user_Data = new User_Data();
            sortableData_user = user_Data.readUserFile(userData, wqTest, beginDate, endDate);
        }
        
        //Sort the Data by date to remove duplicate date entries
        DoubleArray doubleArray = new DoubleArray();
        String[][] sortedData = doubleArray.removeDuplicateDates(sortableData);
        String[][] sortedData_user = doubleArray.removeDuplicateDates(sortableData_user);

        //Merge the two datasets (if user data is empty nothing will be merged)
        String[][] sortedData_combined = doubleArray.mergeData(sortedData, sortedData_user, mergeMethod);
        if(sortedData_combined.length == 0){
            ArrayList<String> errorMessage = new ArrayList<String>();
            if(sortableData.length == 0){
                errorMessage.add("There is no available " + wqTest + " data in the " + database + " database for station '" + stationID + "' and the specified date range.");
                if(database.equalsIgnoreCase("CDWR")){
                    errorMessage.add("The CDWR database is sensitive to the begin date used, try specifying a later begin date");
                }
            }
            if(sortedData_user.length == 0){
                errorMessage.add("There is no available uploaded data for station '" + stationID + "' and the specified date range");
            }
            writeError(errorMessage);
        }
        
        //Save analysis results
        this.start = sortedData_combined[0][0];
        this.end = sortedData_combined[sortedData_combined.length - 1][0];
        
        //Perform operations on daily data before it is converted to the user desired timeStep
        createTimeseriesMonthlyGraph(sortedData_combined, monthlyYaxisTitle);
//        createTimeseriesEnvelopeGraph(sortedData_combined, yAxisTitle);
        FlowStatistics flowStats = new FlowStatistics();
        if(wqTest.equalsIgnoreCase("flow")){
            //Calculate Hydrologic Indicators of Alteration
            flowStats.calculateAllStatisticsSummaries(mainFolder, stationID, stationName, sortedData_combined, highPercentile, lowPercentile, 0, 0, true,
                        seasonBegin, seasonEnd, period1Begin, period1End, period2Begin, period2End, period3Begin, period3End);
        }else{
            //Report that there cannot be flow statistics for water quality data
            String[][] errorMessage = {{"Error"," Cannot compute flow statistics as indicators of hydrologic alteration using water quality data."},{"This output is only applicable for flow analysis.",""}};
            flowStats.writeStatsSummaryFile(mainFolder, errorMessage);
        }
        
        //Perform analysis method on data
        sortedData_combined = doubleArray.computeFlowMethod(sortedData_combined, timeStep, method, false);
        sortedData_user = doubleArray.computeFlowMethod(sortedData_user, timeStep, method, false);
        
        //Calculate stats of data
        ArrayList<Double> dataList = new ArrayList<Double>();
        for(int i=0; i<sortedData_combined.length; i++){
            dataList.add(Double.parseDouble(sortedData_combined[i][1]));
        }
        CalculateStatistics(dataList, "all");
        
        //Graph the timeseries data
        createTimeseriesGraph(sortedData_combined, sortedData_user, color, color2, showLine, yAxisTitle, graphUnits, medianTF);
        createTimeseriesBoxplot(sortedData_combined, yAxisTitle);
        createTimeseriesHistogram(sortedData_combined, sortedData_user, color, yAxisTitle);
        createTimeseriesCDF(sortedData_combined, sortedData_user, color, color2, yAxisTitle);
        
        //Re-Format graphing data for output for use with JHighChart
        SimpleDateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date period1Begin_date = new Date();
        Date period1End_date = new Date();
        Date period2Begin_date = new Date();
        Date period2End_date = new Date();
        Date period3Begin_date = new Date();
        Date period3End_date = new Date();
        if(!period1Begin.isEmpty() && !period1End.isEmpty()){
            period1Begin_date = desiredDateFormat.parse(period1Begin);
            period1End_date = desiredDateFormat.parse(period1End);
        }
        if(!period2Begin.isEmpty() && !period2End.isEmpty()){
            period2Begin_date = desiredDateFormat.parse(period2Begin);
            period2End_date = desiredDateFormat.parse(period2End);
        }
        if(!period3Begin.isEmpty() && !period3End.isEmpty()){
            period3Begin_date = desiredDateFormat.parse(period3Begin);
            period3End_date = desiredDateFormat.parse(period3End);
        }
        
        String[][] graphData = new String[sortedData_combined.length][5];
        SimpleDateFormat dataDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        if(timeStep.equalsIgnoreCase("Monthly")){
            dataDateFormat = new SimpleDateFormat("yyyy-MM");
        }else if(timeStep.equalsIgnoreCase("Yearly")){
            dataDateFormat = new SimpleDateFormat("yyyy");
        }
        for(int i=0; i<sortedData_combined.length; i++){
            Date newDate = dataDateFormat.parse(sortedData_combined[i][0]);
            graphData[i][0] = sortedData_combined[i][0];
            graphData[i][1] = sortedData_combined[i][1];
            
            //Period1
            if(newDate.compareTo(period1Begin_date) >= 0 && newDate.compareTo(period1End_date) <= 0){
                if(medianTF){
                    graphData[i][2] = String.valueOf(median_period1);
                }else{
                    graphData[i][2] = String.valueOf(mean_period1);
                }
            }else{
                graphData[i][2] = "-1";
            }
            
            //Period2
            if(newDate.compareTo(period2Begin_date) >= 0 && newDate.compareTo(period2End_date) <= 0){
                if(medianTF){
                    graphData[i][3] = String.valueOf(median_period2);
                }else{
                    graphData[i][3] = String.valueOf(mean_period2);
                }
            }else{
                graphData[i][3] = "-1";
            }
            
            //Period3
            if(newDate.compareTo(period3Begin_date) >= 0 && newDate.compareTo(period3End_date) <= 0){
                if(medianTF){
                    graphData[i][4] = String.valueOf(median_period3);
                }else{
                    graphData[i][4] = String.valueOf(mean_period3);
                }
            }else{
                graphData[i][4] = "-1";
            }
        }
        
        //Prep. boxplot data for output function
        ArrayList<ArrayList<Double>> boxplotOutliers = new ArrayList<ArrayList<Double>>();
        ArrayList<Double> outliers = new ArrayList<Double>();// Get daily outliers
        double IQR = upperQuartile - lowerQuartile;// Find IQR
    	for (int i = 0; i < dataList.size(); i++){
            if(dataList.get(i) < (lowerQuartile - 1.5 * IQR) || dataList.get(i) > (upperQuartile + 1.5 * IQR)){		
                outliers.add(dataList.get(i));
            }
    	}
        boxplotOutliers.add(outliers);
        double[][] boxplotData = {{max},{upperQuartile},{median},{lowerQuartile},{min}};
        
        //Write output for JHighChart boxplot and timeseries for use on eRAMS
        //output for JHighChart's monthly boxplot is performed during the JFreeChart graphing of monthly boxplot
        Arrays.sort(graphData, new DateComparator());
        doubleArray.writeTimeSeries(mainFolder, graphData, timeStep, getTimeseriesOutput().getName(), false);
        doubleArray.writeBoxplot(mainFolder, boxplotOutliers, boxplotData, getBoxplotOutput().getName());
        
        //Get today's date for the source reference
        Date currentDate = new Date();
        SimpleDateFormat sourceDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String today = sourceDateFormat.format(currentDate);
        if(database.equalsIgnoreCase("USGS")){
            this.dataSource = "Stream flow data and water quality test data courtesy of the U.S. Geological Survey, National Water Information System: Web Interface. http://waterdata.usgs.gov/nwis, accessed: " + today;
        }else if(database.equalsIgnoreCase("STORET")){
            this.dataSource = "Stream flow data and water quality test data courtesy of the U.S. Environmental Protection Agency, STORET. http://www.epa.gov/storet/index.html accessed: " + today;
        }else if(database.equalsIgnoreCase("CDWR")){
            this.dataSource = "Stream flow data courtesy of the Colorado Division of Water Resources, CDWR. http://www.dwr.state.co.us accessed: " + today;
        }
        this.len = String.valueOf(sortedData_combined.length);
        this.units = graphUnits;
    }
    public static void main(String[] args) throws IOException, InterruptedException, Exception {
        guiTimeseries_Model timeseries_Model = new guiTimeseries_Model();
        
        //Run model
        timeseries_Model.run();
    }
}