guiBaseflow_Model.java [src/java/cfa] Revision: c2e48e60977c5a4055113bdf9743acf5247d9cd1  Date: Fri Oct 16 13:50:27 MDT 2015
package cfa;

import csip.Executable;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;

/**
 * Last Updated: 23-March-2015
 * @author Tyler Wible
 * @since 15-June-2012
 */
public class guiBaseflow_Model {
    //Inputs
    String mainFolder = "C:/Projects/TylerWible/CodeDirectories/NetBeans/data/CFA/Baseflow";//"/od/projects/cfa/GUI_FlowAnalysis";
    String database = "USGS";//"CDWR";//"STORET";//"UserData";//
    String organizationName = "USGS";//"Co. Division of Water Resources";//"Colorado Dept. of Public Health & Environment";//
    String stationID = "06741510";//"CLAGRECO";//"000028";//
    String stationName = "BIG THOMPSON RIVER AT LOVELAND, CO.";//"Cache La Poudre Near Greeley";//"BIG THOMPSON R NEAR MOUTH";//
    double drainageArea = 27.3;//Square miles
    String modelType = "BFLOW";//"HYSEP";//
    String beginDate = "";//"1900-01-01";
    String endDate = "";//"2002-09-01";
    int ndmin = 10;
    int ndmax = 10;
    boolean useSTORETretrieval = false;//true;// 
    String STORETdata = "";//"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";//
    String userData =  "";//"Date\tFlow\n1999-04-29\t8.3\n1999-05-09\t60.2\n1999-05-29\t20.1";
    
    //Outputs
    String len = "-1";
    String start = "?";
    String end = "?";
    String dataSource = "?";
    double bflowStream_max = -1;
    double bflowStream_min = -1;
    double bflowStream_median = -1;
    double bflowStream_mean = -1;
    
    double bflow1_max = -1;
    double bflow1_min = -1;
    double bflow1_median = -1;
    double bflow1_mean = -1;
    
    double bflow2_max = -1;
    double bflow2_min = -1;
    double bflow2_median = -1;
    double bflow2_mean = -1;
    
    double bflow3_max = -1;
    double bflow3_min = -1;
    double bflow3_median = -1;
    double bflow3_mean = -1;

    //Gets
    public File getBaseflow_out() {
        return new File(mainFolder, "baseflow.out");
    }
    public File getBaseflow_dat() {
        return new File(mainFolder, "baseflow.dat");
    }
    public String getGraph() {
        return "baseflow_graph.jpg";
    }
    public File getTimeseriesOutput(){
        //This output file is for use with JSHighCharts
        return new File(mainFolder, "baseflow_timeseries.out");
    }
    public String getLen() {
        return len;
    }
    public String getStart() {
        return start;
    }
    public String getEnd() {
        return end;
    }
    public String getDataSource(){
        return dataSource;
    }
    public String getBFLOWstream_Max(){
        return String.valueOf(bflowStream_max);
    }
    public String getBFLOWstream_Min(){
        return String.valueOf(bflowStream_min);
    }
    public String getBFLOWstream_Median(){
        return String.valueOf(bflowStream_median);
    }
    public String getBFLOWstream_Mean(){
        return String.valueOf(bflowStream_mean);
    }
    public String getBFLOWpass1_Max(){
        return String.valueOf(bflow1_max);
    }
    public String getBFLOWpass1_Min(){
        return String.valueOf(bflow1_min);
    }
    public String getBFLOWpass1_Median(){
        return String.valueOf(bflow1_median);
    }
    public String getBFLOWpass1_Mean(){
        return String.valueOf(bflow1_mean);
    }
    public String getBFLOWpass2_Max(){
        return String.valueOf(bflow2_max);
    }
    public String getBFLOWpass2_Min(){
        return String.valueOf(bflow2_min);
    }
    public String getBFLOWpass2_Median(){
        return String.valueOf(bflow2_median);
    }
    public String getBFLOWpass2_Mean(){
        return String.valueOf(bflow2_mean);
    }
    public String getBFLOWpass3_Max(){
        return String.valueOf(bflow3_max);
    }
    public String getBFLOWpass3_Min(){
        return String.valueOf(bflow3_min);
    }
    public String getBFLOWpass3_Median(){
        return String.valueOf(bflow3_median);
    }
    public String getBFLOWpass3_Mean(){
        return String.valueOf(bflow3_mean);
    }

    //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 setStationID(String stationID) {
        this.stationID = stationID;
    }
    public void setStationName(String stationName) {
        this.stationName = stationName;
    }
    public void setDrainageArea(double drainageArea) {
        this.drainageArea = drainageArea;
    }
    public void setModelType(String modelType) {
        this.modelType = modelType;
    }
    public void setBeginDate(String beginDate) {
        this.beginDate = beginDate;
    }
    public void setEndDate(String endDate) {
        this.endDate = endDate;
    }
    public void setNDMIN(int ndmin) {
        this.ndmin = ndmin;
    }
    public void setNDMAX(int ndmax) {
        this.ndmax = ndmax;
    }
    public void setUseSTORETretrieval(boolean useSTORETretrieval) {
        this.useSTORETretrieval = useSTORETretrieval;
    }
    public void setSTORETdata(String STORETdata) {
        this.STORETdata = STORETdata;
    }
    public void setMergeDatasets(boolean mergeDatasets) {
        this.mergeDatasets = mergeDatasets;
    }
    public void setMergeMethod(String mergeMethod) {
        this.mergeMethod = mergeMethod;
    }
    public void setUserData(String userData) {
        this.userData = userData;
    }
    /**
     * Takes the input String[][] and reformats it to the desired input for
     * BFLOW (a two column tab delimited file) which is: the first column of
     * dates (format = yyyymmdd) the second column of flow values
     * @param allFlowData the String[][] output from getting USGS data from
     * "USGS_read_FDC.OpenWebpage" or STORET data from
     * @return returns the original array with the first column of yyyy-mm-dd
     * dates reformated into yyyymmdd
     */
    private String[][] reformatBFLOWdata(String[][] allFlowData) {
        //Reformat array to match BFLOW inputs
        String[][] reformatedData = new String[allFlowData.length][2];

        for (int i = 0; i < allFlowData.length; i++) {
            //Convert dates from yyyy-mm-dd format to yyyymmdd desired by BFLOW
            String currentYear = allFlowData[i][0].substring(0, 4);
            String currentMonth = allFlowData[i][0].substring(5, 7);
            String currentDay = allFlowData[i][0].substring(8, 10);

            String newDate = currentYear + currentMonth + currentDay;

            reformatedData[i][0] = newDate; //date
            reformatedData[i][1] = allFlowData[i][1]; //flow
        }

        //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 retrieved from 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 retrieved from 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 retrieved from the Colorado Division of Water Resources, CDWR. http://www.dwr.state.co.us accessed: " + today;
        }
        this.len = String.valueOf(allFlowData.length);
        this.start = allFlowData[0][0];
        this.end = allFlowData[allFlowData.length - 1][0];

        return reformatedData;
    }
    /**
     * Writes out the inputs files required for the BFLOW.exe software
     *
     * @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
     * @param ndmin the integer number of minimum baseflow days for BFLOW
     * @param ndmax the integer number of maximum baseflow days for BFLOW
     * @throws IOException
     */
    private void writeBFLOWinputFiles(String[][] dynamicSummary, int ndmin, int ndmax) throws IOException {
        //Write input data file for BFLOW
        String path = mainFolder + File.separator + "baseflow.txt";
        FileWriter writer = new FileWriter(path, false);
        PrintWriter print_line = new PrintWriter(writer);
        print_line.printf("%s" + "\r\n", "Date     Flow");
        for (int i = 0; i < dynamicSummary.length; i++) {
            print_line.printf("%s" + "\r\n", dynamicSummary[i][0] + " " + dynamicSummary[i][1]);
        }
        print_line.close();
        writer.close();
        System.out.println("Text File located at:\t" + path);

        //Additionally write out the file "baseflow.lst" required for BFLOW to 
        //know what files to run on
        String path2 = mainFolder + File.separator + "file.lst";
        FileWriter writer2 = new FileWriter(path2, false);
        PrintWriter print_line2 = new PrintWriter(writer2);
        print_line2.printf("%s" + "\r\n", "!!Input for baseflow program:");
        print_line2.printf("    %d !NDMIN: minimum number of days for alpha calculation" + "\r\n", ndmin);
        print_line2.printf("    %d !NDMAX: maximum number of days for alpha calculation" + "\r\n", ndmax);
        print_line2.printf("%s" + "\r\n", "     1 !IPRINT: daily print option (0-no; 1-yes)");
        print_line2.printf("%s" + "\r\n", "");
        print_line2.printf("%s" + "\r\n", "!!Daily stream data files");
        print_line2.printf("%s" + "\r\n", "  baseflow.txt    baseflow.out");
        print_line2.close();
        writer2.close();
        System.out.println("Text File located at:\t" + path2);
    }
    /**
     * Graph the 3-pass base-flow separation results from "BFLOW"
     * @throws IOException
     */
    private void graphBFLOWresults(String[][] sortedData_user) throws IOException{
        Graphing graphing = new Graphing();

        //Pull out results to be graphed
        String[][] baseFlowResults = readBFLOWresults(mainFolder);

        //Create TimeSeries to graph of the stream flow, baseflow1, baseflow2, and baseflow3
        TimeSeries streamflow_series = new TimeSeries("Streamflow");
        TimeSeries baseflow1_series = new TimeSeries("Base-flow Pass 1");
        TimeSeries baseflow2_series = new TimeSeries("Base-flow Pass 2");
        TimeSeries baseflow3_series = new TimeSeries("Base-flow Pass 3");
        TimeSeries streamflow_series_user = new TimeSeries("Streamflow User Data");
        TimeSeries baseflow1_series_user = new TimeSeries("Base-flow Pass 1 User Data");
        TimeSeries baseflow2_series_user = new TimeSeries("Base-flow Pass 2 User Data");
        TimeSeries baseflow3_series_user = new TimeSeries("Base-flow Pass 3 User Data");
        String[][] graphData = new String[baseFlowResults.length][5];
        int ctr = 0;
        for(int i=0; i < baseFlowResults.length; i++) {
            double streamFlow = Double.parseDouble(baseFlowResults[i][3]);
            double baseFlow1 = Double.parseDouble(baseFlowResults[i][4]);
            double baseFlow2 = Double.parseDouble(baseFlowResults[i][5]);
            double baseFlow3 = Double.parseDouble(baseFlowResults[i][6]);
            //Re-format date for graph
            double y = Double.parseDouble(baseFlowResults[i][0]);
            double m = Double.parseDouble(baseFlowResults[i][1]);
            double d = Double.parseDouble(baseFlowResults[i][2]);
            int year = (int)y;
            int month = (int)m;
            int day =  (int)d;
            if(year >= 1900){//artificial limit for JFreeCharts' "Day" class
                Day date = new Day(day,month,year);//day,month,year
                streamflow_series.add(date, streamFlow);
                baseflow1_series.add(date, baseFlow1);
                baseflow2_series.add(date, baseFlow2);
                baseflow3_series.add(date, baseFlow3);
            }
            
            //Create a date string of the current date to compare against dates of user data
            String date_str = String.valueOf(year);
            if(month < 10){
                date_str = date_str + "-0" + String.valueOf(month);
            }else{
                date_str = date_str + "-" + String.valueOf(month);                
            }
            if(day < 10){
                date_str = date_str + "-0" + String.valueOf(day);
            }else{
                date_str = date_str + "-" + String.valueOf(day);
            }
            
            //Save results for use with JHighCharts
            graphData[i][0] = String.valueOf(date_str);
            graphData[i][1] = String.valueOf(streamFlow);
            graphData[i][2] = String.valueOf(baseFlow1);
            graphData[i][3] = String.valueOf(baseFlow2);
            graphData[i][4] = String.valueOf(baseFlow3);
            
            //Create an XYSeries only if user data is not empty
            if(sortedData_user.length > 0 && sortedData_user.length > ctr){
                String userDate = sortedData_user[ctr][0];
                if(date_str.equalsIgnoreCase(userDate) && year >= 1900){//artificial limit for JFreeCharts' "Day" class
                    Day date = new Day(day,month,year);//day,month,year
                    streamflow_series_user.add(date, streamFlow);
                    baseflow1_series_user.add(date, baseFlow1);
                    baseflow2_series_user.add(date, baseFlow2);
                    baseflow3_series_user.add(date, baseFlow3);
                    ctr++;
                }
            }
        }
        
        //Save statistics of the baseflow
        calculateBFLOWstats(graphData);
        
        //Save results for graphing with JSHighcharts
        DoubleArray doubleArray = new DoubleArray();
        doubleArray.writeTimeSeries(mainFolder, graphData, "Daily", getTimeseriesOutput().getName(), false);

        //Graph the baseflow on a timeseries axis
        XYPlot plotTime = new XYPlot();

        if(sortedData_user.length != 0){//only show user points if it is not zero
            plotTime = graphing.graphTimeData(plotTime, streamflow_series, true, Color.lightGray, false, false, false, true, 7);
            plotTime = graphing.graphTimeData(plotTime, baseflow1_series, true, Color.gray, true, true, false, true, 6);
            plotTime = graphing.graphTimeData(plotTime, baseflow2_series, true, Color.darkGray, false, false, false, true, 5);
            plotTime = graphing.graphTimeData(plotTime, baseflow3_series, true, Color.black, true, true, false, true, 4);
            
            //Create user data points
            plotTime = graphing.graphTimeData(plotTime, streamflow_series_user, false, Color.lightGray, false, false, false, true, 3);
            plotTime = graphing.graphTimeData(plotTime, baseflow1_series_user, false, Color.gray, false, false, false, true, 2);
            plotTime = graphing.graphTimeData(plotTime, baseflow2_series_user, false, Color.darkGray, false, false, false, true, 1);
            plotTime = graphing.graphTimeData(plotTime, baseflow3_series_user, false, Color.black, false, false, false, true, 0);
        }else{
            //Create data points
            plotTime = graphing.graphTimeData(plotTime, streamflow_series, true, Color.lightGray, false, false, false, true, 3);
            plotTime = graphing.graphTimeData(plotTime, baseflow1_series, true, Color.gray, true, true, false, true, 2);
            plotTime = graphing.graphTimeData(plotTime, baseflow2_series, true, Color.darkGray, false, false, false, true, 1);
            plotTime = graphing.graphTimeData(plotTime, baseflow3_series, true, Color.black, true, true, false, true, 0);
        }
        
        //Define Y Axis
        ValueAxis rangeTime = new NumberAxis("Flow [cfs]");
        rangeTime.setRange(0, bflow1_max);
        plotTime.setRangeAxis(0, rangeTime);

        //Define X Axis
        DateAxis domainTime = new DateAxis("Date");
        domainTime.setLowerMargin(0.03);
        domainTime.setUpperMargin(0.03);
        plotTime.setDomainAxis(0, domainTime);

        //Set other graph preferences
        graphing.setTimeAxisPreferences(plotTime);

        //Graph plot onto JfreeChart
        String graphTitle = "BFLOW Base-flow Separation for " + database + " Station " + stationID + "; " + stationName;
        JFreeChart parentChart = new JFreeChart(graphTitle, graphing.titleFont, plotTime, true);

        //Set legend Font
        LegendTitle legendTitle = parentChart.getLegend();
        legendTitle.setItemFont(graphing.masterFont);

        //Save resulting graph
        try {
            guiBaseflow_Model model = new guiBaseflow_Model();
            String path = mainFolder + File.separator + model.getGraph();
            ChartUtilities.saveChartAsJPEG(new File(path), parentChart, 1280, 800);
            System.out.println("JFreeChart created properly at: " + path);

        } catch (IOException e) {
            System.out.println("Problem occurred creating chart");
            System.out.println(e);
        }
    }
    /**
     * Reads the output file of BFLOW and reformats it to be used in later functions
     * @param path  the file location of the output file
     * @param fileName  the name of the output file
     * @return  a String[][] of the results of the BFLOW analysis where:
     * the first column contains the year of the date, the second column the month, third column the day, 
     * fourth column the initial streamflow value, the fifth column the 1st baseflow analysis pass, the 
     * sixth column the 2nd baseflow analysis pass, and the seventh column the 3rd baseflow analysis pass
     * @throws IOException
     */
    private String[][] readBFLOWresults(String path) throws IOException{
        //Open a reader for the results file
        FileReader file_to_read = new FileReader(path + File.separator + "baseflow.out");
        BufferedReader reader = new BufferedReader(file_to_read);

        String currentLine;
        int ctr = 0;
        ArrayList<String> resultList = new ArrayList<String>();

        //Read out the contents of the results file
        while((currentLine = reader.readLine()) !=null){
            if(ctr >= 2){//Ignore the first two rows of headers
                String[] columns = currentLine.split(",");
                String year = columns[0];
                String month = columns[1];
                String day = columns[2];

                if(!year.equalsIgnoreCase("   0") && !month.equalsIgnoreCase(" 0") && !day.equalsIgnoreCase(" 0")){
                    resultList.add(currentLine);
                    //System.out.println(currentLine);
                }
            }
            ctr++;
        }
        reader.close();
        file_to_read.close();

        String[][] resultArray = new String[resultList.size()][7];
        Iterator<String> iterate = resultList.iterator();
        ctr = 0;
        while(iterate.hasNext()){
            String[] columns = iterate.next().split(",");
            //System.out.println(ctr + "\t" + currentLine);

            //substring out the results of year, month, day, Streamflow, Baseflow Pass1, Baseflow Pass2, Baseflow Pass3 
            //based on csv columns
            //dates
            String year = columns[0];
            String month = columns[1];
            String day = columns[2];
            
            //flows
            String streamFlow = columns[3];
            String baseFlow1 = columns[4];
            String baseFlow2 = columns[5];
            String baseFlow3 = columns[6];

            //Trim off extra white space characters
            year = year.trim();
            month = month.trim();
            day = day.trim();

            streamFlow = streamFlow.trim();
            baseFlow1 = baseFlow1.trim();
            baseFlow2 = baseFlow2.trim();
            baseFlow3 = baseFlow3.trim();

            //Add values to the result array
            resultArray[ctr][0] = year;
            resultArray[ctr][1] = month;
            resultArray[ctr][2] = day;
            resultArray[ctr][3] = streamFlow;
            resultArray[ctr][4] = baseFlow1;
            resultArray[ctr][5] = baseFlow2;
            resultArray[ctr][6] = baseFlow3;

            ctr++;
        }

        return resultArray;
    }
    private void calculateBFLOWstats(String[][] graphData){
        DoubleMath doubleMath = new DoubleMath();
        
        //Calculate the statistics for each baseflow filter pass
        this.bflowStream_max = doubleMath.round(doubleMath.max(getColumn(graphData,1)),3);
        this.bflowStream_min = doubleMath.round(doubleMath.min(getColumn(graphData,1)),3);
        this.bflowStream_median = doubleMath.round(doubleMath.median(getColumn(graphData,1)),3);
        this.bflowStream_mean = doubleMath.round(doubleMath.meanArithmetic(getColumn(graphData,1)),3);
        
        this.bflow1_max = doubleMath.round(doubleMath.max(getColumn(graphData,2)),3);
        this.bflow1_min = doubleMath.round(doubleMath.min(getColumn(graphData,2)),3);
        this.bflow1_median = doubleMath.round(doubleMath.median(getColumn(graphData,2)),3);
        this.bflow1_mean = doubleMath.round(doubleMath.meanArithmetic(getColumn(graphData,2)),3);
        
        this.bflow2_max = doubleMath.round(doubleMath.max(getColumn(graphData,3)),3);
        this.bflow2_min = doubleMath.round(doubleMath.min(getColumn(graphData,3)),3);
        this.bflow2_median = doubleMath.round(doubleMath.median(getColumn(graphData,3)),3);
        this.bflow2_mean = doubleMath.round(doubleMath.meanArithmetic(getColumn(graphData,3)),3);
        
        this.bflow3_max = doubleMath.round(doubleMath.max(getColumn(graphData,4)),3);
        this.bflow3_min = doubleMath.round(doubleMath.min(getColumn(graphData,4)),3);
        this.bflow3_median = doubleMath.round(doubleMath.median(getColumn(graphData,4)),3);
        this.bflow3_mean = doubleMath.round(doubleMath.meanArithmetic(getColumn(graphData,4)),3);
    }
    /**
     * Creates a double[] array of the elements in dataArray[all][column]
     * @param dataArray  the string[][] data array
     * @param column  the desired column from data array (zero based)
     * @return  a double[] array of the elements in dataArray[all][column]
     */
    private double[] getColumn(String[][] dataArray, int column){
        //Gets the values of dataArray[all][column] in a double[]
        double[] currentElements = new double[dataArray.length];

        for(int i=0; i<dataArray.length; i++){
            currentElements[i] = Double.parseDouble(dataArray[i][column]);
        }
        return currentElements;
    }
    /**
     * Takes the input String[][] and reformats it to the desired input for
     * BFLOW (a two column tab delimited file) which is: the first column of
     * dates (format = yyyymmdd) the second column of flow values
     * @param allFlowData the String[][] output from getting USGS data from
     * "USGS_read_FDC.OpenWebpage" or STORET data from
     * @param drainageArea the drainage area (in square miles) of the streamflow
     * station of interest. This value is needed because '.gsd' (WATSTORE) files
     * are stream flow records per drainage area values
     * @return returns an ArrayList<String> reformatted into a watstore file
     * input
     */
    private ArrayList<String> reformatHYSEPdata(String[][] allFlowData, double drainageArea) {
        //Pre-process a few key points for input into the input file
        int numberOfYears = 1;
        int startYear = 1;
        if (allFlowData.length > 1) {
            startYear = Integer.parseInt(allFlowData[0][0].substring(0, 4));
            String endYear = allFlowData[allFlowData.length - 1][0].substring(0, 4);
            numberOfYears = Integer.parseInt(endYear) - startYear + 1;
        }

        //Reformat array to create a USGS ".gsd" file (which will then be passed to USGS' ANNIE program to create a type input
        ArrayList<String> reformatedData = new ArrayList<>();

        //Header?
        //  ? stationID                                        drainage area (mi^2)
        //  ? stationID     station name
        //  ? stationID                 ??????????                ???
        reformatedData.add("H 01472157                                         " + drainageArea);
        reformatedData.add("N 01472157      Fake Station Name and ID");
        reformatedData.add("2 01472157                  0006000003                ENT");//This line is the separater for years?

        int ctr = 0;
        int currentYear = Integer.parseInt(allFlowData[ctr][0].substring(0, 4));
        int currentMonth = Integer.parseInt(allFlowData[ctr][0].substring(5, 7));
        int currentDay = Integer.parseInt(allFlowData[ctr][0].substring(8, 10));
        for (int i = startYear; i < (startYear + numberOfYears); i++) {//Loop years for gsd(WATSTORE) file
            for (int j = 1; j <= 12; j++) {//loop months
                int[] maxDays = new int[4];
                switch (j) {//Determine the number of days per 'week' in each month (gsd/WATSTORE files use an 8 or less day week to allow each month to have exactly 4 'weeks' keeping a consistent number of lines per year in the file)
                    case 1:
                        int[] jan = {8, 8, 8, 7};
                        maxDays = jan;
                        break;
                    case 2:
                        int[] feb = checkFebLeapYear(i);
                        maxDays = feb;
                        break;
                    case 3:
                        int[] mar = {8, 8, 8, 7};
                        maxDays = mar;
                        break;
                    case 4:
                        int[] apr = {8, 8, 8, 6};
                        maxDays = apr;
                        break;
                    case 5:
                        int[] may = {8, 8, 8, 7};
                        maxDays = may;
                        break;
                    case 6:
                        int[] jun = {8, 8, 8, 6};
                        maxDays = jun;
                        break;
                    case 7:
                        int[] jul = {8, 8, 8, 7};
                        maxDays = jul;
                        break;
                    case 8:
                        int[] aug = {8, 8, 8, 7};
                        maxDays = aug;
                        break;
                    case 9:
                        int[] sep = {8, 8, 8, 6};
                        maxDays = sep;
                        break;
                    case 10:
                        int[] oct = {8, 8, 8, 7};
                        maxDays = oct;
                        break;
                    case 11:
                        int[] nov = {8, 8, 8, 6};
                        maxDays = nov;
                        break;
                    case 12:
                        int[] dec = {8, 8, 8, 7};
                        maxDays = dec;
                        break;
                    default:
                        break;
                }
                int dayCounter = 1;
                for (int k = 1; k <= 4; k++) {//loop "weeks" in month
                    String month = String.valueOf(j);
                    if (j < 10) {
                        month = "0" + String.valueOf(j);
                    }
                    String week = "3 01472157      " + i + month + "0" + k;
                    for (int l = 1; l <= maxDays[k - 1]; l++) {//loop days in "week"
                        if (currentYear == i && currentMonth == j && currentDay == dayCounter) {//If the dataset's current date is the date expected then use its corresponding flow value
                            String flowString = shortenHYSEPflowString(allFlowData[ctr][1]);
                            week = week + flowString;

                            //Move to the next day in the dataset if it exists
                            ctr++;
                            if (ctr < allFlowData.length) {
                                currentYear = Integer.parseInt(allFlowData[ctr][0].substring(0, 4));
                                currentMonth = Integer.parseInt(allFlowData[ctr][0].substring(5, 7));
                                currentDay = Integer.parseInt(allFlowData[ctr][0].substring(8, 10));
                            }

                        } else {//Otherwise put in a blank
//                            week = week + "     0.0";
                            week = week + "-9999.0";
                        }
                        dayCounter++;
                    }
                    //Add this "week" to the data file
                    reformatedData.add(week);
                }
            }
            //Add file delimiter for end of year
            if (i + 1 < (startYear + numberOfYears)) {
                reformatedData.add("2 01472157                  0006000003                ENT");
            }
        }

        //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 retrieved from 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 retrieved from 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 retrieved from the Colorado Division of Water Resources, CDWR. http://www.dwr.state.co.us accessed: " + today;
        }else{
            this.dataSource = "Stream flow data provided by the user. Flow analysis accessed: " + today;
        }
        this.len = String.valueOf(allFlowData.length);
        this.start = allFlowData[0][0];
        this.end = allFlowData[allFlowData.length - 1][0];

        return reformatedData;
    }
    /**
     * Determines if the provided year (as an integer) is a leap year or not
     * @param currentYear the current year
     * @return an integer[] containing the number of days in each of the 4
     * 'weeks' of february (a WATSTORE file 'week')
     */
    private int[] checkFebLeapYear(int currentYear) {
        //Determine how many days February should have based on if it is a leap year or not
        DoubleArray doubleArray = new DoubleArray();
        boolean leapYear = doubleArray.getLeapYearTF(currentYear);

        //Based on the leap year or not determine the number of days in February
        int[] feb = {8, 8, 8, 4};
        if (leapYear) {
            feb[0] = 8;
            feb[1] = 8;
            feb[2] = 8;
            feb[3] = 5;
        }
        return feb;
    }
    /**
     * Shortens the provided flow value string, while trying to minimize error
     * in reducing the decimals
     * @param currentFlowValue a String.valueOf( current stream flow value )
     * @return the above current flow value with a length = 6 as to meet the
     * formatting requirements of USGS' WATSTORE file format
     */
    private String shortenHYSEPflowString(String currentFlowValue) {
        if(currentFlowValue.length() > 7){
            //Determine if there is sufficient decimals that can be dropped from the flow while retaining accuracy
            int index = currentFlowValue.indexOf(".");
            if(-1 < index && index <= 7){
                currentFlowValue = currentFlowValue.substring(0, index);
            }else{
                System.err.println("Couldn't shorten flow value: " + currentFlowValue + " properly.");
                currentFlowValue = currentFlowValue.substring(0, 7);
            }

        }else if(currentFlowValue.length() > 6) {
            currentFlowValue = "" + currentFlowValue;
        }else if(currentFlowValue.length() > 5) {
            currentFlowValue = " " + currentFlowValue;
        }else if(currentFlowValue.length() > 4) {
            currentFlowValue = "  " + currentFlowValue;
        }else if(currentFlowValue.length() > 3) {
            currentFlowValue = "   " + currentFlowValue;
        }else if(currentFlowValue.length() > 2) {
            currentFlowValue = "    " + currentFlowValue;
        }else if(currentFlowValue.length() > 1) {
            currentFlowValue = "     " + currentFlowValue;
        }else{
            currentFlowValue = "      " + currentFlowValue;
        }

        System.out.println(currentFlowValue);
        return currentFlowValue;
    }
    /**
     * @param fileContents ArrayList<String> array to be written as each line of
     * the text file
     * @throws IOException
     */
    private void writeHYSEPinputFile(ArrayList<String> fileContents) throws IOException {
        String path = mainFolder + File.separator + "hysep.gsd";
        FileWriter write = new FileWriter(path, false);
        PrintWriter print_line = new PrintWriter(write);

        for (int i = 0; i < fileContents.size(); i++) {
            print_line.printf("%s" + "\r\n", fileContents.get(i));
        }
        print_line.close();
        System.out.println("Text File located at:\t" + path);
    }
    /**
     * Graph the hydrograph separation results from "HYSEP
     * @throws IOException
     */
    private void graphHYSEPresults() throws IOException{
        Graphing graphing = new Graphing();


        //Pull out results to be graphed
        String[][] baseFlowResults = readHYSEPresults(mainFolder, ".bsf");//hysep.bsf
        String[][] streamFlowResults = readHYSEPresults(mainFolder, ".sro");//hysep.sro


        //Create TimeSeries to graph of the stream flow, baseflow and total flow to be graphed
        TimeSeries totalFlow_series = new TimeSeries("Total Stream flow");
        TimeSeries streamflow_series = new TimeSeries("Stream flow");
        TimeSeries baseflow_series = new TimeSeries("Base-flow");
        String[][] graphData = new String[baseFlowResults.length][4];
        int day = 1, month = 1, year = 1900;
        double d=1, m=1, y=1;
        for(int i=0; i < baseFlowResults.length; i++) {
            double streamFlow = Double.parseDouble(streamFlowResults[i][3]);
            double baseFlow = Double.parseDouble(baseFlowResults[i][3]);

            //Re-format date for graph
            y = Double.parseDouble(baseFlowResults[i][0]);
            m = Double.parseDouble(baseFlowResults[i][1]);
            d = Double.parseDouble(baseFlowResults[i][2]);
            year = (int)y;
            month = (int)m;
            day =  (int)d;
            Day date = new Day(day,month,year);//day,month,year
            streamflow_series.add(date, streamFlow);
            baseflow_series.add(date, baseFlow);
            totalFlow_series.add(date, streamFlow + baseFlow);
            
            //Create a date string of the current date to compare against dates of user data
            String date_str = String.valueOf(year);
            if(month < 10){
                date_str = date_str + "-0" + String.valueOf(month);
            }else{
                date_str = date_str + "-" + String.valueOf(month);                
            }
            if(day < 10){
                date_str = date_str + "-0" + String.valueOf(day);
            }else{
                date_str = date_str + "-" + String.valueOf(day);
            }
            
            //Save results for use with JHighCharts
            graphData[i][0] = String.valueOf(date_str);
            graphData[i][1] = String.valueOf(streamFlow + baseFlow);
            graphData[i][2] = String.valueOf(streamFlow);
            graphData[i][3] = String.valueOf(baseFlow);
        }
        
        //Save results for graphing with JSHighcharts
        DoubleArray doubleArray = new DoubleArray();
        doubleArray.writeTimeSeries(mainFolder, graphData, "Daily", getTimeseriesOutput().getName(), false);

        //Graph the baseflow on a timeseries axis
        XYPlot plotTime = new XYPlot();
        plotTime = graphing.graphTimeData(plotTime, totalFlow_series, true, Color.black, false, false, false, true, 2);
//      plotTime = graphTimeData(plotTime, streamflow_series, true, Color.lightGray, false, false, true, 1);
        plotTime = graphing.graphTimeData(plotTime, baseflow_series, true, Color.gray, false, false, false, true, 0);

        //Define Y Axis
        ValueAxis rangeTime = new NumberAxis("Flow [cfs]");
        plotTime.setRangeAxis(0, rangeTime);

        //Define X Axis
        DateAxis domainTime = new DateAxis("Date");
        domainTime.setLowerMargin(0.03);
        domainTime.setUpperMargin(0.03);
        plotTime.setDomainAxis(0, domainTime);

        //Set other graph preferences
        graphing.setTimeAxisPreferences(plotTime);

        //Graph plot onto JfreeChart
        String title = "USGS-HYSEP Hydrograph Separation For " + database + " Station: " + stationID + ", " + stationName;
        JFreeChart parentChart = new JFreeChart(title, graphing.titleFont, plotTime, true);

        //Set legend Font
        LegendTitle legendTitle = parentChart.getLegend();
        legendTitle.setItemFont(graphing.masterFont);


        //Save resulting graph
        try {
            String path = mainFolder + "/baseflow_graph.jpg";
            ChartUtilities.saveChartAsJPEG(new File(path), parentChart, 1280, 800);
            System.out.println("JFreeChart created properly at: " + path);

        } catch (IOException e) {
            System.out.println("Problem occurred creating chart");
            System.out.println(e);
        }
    }
    /**
     * Reads one of the WATSTORE formated output files of HYSEP and reformats it into a daily timeseries to be used in later functions
     * @param mainFolder  the file location of the output file
     * @param fileType  the type of HYSEP output file being read (".bsf" for baseflow or ".sro" for stream flow)
     * @return  a String[][] of the results of the HYSEOP analysis where:
     * the first column contains the year of the date, the second column the month, third column the day, 
     * fourth column the values of the file (if the file is .bsf then baseflow values, if the file is .sro then streamflow values)
     * @throws IOException
     */
    private String[][] readHYSEPresults(String mainFolder, String fileType) throws IOException{
        //Open a reader for the results file
        FileReader file_to_read = new FileReader(mainFolder + File.separator + "baseflow" + fileType);
        BufferedReader reader = new BufferedReader(file_to_read);

        String currentLine;
        int ctr=0, dayCtr=1;
        ArrayList<String> yearList = new ArrayList<String>();
        ArrayList<String> monthList = new ArrayList<String>();
        ArrayList<String> dayList = new ArrayList<String>();
        ArrayList<String> flowList = new ArrayList<String>();

        //Read out the contents of the results file
        while((currentLine = reader.readLine()) !=null){
            if(ctr >= 1){//Ignore the first row header
                //System.out.println(currentLine);

                //substring out the results of year, month, day, Streamflow, Baseflow Pass1, Baseflow Pass2, Baseflow Pass3 
                //based on fixed width columns
                String year = currentLine.substring(16,20).trim();
                String month = currentLine.substring(20,22).trim();
                String week = currentLine.substring(22,24).trim();

                //Loop through the 'week' and pull out the values for each day
                int numberOfDays = checkHYSEPmonthYear(year, month, week);
                for(int i=1; i<=numberOfDays; i++){
                    String dayFlow = currentLine.substring(24 + (i-1)*7, 24 + i*7).trim();

                    //Add the day's flow value to the list
                    yearList.add(year);
                    monthList.add(month);
                    dayList.add(String.valueOf(dayCtr));
                    flowList.add(dayFlow);
                    dayCtr++;
                }
                if(Integer.parseInt(week) == 4){
                    dayCtr=1;
                }
            }
            ctr++;
        }
        reader.close();


        String[][] resultArray = new String[dayList.size() - 1][4];//The minus one is there because if 10 days are given only 9 will have baseflow calculations because the 10th day is the calculation for the 9th day
        for(int i=0; i<resultArray.length; i++){
            //System.out.println(i + "\t" + currentLine);

            //Add values to the result array
            resultArray[i][0] = yearList.get(i);
            resultArray[i][1] = monthList.get(i);
            resultArray[i][2] = dayList.get(i);
            resultArray[i][3] = flowList.get(i);
        }

        return resultArray;
    }
    /**
     * Checks based on the year, month, and week how many days are in the current 'week' of the WATSTORE (.gsd) file
     * @param year  the current year
     * @param month  the current month
     * @param week  the current WATSTORE file 'week'
     * @return  an integer number of days in the current 'week' of the WATSTORE (.gsd) file
     */
    private int checkHYSEPmonthYear(String year, String month, String week){
        //If week = 1, 2, or 3 it has 8 days
        if(Integer.parseInt(week) < 4){
            return 8;
        }

        //If week = 4 then its more complicated
        int numberOfDays = 0;
        switch(Integer.parseInt(month)){//Determine the number of days per the 4th 'week' in each month (gsd/WATSTORE files use an 8 or less day week to allow each month to have exactly 4 'weeks' keeping a consistent number of lines per year in the file)
            case 1:  numberOfDays = 7;	break;
            case 2:  numberOfDays = checkWATSTOREleapYear(Integer.parseInt(year));break;
            case 3:  numberOfDays = 7;	break;
            case 4:  numberOfDays = 6;	break;
            case 5:  numberOfDays = 7;	break;
            case 6:  numberOfDays = 6;	break;
            case 7:  numberOfDays = 7;	break;
            case 8:  numberOfDays = 7;	break;
            case 9:  numberOfDays = 6;	break;
            case 10: numberOfDays = 7;	break;
            case 11: numberOfDays = 6;	break;
            case 12: numberOfDays = 7;	break;
            default:                    break;
        }

        return numberOfDays;
    }
    /**
     * Determines if the provided year (as an integer) is a leap year or not
     * @param currentYear  the current year
     * @return  an integer number of days in the forth 'week' of february (a WATSTORE file 'week')
     */
    private int checkWATSTOREleapYear(int currentYear){
        //Determine how many days February should have based on if it is a leap year or not
        DoubleArray doubleArray = new DoubleArray();
        boolean leapYear = doubleArray.getLeapYearTF(currentYear);

        //Based on the leap year or not determine the number of days in February
        int feb = 4;
        if(leapYear){
            feb = 5;
        }
        return feb;
    }
    /**
     * 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);
    }

    public void run(Executable e) throws IOException, InterruptedException, Exception {
        //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);
        }
        
        //Check if any flow data exists
        Data data = new Data();
        String[][] sortableData = data.extractFlowData(mainFolder, database, organizationName, stationID, beginDate, endDate, userData, useSTORETretrieval, STORETdata);
        
        //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", stationID, userData, "flow", 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(sortedData.length == 0){
                errorMessage.add("There is no available flow 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);
        }
        
        //To prep. for graphing sort userdata by flow
        Arrays.sort(sortedData_user, new FlowComparator());

        if(modelType.equalsIgnoreCase("BFLOW")){
            //Reformat the extracted data for BFLOW
            String[][] inputFlowData = reformatBFLOWdata(sortedData_combined);

            //Write out the BFLOW input files
            writeBFLOWinputFiles(inputFlowData, ndmin, ndmax);

            //Call BFLOW model
            //Expected Input: "baseflow.txt" and "file.lst"
            e.exec();            
            
            //Expected Output: "baseflow.dat" and "baseflow.out"
            if (!new File(mainFolder, "baseflow.dat").exists()) {
                throw new FileNotFoundException("baseflow.dat");
            }
            if (!new File(mainFolder, "baseflow.out").exists()) {
                throw new FileNotFoundException("baseflow.out");
            }

            //Call graphing function for the outputs of the BFLOW model
            graphBFLOWresults(sortedData_user);
            
        }else if(modelType.equalsIgnoreCase("HYSEP")){
            //Reformat data for HYSEP
            ArrayList<String> inputFlowData = reformatHYSEPdata(sortedData, drainageArea);

            //Write out the HYSEP input file
            writeHYSEPinputFile(inputFlowData);

            //Call the HYSEP model (not functional yet)


            //Call graphing function for the outputs of the BFLOW model
            graphHYSEPresults();

        }

    }

    public static void main(String[] args) throws IOException, InterruptedException, Exception {
        guiBaseflow_Model baseflow_Model = new guiBaseflow_Model();
        //Run model
        baseflow_Model.run(null);
    }
}