guiDrought_Model.java [src/java/cfa] Revision: 55a7b16ccd741d97f18b72fa301f65216282c8a6 Date: Mon Feb 03 17:04:25 MST 2014
package cfa;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.chart.annotations.XYDrawableAnnotation;
import org.jfree.chart.annotations.XYPointerAnnotation;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.annotations.XYTitleAnnotation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.TickUnits;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.time.Year;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.TextAnchor;
/**
* Last Updated: 3-February-2014
* @author Tyler Wible
* @since 10-July-2012
*/
public class guiDrought_Model {
//Inputs
String mainFolder = "C:/Projects/TylerWible/CodeDirectories/NetBeans/CSIP/data/CFA";
String organizationName = "USGS";//"UserData";//
String stationID = "06752000";//"ARTest2_7";//"06741510";//"ARExample";//"06752260";//
String stationName = "CACHE LA POUDRE RIV AT MO OF CN, NR FT COLLINS, CO";
String beginDate = "";//"1900-01-01";//
String endDate = "";//"2002-01-01";//
String lambdaString = "optimize";//"1";//
String action = "all";//"optimizeModel";//"optimizeParameters";//"useParameters";//
String phiValues = "1";
String thetaValues = "";
double droughtLimit = -1;
String userData = "";//"Date\tFlow\n1997-04-29\t8.3\n1998-05-09\t60.2\n1999-05-29\t20.1";
boolean mergeDatasets = false;//true;//
String mergeMethod = "user";//"public";//"max";//"average";//"min";//
//Outputs
String len = "-1";
String start = "?";
String end = "?";
//Gets
public File getResult() {
return new File(mainFolder, "drought_summary.txt");
}
public String[] get_all_output() {
String[] graphs = new String[5];
graphs[0] = "drought_graph1.jpg";
graphs[1] = "drought_graph2.jpg";
graphs[2] = "drought_graph3.jpg";
graphs[3] = "drought_graph4.jpg";
graphs[4] = "drought_graph5.jpg";
return graphs;
}
public String[] get_optimizeModel_output() {
if(thetaValues.equalsIgnoreCase("")){
//The drought model used is AR(p) so only 1 graph
String[] graphs = new String[1];
graphs[0] = "optimize_drought_graph1.jpg";
return graphs;
}else{
//The drought model used is ARMA(p,q) so only 2 graph
String[] graphs = new String[2];
graphs[0] = "optimize_drought_graph3.jpg";
graphs[1] = "optimize_drought_graph4.jpg";
return graphs;
}
}
public String get_parameter_output(){
String[] graphNames = get_all_output();
//Return only the name of the fitted data graph
return graphNames[2];
}
public String getLen() {
return len;
}
public String getStart() {
return start;
}
public String getEnd() {
return end;
}
//These 'gets' are to help decide which return function to perform
public String getAction() {
return action;
}
public String getThetaValues() {
return thetaValues;
}
//Sets
public void setMainFolder(String mainFolder) {
this.mainFolder = mainFolder;
}
public void setOrganizationName(String organizationName) {
this.organizationName = organizationName;
}
public void setStationID(String stationID) {
this.stationID = stationID;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
public void setBeginDate(String beginDate) {
this.beginDate = beginDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
public void setLambdaString(String lambdaString) {
this.lambdaString = lambdaString;
}
public void setAction(String action) {
this.action = action;
}
public void setPhiValues(String phiValues) {
this.phiValues = phiValues;
}
public void setThetaValues(String thetaValues) {
this.thetaValues = thetaValues;
}
public void setDroughtLimit(double droughtLimit) {
this.droughtLimit = droughtLimit;
}
public void setUserData(String userData) {
this.userData = userData;
}
public void setMergeDatasets(boolean mergeDatasets) {
this.mergeDatasets = mergeDatasets;
}
public void setMergeMethod(String mergeMethod) {
this.mergeMethod = mergeMethod;
}
/**
* Performs a drought analysis on the provided sortedData (daily average stream flow value) and calculates the long-term
* average water supply to be taken as the drought threshold. First it calculates total annual river flows based on the
* provided daily stream flow averages, then calculates the long-term average of these annual flow values, then calculates
* the properties of the droughts (deficit, duration, intensity) compared against the drought threshold. Finally graphs a
* timeseries of the annual flow data against the water demand.
* @param mainFolder the file path to the location of the graph (ex. "C:/temp/myFolder")
* @param sortedData a string[][] containing daily average stream flow values in the following format: column1 = dates (yyyy-mm-dd format),
* column2 = daily average stream flow values (cfs)
* @param lambdaString contains either the user provided value of lambda or "optimize" which tells the program to optimize lambda based on the dataset
* @param action if "optimize", this only optimizes the lambda and phi values for the model and then reports these back to the user,
* if "all" then it generates data based on the calculated phi values and finishes performing the drought analysis,
* if "useParameters" then it takes the previously generated parameters and uses these to perform the regression
* @param phiValues
* @param thetaValues
* @param graphTitle the title of the graph to be created (should contain station ID and station Name for user's benefit)
* @return a String[] containing a tab-delimited summary of the droughts (water supply < water demand)
* @throws IOException
*/
public String[] generalDroughtAnalysis(String mainFolder,
String[][] sortedData,
String lambdaString,
String action,
String phiValues,
String thetaValues,
String graphTitle) throws IOException{
DoubleMath doubleMath = new DoubleMath();
DoubleArray doubleArray = new DoubleArray();
//Calculate total annual flows
double[][] annualData = sumAnnualFlows(sortedData);
//Get the flow values and calculate the long-term (total) average of them
double longTermAverage = doubleMath.Average(doubleArray.getColumn(annualData, 1));
//Call built in function to perform the drough analysis and graph the resulting data
String[] droughtInfo = generalDroughtAnalysis(mainFolder, sortedData, longTermAverage, lambdaString, action, phiValues, thetaValues, graphTitle);
return droughtInfo;
}
/**
* Performs a drought analysis on the provided sortedData (daily average stream flow value) and the provided long-term
* average water demand specified by the user. First it calculated total annual river flows based on the provided daily
* stream flow averages, then calculates the properties of the droughts (deficit, duration, intensity) compared against
* the water demand. Finally graphs a timeseries of the annual flow data against the water demand.
* @param mainFolder the file path to the location of the graph (ex. "C:/temp/myFolder")
* @param sortedData a string[][] containing daily average stream flow values in the following format: column1 = dates (yyyy-mm-dd format),
* column2 = daily average stream flow values (cfs)
* @param droughtLimit a double representing the annual drought limit (water demand) on the watershed as specified by the user (in units of acre-ft/year)
* @param lambdaString contains either the user provided value of lambda or "optimize" which tells the program to optimize lambda based on the dataset
* @param action if "optimize", this only optimizes the phi values for the model and then reports these back to the user,
* if "all" then it generates data based on the calculated phi values and finishes performing the drought analysis,
* if "useParameters" then it takes the previously generated parameters and uses these to perform the regression
* @param phiValues
* @param thetaValues
* @param graphTitle the title of the graph to be created (should contain station ID and station Name for user's benefit)
* @return a String[] containing a tab-delimited summary of the droughts (water supply < water demand)
* @throws IOException
*/
@SuppressWarnings("unchecked")
public String[] generalDroughtAnalysis(String mainFolder,
String[][] sortedData,
double droughtLimit,
String lambdaString,
String action,
String phiValues,
String thetaValues,
String graphTitle) throws IOException{
// //Test AR(p) model on sample data
// double[][] annualData = sumAnnualFlows(sortedData);
// Object[] returnArray = autoRegression.AR(p, annualData);
// annualData = (double[][]) returnArray[0];
// double[][] generatedData = (double[][]) returnArray[1];
// autoRegression.graphAR1Double(annualData, generatedData);
DoubleMath doubleMath = new DoubleMath();
DoubleArray doubleArray = new DoubleArray();
AutoRegression autoRegression = new AutoRegression();
//Calculate total annual flows
double[][] annualData = sumAnnualFlows(sortedData);
//Convert the data to only the stoicastic portion to modeled using AR(1) or ARMA(1,1)
double average = doubleMath.Average(doubleArray.getColumn(annualData, 1));
double stDev = doubleMath.StandardDeviationSample(doubleArray.getColumn(annualData, 1));
double[][] stocaisticData = autoRegression.StoicasticConversion(annualData, average, stDev, true);
//Perform a transformation on the stoicastic data to change its distribution to a normal distribution
double lambda = 1.0;
if(lambdaString.equalsIgnoreCase("optimize")){
lambda = autoRegression.BoxCox(stocaisticData);
}else{
lambda = Double.parseDouble(lambdaString);
}
double[][] transformedData = autoRegression.BoxCox(lambda, stocaisticData, true);
// double[][] transformedData = autoRegression.SalasExampleTransformation(stocaisticData, true);
//Run the user specified auto-regressive model on the data
double[][] fittedData = null;
double[][] predictedData = null;
String methodType = "";
Object[] returnArray = autoRegression.AutoregressiveModel(mainFolder, action, phiValues, thetaValues, transformedData);
if(action.equalsIgnoreCase("optimizeParameters")){
//Find the parameters of the regression only and report these back
String[] stringArray = (String[]) returnArray;
//Perform regression with data and graph the resulting data
Object[] dataArray = autoRegression.AutoregressiveModel(mainFolder, "useParameters", stringArray[0], stringArray[1], transformedData);
//Keep the data from the regression and graph it
methodType = (String) dataArray[0];
transformedData = (double[][]) dataArray[1];
fittedData = (double[][]) dataArray[2];
predictedData = (double[][]) dataArray[3];
}else if(action.equalsIgnoreCase("optimizeModel")){
if(thetaValues.equalsIgnoreCase("")){
//Calculate optimal AR(p) model and return the optimal phi values
ArrayList<Double> aic = (ArrayList<Double>) returnArray[0];
ArrayList<Double> bic = (ArrayList<Double>) returnArray[1];
String phi = (String) returnArray[2];
String theta = (String) returnArray[3];
//Graph the AIC/BIC curve for the user
graphAR_AIC_BIC(mainFolder, aic, bic);
String[] optimalValues = {phi + "$$", theta};
return optimalValues;
}else{
//Calculate optimal ARMA(p,q) model and return the optimal phi/theta values
double[][] results = (double[][]) returnArray[0];
String phi = (String) returnArray[1];
String theta = (String) returnArray[2];
//Graph the AIC/BIC curve for the user
graphARMA_AIC_BIC(mainFolder, "AIC", results, 3);
graphARMA_AIC_BIC(mainFolder, "BIC" , results, 4);
String[] optimalValues = {phi + "$$", theta};
return optimalValues;
}
}else{
//Keep the data from the regression and graph it
methodType = (String) returnArray[0];
transformedData = (double[][]) returnArray[1];
fittedData = (double[][]) returnArray[2];
predictedData = (double[][]) returnArray[3];
}
//Un-transform the stoicastic historic and stoicastic generated data so as to graph real stream flow values
transformedData = autoRegression.BoxCox(lambda, transformedData, false);
fittedData = autoRegression.BoxCox(lambda, fittedData, false);
predictedData = autoRegression.BoxCox(lambda, predictedData, false);
// transformedData = autoRegression.SalasExampleTransformation(transformedData, false);
// fittedData = autoRegression.SalasExampleTransformation(fittedData, false);
// predictedData = autoRegression.SalasExampleTransformation(predictedData, false);
//Convert back from the stoicastic component of the data to the entire original data
double[][] originalData = autoRegression.StoicasticConversion(transformedData, average, stDev, false);
fittedData = autoRegression.StoicasticConversion(fittedData, average, stDev, false);
predictedData = autoRegression.StoicasticConversion(predictedData, average, stDev, false);
//Calculate the droughts, their accumulated deficits, durations, and intensities
DroughtProperties historicDroughtProps = new DroughtProperties(annualData, droughtLimit);
DroughtProperties predictedDroughtProps = new DroughtProperties(predictedData, droughtLimit);
//Determine the return periods for the droughts on record
double[][] historicDroughts = recurrenceInterval(historicDroughtProps, droughtLimit, annualData);
double[][] predictedDroughts = recurrenceInterval(predictedDroughtProps, droughtLimit, predictedData);
//Graph the drought data
graphTimeseries(mainFolder, annualData, droughtLimit, graphTitle); //graph1
graphNegativeBarChart(mainFolder, annualData, droughtLimit, graphTitle); //graph2
graphFittedData(mainFolder, originalData, fittedData, methodType, graphTitle); //graph3
graphPredictedData(mainFolder, originalData, predictedData, methodType, graphTitle); //graph4
graphReturnPeriod(mainFolder, historicDroughts, predictedDroughts, graphTitle); //graph5
//Populate the return array with the information gained from the drought analysis
String[] droughtInfo = new String[historicDroughtProps.numberOfDroughts() + 1];
droughtInfo[0] = "Deficit [acre-ft]\tLambda\tDuration [years]\tIntensity\tFinal Year";
for(int i=0; i<historicDroughtProps.numberOfDroughts(); i++){
droughtInfo[i+1] = String.valueOf(historicDroughtProps.getDeficit(i)) + "\t" +
String.valueOf(historicDroughtProps.getDeficit(i)/droughtLimit) + "\t" +
String.valueOf(historicDroughtProps.getDuration(i)) + "\t" +
String.valueOf(historicDroughtProps.getIntensity(i)) + "\t" +
String.valueOf(historicDroughtProps.getEndYear(i));
}
if(action.equalsIgnoreCase("optimizeParameters")){
//Return only the parameters of the regression
String[] stringArray = (String[]) returnArray;
stringArray[0] = stringArray[0] + "$$";
return stringArray;
}else if(action.equalsIgnoreCase("updateParameters")){
//Return only the parameters of the regression
String[] stringArray = {phiValues + "$$", thetaValues};
return stringArray;
}
return droughtInfo;
}
/**
* Calculates the average recurrence interval for each drought on record and the lambda coefficient of each drought (deficit = lambda * droughtLimit)
* @param currentDroughtProps the DroughtProperties class containing the droughts to be analyzed
* @param droughtLimit the value of the water demand in acre-ft to calculate lambda from (lambda = deficit/water demand)
* @param flowData the double[][] array containing the annual flow data with column1 = years, column2 = flow values in acre-ft
* @return a double[][] array containing: column1 = lambda sets of eleven lambda values {0. 0.5, 0.75, 1.0, 2.0}, column2 = average recurrence interval (return period), column3 = length of drought (from 1 to 11
*/
private double[][] recurrenceInterval(DroughtProperties currentDroughtProps, double droughtLimit, double[][] flowData){
DoubleMath doubleMath = new DoubleMath();
double[] lambda = {0.0, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0};
ArrayList<Double> droughtLambda = new ArrayList<Double>();
ArrayList<Double> droughtRecurrence = new ArrayList<Double>();
ArrayList<Double> droughtLength = new ArrayList<Double>();
//Loop through lambda values
for(int i=0; i<lambda.length; i++){
//Loop drought year length
for(int j=1; j<11; j++){
ArrayList<Double> recurrence = new ArrayList<Double>();
int endYear = (int) flowData[0][0];
//Loop through droughts
for(int k=0; k<currentDroughtProps.numberOfDroughts(); k++){
//Compare the properties of the current drought to the provided
if(currentDroughtProps.getDeficit(k) > lambda[i]*droughtLimit && currentDroughtProps.getDuration(k) == j){
//Measure recurrence from start of drought to start of drought (Salas, 2005 pg 385)
recurrence.add((double) (currentDroughtProps.getEndYear(k) - endYear));
endYear = currentDroughtProps.getEndYear(k);
}
}
//There were observed droughts larger than this one, calculate the average recurrence interval
if(recurrence.size() != 0){
droughtLambda.add(lambda[i]);//lambda value
droughtRecurrence.add(doubleMath.Average(recurrence));//average recurrence = return period
droughtLength.add((double) j);//length of drought
}
}
}
//Return the drought properties in a single double[][]
double[][] newDroughts = new double[droughtLambda.size()][3];
for(int i=0; i<droughtLambda.size(); i++){
newDroughts[i][0] = droughtLambda.get(i);//lambda value
newDroughts[i][1] = droughtRecurrence.get(i);//average recurrence = return period
newDroughts[i][2] = droughtLength.get(i);//length of drought
}
return newDroughts;
}
/**
* Calculated the annual flow values given the average daily data provided. This sums the daily cfs values and
* converts it to acre-ft/day adding each day's worth of flow to the annual total
* @param sortedData a String[][] containing the flow data in the following format: column1 = dates (yyyy-mm-dd format),
* column2 = average daily streamflow values (cubic feet per second, cfs)
* @return
*/
private double[][] sumAnnualFlows(String[][] sortedData){
//Check for size issues and return early
if(sortedData.length<=1){
return new double[0][2];
}
//Get a unique list of years along with their accumulated flow values
ArrayList<String> uniqueYears = new ArrayList<String>();
ArrayList<Double> uniqueFlows = new ArrayList<Double>();
ArrayList<Integer> uniqueDays = new ArrayList<Integer>();
uniqueYears.add(sortedData[0][0].substring(0,4));
uniqueFlows.add((Double.parseDouble(sortedData[0][1])*24*3600)/43560);
uniqueDays.add(1);
int ctr = 0;
for(int i=1; i<sortedData.length; i++){
String previousYear = sortedData[i-1][0].substring(0,4);
String currentYear = sortedData[i][0].substring(0,4);
if(previousYear.equalsIgnoreCase(currentYear)){
//If the current year matches the previous year then add its flow value to the current total and continue
double currentFlowTotal = uniqueFlows.remove(ctr);
int currentNumberOfDays = uniqueDays.remove(ctr);
//convert daily cfs into acre-ft contributed by one day
//acre-ft = cfs * 1day * 24hr/1day * 3600sec/1hr * 1acre-ft/43560 ft^3
double currentFlow = (Double.parseDouble(sortedData[i][1])*24*3600)/43560;
uniqueFlows.add(ctr, currentFlowTotal + currentFlow);
uniqueDays.add(ctr, currentNumberOfDays + 1);
}else{
//If the current year does not match the previous year, then we are in a new year so add the year to the
//uniqueYear list and reset the annual flow total
uniqueYears.add(currentYear);
uniqueDays.add(1);
//convert daily cfs into acre-ft for one day
//acre-ft = cfs * 1day * 24hr/1day * 3600sec/1hr * 1acre-ft/43560 ft^3
double currentFlow = (Double.parseDouble(sortedData[i][1])*24*3600)/43560;
uniqueFlows.add(currentFlow);
ctr++;
}
}
System.out.println("Total Years: " + uniqueYears.size() + "\tTotal Flows: " + uniqueFlows.size());
// //Compute average flow/day for each year in acre-ft/day and then annualize this value for a total flow per year value
// //This average allows some years to have minimal data but still be used in the drought analysis
// for(int i=0; i<uniqueYears.size(); i++){
// int totalDays = uniqueDays.remove(i);
// if(totalDays < 365){
// //If less than a full year's worth of flows has been added, annualize each year's flow based on an average of the available data
// double totalFlow = uniqueFlows.remove(i);
// String year = uniqueYears.get(i);
// System.out.println(year + "\tOld flow: " + totalFlow + "acre-ft/day\t" + totalDays + " days");
//
// double averageFlowDay = totalFlow/totalDays;
// double annualFlow = averageFlowDay*365;
//
// uniqueFlows.add(i, annualFlow);
// uniqueDays.add(i, 365);
// System.out.println(year + "\tNew flow: " + annualFlow + "acre-ft/day\t365 days");
//
// }else{
// //If it is a full year of data, then don't change anything
// uniqueDays.add(i, totalDays);
// }
// }
double[][] uniqueData = new double[uniqueYears.size()][2];
for(int i=0; i<uniqueYears.size(); i++){
uniqueData[i][0] = Double.parseDouble(uniqueYears.get(i));//years
uniqueData[i][1] = uniqueFlows.get(i);//annual flow value
}
return uniqueData;
}
/**
* Graph the timeseries data and save the resulting graph to the specified location
* @param mainFolder the file path to the location of the graph (ex. "C:/temp/myFolder")
* @param sortedData the String[][] containing sorted data for the timeseries (column 1 = dates (yyyy-mm-dd) column 2 = values)
* @param droughtLimit the calculated drought limit (if supply < drought limit then that year is a drought)
* @param graphTitle the graph title
*/
public void graphTimeseries(String mainFolder,
double[][] sortedData,
double droughtLimit,
String graphTitle){
Graphing graphing = new Graphing();
//Round the long term average to fit the data better
double tempRounding = droughtLimit*1000;
tempRounding = Math.round(tempRounding)/1000;
//Create TimeSeries graph
TimeSeries series = new TimeSeries("Annual Flow Rate");
TimeSeries averageSeries = new TimeSeries("Drought Limit: " + String.valueOf(tempRounding) + " [acre-ft]");
for(int i=0; i < sortedData.length; i++) {
Year year = new Year((int) sortedData[i][0]);
series.add(year, sortedData[i][1]);
averageSeries.add(year,droughtLimit);
}
//Graph data
XYPlot plotTime = new XYPlot();
plotTime = graphing.graphTimeData(plotTime, series, true, Color.blue, false, false, false, true, 0);
plotTime = graphing.graphTimeData(plotTime, averageSeries, true, Color.black, false, false, true, true, 1);
//Create Y axis
ValueAxis rangeTime = new NumberAxis("Annual Flow Rate [acre-ft]");
plotTime.setRangeAxis(0, rangeTime);
//Create X Axes
DateAxis domainTime = new DateAxis("Year");
domainTime.setLowerMargin(0.05);
domainTime.setUpperMargin(0.05);
plotTime.setDomainAxis(0, domainTime);
//Set extra plot preferences
graphing.setTimeAxisPreferences(plotTime);
//Create the chart with the plot and a legend
JFreeChart chart = new JFreeChart(graphTitle, graphing.titleFont, plotTime, true);
//Set legend Font
LegendTitle legendTitle = chart.getLegend();
legendTitle.setItemFont(graphing.masterFont);
//Save resulting graph for use later
try{
String[] graphNames = get_all_output();
String path = mainFolder + File.separator + graphNames[0];
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("Graph located at:\t" + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* Graph the bar chart of the timeseries data where the values graphed ar those in sortedData and save the resulting graph to the specified location
* @param mainFolder the file path to the location of the graph (ex. "C:/temp/myFolder")
* @param sortedData the String[][] containing sorted data for the timeseries (column 1 = dates (yyyy-mm-dd) column 2 = values)
* @param droughtLimit the calculated drought limit (if supply < drought limit then that year is a drought)
* @param graphTitle the graph title
*/
public void graphNegativeBarChart(String mainFolder,
double[][] sortedData,
double droughtLimit,
String graphTitle){
Graphing graphing = new Graphing();
//Round the long term average to fit the data better
double tempRounding = droughtLimit*1000;
tempRounding = Math.round(tempRounding)/1000;
//Create TimeSeries graph
TimeTableXYDataset dataset = new TimeTableXYDataset();
boolean deficitFirst = false;
for(int i=0; i < sortedData.length; i++) {
Year year = new Year((int) sortedData[i][0]);
if(sortedData[i][1] < droughtLimit){
dataset.add(year, sortedData[i][1] - droughtLimit, "Deficit");
if(i == 0){
//If the first year is a deficit mark it as true to get the colors for the graph right
deficitFirst = true;
}
}else{
dataset.add(year, sortedData[i][1] - droughtLimit, "Surplus");
}
}
//Define axis and properties
NumberAxis numberaxis = new NumberAxis("Annual Deficit/Surplus of Drought Limit (" + tempRounding + " acre-ft)");
DateAxis dateaxis = new DateAxis("Year");
dateaxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
dateaxis.setLowerMargin(0.01D);
dateaxis.setUpperMargin(0.01D);
//Define renderer properties for stacked graph
StackedXYBarRenderer stackedxybarrenderer = new StackedXYBarRenderer(0);
stackedxybarrenderer.setDrawBarOutline(false);
stackedxybarrenderer.setShadowVisible(false);
if(deficitFirst){
stackedxybarrenderer.setSeriesPaint(0, Color.red);
stackedxybarrenderer.setSeriesPaint(1, Color.blue);
}else{
stackedxybarrenderer.setSeriesPaint(0, Color.blue);
stackedxybarrenderer.setSeriesPaint(1, Color.red);
}
//Graph the dataset using the renderer and create a chart
XYPlot plot = new XYPlot(dataset, dateaxis, numberaxis, stackedxybarrenderer);
//Set extra plot preferences
graphing.setTimeAxisPreferences(plot);
//Create the chart with the plot
JFreeChart chart = new JFreeChart(graphTitle, graphing.titleFont, plot, false);
//Save resulting graph for use later
try{
String[] graphNames = get_all_output();
String path = mainFolder + File.separator + graphNames[1];
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("Graph located at:\t" + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* Graph the orignalData against the generatedData on a timeseries graph. Also graphs a scatter plot of the errors between the originalData and generatedData
* @param mainFolder the file path to the location of the graph (ex. "C:/temp/myFolder")
* @param orignalData double array of the original data. Column1 = years, column2 = annual flow values
* @param fittedData double array of the fitted data during the same time period as the original data. Column1 = years, column2 = annual flow values
* @param methodType the name of the method used to generate and project the data to be used in the legend of the graph
* @param graphTitle the graph title
*/
public void graphFittedData(String mainFolder,
double[][] orignalData,
double[][] fittedData,
String methodType,
String graphTitle){
Graphing graphing = new Graphing();
//Convert graph x = original data, y = fitted data
XYSeries series1 = new XYSeries("Regression of Data");
double max = 0;
for(int i=0; i < orignalData.length; i++) {
series1.add(orignalData[i][1], fittedData[i][1]);
if(Double.compare(orignalData[i][1], max) > 0){
max = orignalData[i][1];
}
if(Double.compare(fittedData[i][1], max) > 0){
max = fittedData[i][1];
}
}
XYPlot plot = new XYPlot();
max = 1.02*max;
//Add first series to graph
XYDataset dataset1 = new XYSeriesCollection(series1);
XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(false, true);
currentRenderer.setSeriesPaint(0, Color.black);
plot.setDataset(0, dataset1);
plot.setRenderer(0, currentRenderer);
//Add line of slope 1 to graph
XYSeries series2 = new XYSeries("One to one line, Don't show in legend");
series2.add(0,0);
series2.add(max, max);
XYDataset dataset2 = new XYSeriesCollection(series2);
XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, false);
currentRenderer2.setSeriesPaint(0, Color.lightGray);
plot.setDataset(1, dataset2);
plot.setRenderer(1, currentRenderer2);
//Create the X Axis
ValueAxis xAxis = new NumberAxis("Original Data [acre-ft/yr]");
xAxis.setRange(0, max);
xAxis.setVerticalTickLabels(true);
plot.setDomainAxis(0, xAxis);
//Create the Y Axis
ValueAxis yAxis = new NumberAxis(methodType + " Data [acre-ft/yr]");
yAxis.setRange(0, max);
plot.setRangeAxis(0, yAxis);
//Set extra plot preferences
graphing.setAxisPreferences(plot);
//Create the chart with the plot
JFreeChart chart = new JFreeChart("Data Correlelation for " + graphTitle, graphing.titleFont, plot, false);
try{
String[] graphNames = get_all_output();
String path = mainFolder + File.separator + graphNames[2];
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("Graph located at:\t" + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* Graph the orignalData against the future predictedData on a timeseries graph.
* @param mainFolder the file path to the location of the graph (ex. "C:/temp/myFolder")
* @param orignalData double array of the original data. Column1 = years, column2 = annual flow values
* @param predictedData double array of the future predicted data. Column1 = years, column2 = annual flow values
* @param methodType the name of the method used to generate and project the data to be used in the legend of the graph
* @param graphTitle the graph title
*/
public void graphPredictedData(String mainFolder,
double[][] orignalData,
double[][] predictedData,
String methodType,
String graphTitle){
Graphing graphing = new Graphing();
//Convert series into Dates
XYSeries series1 = new XYSeries("Original Data");
XYSeries series2 = new XYSeries(methodType + " warm up period");
XYSeries series3 = new XYSeries(methodType + " Predicted Data");
for(int i=0; i < orignalData.length; i++) {
series1.add(orignalData[i][0], orignalData[i][1]);
}
for(int i=0; i < predictedData.length; i++) {
if(i<100){
//Keep the warm up series
series2.add(predictedData[i][0], predictedData[i][1]);
}else{
//Keep the final dataseries
series3.add(predictedData[i][0], predictedData[i][1]);
}
}
XYPlot plot = new XYPlot();
//Add first series to graph
XYDataset dataset1 = new XYSeriesCollection(series1);
XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(true, false);
currentRenderer.setSeriesPaint(0, Color.black);
plot.setDataset(0, dataset1);
plot.setRenderer(0, currentRenderer);
//Add second series to graph
XYDataset dataset2 = new XYSeriesCollection(series2);
XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, false);
currentRenderer2.setSeriesPaint(0, Color.gray);
plot.setDataset(1, dataset2);
plot.setRenderer(1, currentRenderer2);
//Add third series to graph
XYDataset dataset3 = new XYSeriesCollection(series3);
XYItemRenderer currentRenderer3 = new XYLineAndShapeRenderer(true, false);
currentRenderer3.setSeriesPaint(0, Color.red);
plot.setDataset(2, dataset3);
plot.setRenderer(2, currentRenderer3);
//Create the X Axis
NumberAxis xAxis = new NumberAxis("Year");
xAxis.setRange(orignalData[0][0], predictedData[predictedData.length - 1][0]/35);
DecimalFormat formatter = new DecimalFormat("#0");
xAxis.setNumberFormatOverride(formatter);
plot.setDomainAxis(0, xAxis);
//Create the Y Axis
ValueAxis yAxis = new NumberAxis("Annual Discharge [acre-ft/yr]");
plot.setRangeAxis(0, yAxis);
//Set extra plot preferences
graphing.setAxisPreferences(plot);
//Create the chart with the plot
JFreeChart chart = new JFreeChart("Projected Data for " + graphTitle, graphing.titleFont, plot, true);
//Set legend Font
LegendTitle legendTitle = chart.getLegend();
legendTitle.setItemFont(graphing.masterFont);
try{
String[] graphNames = get_all_output();
String path = mainFolder + File.separator + graphNames[3];
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("Graph located at:\t" + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* Graphs the drought length verses the return period for each lambda (drought deficit = lambda * droughtLimit) value on a scatter plot
* @param mainFolder the file path to the location of the graph (ex. "C:/temp/myFolder")
* @param originalData double array of the original data. Column1 = years, column2 = annual flow values
* @param predictedData double array of the future predicted data. Column1 = years, column2 = annual flow values
* @param graphTitle the graph title
*/
public void graphReturnPeriod(String mainFolder,
double[][] originalData,
double[][] predictedData,
String graphTitle){
DoubleMath doubleMath = new DoubleMath();
DoubleArray doubleArray = new DoubleArray();
Graphing graphing = new Graphing();
//Create xy scatter plot
XYPlot plot = new XYPlot();
//Add fist series to graph
double[] lambda = {0.0};//, 0.5, 0.75, 1.0};
Color[] colorMatrix2 = {Color.black, Color.darkGray, Color.gray, Color.lightGray};
int ctr = 0;
for(int i=0; i<lambda.length; i++){
//Create and populate the lambda series
XYSeries series2 = new XYSeries("Original Data: Lambda = " + lambda[i]);
for(int j=0; j < originalData.length; j++){
if(Double.compare(originalData[j][0], lambda[i]) == 0){
series2.add(originalData[j][2], originalData[j][1]);
}
}
//Add the new series to the dataset
XYDataset dataset2 = new XYSeriesCollection(series2);
XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, true);
currentRenderer2.setSeriesPaint(0, colorMatrix2[i]);
plot.setDataset(ctr, dataset2);
plot.setRenderer(ctr, currentRenderer2);
ctr++;
}
//Add second series to graph
double[] lambda2 = {0.0, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0};
//Colors in the color matrix below are: purple, blue, cyan, green, gold, red, pink
Color[] colorMatrix1 = {new Color(105, 0, 255), Color.blue, Color.cyan, Color.green, new Color(255, 135, 0), new Color(255, 0, 50), new Color(255, 0, 255, 100)};
for(int i=0; i<lambda2.length; i++){
//Create and populate the lambda series
XYSeries series2 = new XYSeries("Lambda = " + lambda2[i]);
for(int j=0; j < predictedData.length; j++){
if(Double.compare(predictedData[j][0], lambda2[i]) == 0){
series2.add(predictedData[j][2], predictedData[j][1]);
}
}
//Add the new series to the dataset
XYDataset dataset2 = new XYSeriesCollection(series2);
XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, true);
currentRenderer2.setSeriesPaint(0, colorMatrix1[i]);
plot.setDataset(ctr, dataset2);
plot.setRenderer(ctr, currentRenderer2);
ctr++;
}
//Create the X Axis
ValueAxis xAxis = new NumberAxis("Drought Length [years]");
xAxis.setRange(0, 11);
TickUnits unit1 = new TickUnits();
unit1.add(new NumberTickUnit(1, NumberFormat.getNumberInstance()));
xAxis.setStandardTickUnits(unit1);
plot.setDomainAxis(0, xAxis);
//Create the Y Axis
LogarithmicAxis yAxis = new LogarithmicAxis("Recurrence Interval [years]");
yAxis.setAllowNegativesFlag(true);
double maxValue = 1;
try{
maxValue = Math.log10(doubleMath.max(doubleArray.getColumn(predictedData, 1)));
maxValue = Math.ceil(maxValue);
}catch(ArrayIndexOutOfBoundsException e){
maxValue = 1;
System.err.print("No predicted data lambda values\n");
}
yAxis.setRange(1, Math.pow(10, maxValue));
plot.setRangeAxis(0, yAxis);
//Add equation explaining the term lambda
XYTextAnnotation equation = new XYTextAnnotation(" Lambda = Drought Deficit / Drought Limit ", 9, Math.pow(10, maxValue)*0.5);
equation.setFont(graphing.masterFont);
equation.setBackgroundPaint(Color.white);
equation.setOutlinePaint(Color.black);
equation.setOutlineVisible(true);
plot.addAnnotation(equation);
//Set extra plot preferences
graphing.setAxisPreferences(plot);
//Create the chart with the plot
JFreeChart chart = new JFreeChart("Drought Recurrence Intervals for " + graphTitle, graphing.titleFont, plot, true);
//Set legend Font
LegendTitle legendTitle = chart.getLegend();
legendTitle.setItemFont(graphing.masterFont);
try{
String[] graphNames = get_all_output();
String path = mainFolder + File.separator + graphNames[4];
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("Graph located at:\t" + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* Graph the AR(p) AIC and BIC verses the p order of the AR(p) model
* @param mainFolder the folder location of the graph
* @param aic an array list of aic values corresponding to p=1, 2, etc.
* @param bic an array list of bic values corresponding to p=1, 2, etc.
*/
public void graphAR_AIC_BIC(String mainFolder, ArrayList<Double> aic, ArrayList<Double> bic){
DoubleMath doubleMath = new DoubleMath();
Graphing graphing = new Graphing();
//Graph AIC and BIC data vs. the p parameter of the AR(p) model
XYSeries series1 = new XYSeries("AIC");
XYSeries series2 = new XYSeries("BIC");
for(int i=0; i < aic.size(); i++) {
series1.add(i+1, aic.get(i));
series2.add(i+1, bic.get(i));
}
XYPlot plot = new XYPlot();
//Add first series to graph
XYDataset dataset1 = new XYSeriesCollection(series1);
XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(true, true);
currentRenderer.setSeriesPaint(0, Color.black);
plot.setDataset(0, dataset1);
plot.setRenderer(0, currentRenderer);
//Add second series to graph
XYDataset dataset2 = new XYSeriesCollection(series2);
XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, true);
currentRenderer2.setSeriesPaint(0, Color.cyan);
plot.setDataset(1, dataset2);
plot.setRenderer(1, currentRenderer2);
//Create the X Axis
ValueAxis xAxis = new NumberAxis("Number of Model Parameters");
TickUnits unit1 = new TickUnits();
unit1.add(new NumberTickUnit(1, NumberFormat.getNumberInstance()));
xAxis.setStandardTickUnits(unit1);
xAxis.setRange(0.01, aic.size() + 0.99);
plot.setDomainAxis(0, xAxis);
//Create the Y Axis
ValueAxis yAxis = new NumberAxis("AIC/BIC Value");
double max = doubleMath.max(aic);
if(max < doubleMath.max(bic)){
max = doubleMath.max(bic);
}
double min = doubleMath.min(aic);
if(min > doubleMath.min(bic)){
min = doubleMath.min(bic);
}
yAxis.setRange(0.98*min, 1.02*max);
plot.setRangeAxis(0, yAxis);
//Create the legend inside of the graph
LegendTitle lt = new LegendTitle(plot);
lt.setItemFont(new Font("Dialog", Font.PLAIN, 12));
lt.setBackgroundPaint(new Color(255, 255, 255, 255));
lt.setFrame(new BlockBorder(Color.black));
lt.setPosition(RectangleEdge.RIGHT);
lt.setPosition(RectangleEdge.LEFT);
XYTitleAnnotation ta = new XYTitleAnnotation(1, 1, lt, RectangleAnchor.TOP_RIGHT);
plot.addAnnotation(ta);
//Set extra plot preferences
graphing.setAxisPreferences(plot);
//Create the chart with the plot
JFreeChart chart = new JFreeChart("AR(p) Parameter Optimization", graphing.titleFont, plot, false);
try{
String[] graphNames = get_optimizeModel_output();
String path = mainFolder + File.separator + graphNames[0];
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("Graph located at:\t" + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* Graph the ARMA(p,q) AIC or BIC values verses the total number of ARMA model parameters (p+q)
* @param mainFolder the folder location for the graph
* @param method the name of the method either AIC or BIC
* @param results a double[][] containing the points to be plotted
* @param graphNumber the index of the graph either 3 (AIC) or 4 (BIC)
*/
public void graphARMA_AIC_BIC(String mainFolder, String method, double[][] results, int graphNumber){
DoubleMath doubleMath = new DoubleMath();
DoubleArray doubleArray = new DoubleArray();
Graphing graphing = new Graphing();
//Add each point of the aic/bic to the graph
XYPlot plot = new XYPlot();
XYSeries series1 = new XYSeries(method + " Points");
for(int i=0; i < results.length; i++){
series1.add(results[i][0] + results[i][1], results[i][graphNumber - 1]);
}
//Determine what the optimal values of AIC/BIC are for each p+q combination
XYSeries series2 = new XYSeries(method + " Optimal Front");
ArrayList<Double> aicBIC = new ArrayList<Double>();
ArrayList<Double> pValues = new ArrayList<Double>();
ArrayList<Double> qValues = new ArrayList<Double>();
for(int i=2; i<=8; i++){
//For each "value"=p+q find all of the p+q combinations which add to this
//"value" and retrieve their corresponding AIC/BIC value
ArrayList<Double> reArrangedData = new ArrayList<Double>();
for(int j=0; j<results.length; j++){
if(results[j][0] + results[j][1] == i){
reArrangedData.add(results[j][graphNumber - 1]);
}
}
double currentMinimum = doubleMath.min(reArrangedData);
series2.add(i, currentMinimum);
for(int j=0; j<results.length; j++){
if(Double.compare(currentMinimum,results[j][graphNumber - 1]) == 0){
aicBIC.add(results[j][graphNumber - 1]);
pValues.add(results[j][0]);
qValues.add(results[j][1]);
}
}
}
//Add first series to graph
XYDataset dataset1 = new XYSeriesCollection(series1);
XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(false, true);
currentRenderer.setSeriesPaint(0, Color.black);
plot.setDataset(0, dataset1);
plot.setRenderer(0, currentRenderer);
//Add second series to graph
XYDataset dataset2 = new XYSeriesCollection(series2);
XYItemRenderer currentRenderer2 = new XYLineAndShapeRenderer(true, false);
currentRenderer2.setSeriesStroke(0,
new BasicStroke(
1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
1.0f, new float[] {6.0f, 6.0f}, 0.0f
));
currentRenderer2.setSeriesPaint(0, Color.blue);
plot.setDataset(1, dataset2);
plot.setRenderer(1, currentRenderer2);
//Add highlight to best AIC/BIC value (lowest)
XYSeries series3 = new XYSeries("Error");
double minAICbic = doubleMath.min(aicBIC);
String p = "", q = "";
for(int i=0; i<aicBIC.size(); i++){
if(Double.compare(aicBIC.get(i), minAICbic) == 0){
p = String.valueOf(pValues.get(i));
q = String.valueOf(qValues.get(i));
series3.add(pValues.get(i) + qValues.get(i), minAICbic);
break;
}
}
//Create label for optimal ARMA model
p = p.substring(0,p.indexOf("."));
q = q.substring(0,q.indexOf("."));
XYDataset dataset3 = new XYSeriesCollection(series3);
XYItemRenderer currentRenderer3 = new XYLineAndShapeRenderer(false, true);
currentRenderer3.setSeriesPaint(0, Color.black);
currentRenderer3.setSeriesVisibleInLegend(0, false);
//Add an outline around the point
CircleDrawer cd = new CircleDrawer(Color.black, new BasicStroke(1.0f), null);
double x = series3.getX(0).doubleValue();
double y = series3.getY(0).doubleValue();
XYAnnotation bestBid = new XYDrawableAnnotation(x, y, 11, 11, cd);
plot.addAnnotation(bestBid);
//Add an arrow pointing to the point with the year of that flood
final XYPointerAnnotation pointer = new XYPointerAnnotation("Optimal ARMA Model: p= " + p + " q= " + q, x, y, Math.PI - 0.3);
pointer.setBaseRadius(30.0);
pointer.setTipRadius(10.0);
pointer.setFont(graphing.masterFont);
pointer.setPaint(Color.black);
pointer.setBackgroundPaint(Color.white);
pointer.setOutlinePaint(Color.black);
pointer.setTextAnchor(TextAnchor.CENTER_RIGHT);
plot.addAnnotation(pointer);
//Put the LDC line data, renderer, and axis into plot
plot.setDataset(3, dataset3);
plot.setRenderer(3, currentRenderer3);
//Create the X Axis
ValueAxis xAxis = new NumberAxis("Number of Model Parameters");
TickUnits unit1 = new TickUnits();
unit1.add(new NumberTickUnit(1, NumberFormat.getNumberInstance()));
xAxis.setStandardTickUnits(unit1);
xAxis.setRange(0.01, 8.99);
plot.setDomainAxis(0, xAxis);
//Create the Y Axis
ValueAxis yAxis = new NumberAxis(method + " Value");
double max = doubleMath.max(doubleArray.getColumn(results, graphNumber - 1));
double min = doubleMath.min(doubleArray.getColumn(results, graphNumber - 1));
yAxis.setRange(0.8*min, 1.02*max);
plot.setRangeAxis(0, yAxis);
//Set extra plot preferences
graphing.setAxisPreferences(plot);
//Create the chart with the plot
JFreeChart chart = new JFreeChart("ARMA(p, q) " + method + " Parameter Optimization", graphing.titleFont, plot, false);
try{
String[] graphNames = get_optimizeModel_output();
String path = mainFolder + File.separator + graphNames[graphNumber-3];
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("Graph located at:\t" + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* Writes out the dynamically created summary table to be displayed to the user along with the flood graph
* @param dynamicSummary string[] array to be written as each line of the text file
* @param partialpath the partial folder path of the file to be written
* @throws IOException
*/
private void writeSummary(String[] dynamicSummary, String partialpath) throws IOException{
//Intialize the file writer
String path = partialpath + File.separator + "drought_summary.txt";
FileWriter writer = new FileWriter(path, false);
PrintWriter print_line = new PrintWriter(writer);
//Write the summary to the file
for(int i=0; i < dynamicSummary.length; i++) {
print_line.printf("%s" + "%n", dynamicSummary[i]);
}
print_line.close();
writer.close();
System.out.println("Text File located at:\t" + path);
}
/**
* Writes out the error message, if any, for finding the file and then exits the program
* @param error string array to be written as each line of an error message
* @throws IOException
*/
public void writeError(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() throws IOException, InterruptedException {
//If no date input, make it the maximum of available data
if(beginDate == null || beginDate.equalsIgnoreCase("")){
beginDate = "1900-01-01";
}
if(endDate == null || endDate.equalsIgnoreCase("")){
// Pull current date for upper limit of data search
DateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date currentDate = new Date();
endDate = desiredDateFormat.format(currentDate);
}
//Check if any flow data exists
Data data = new Data();
String[][] sortableData = data.extractFlowData(mainFolder, organizationName, stationID, beginDate, endDate, userData);
//If the user wants the datasets (public and user) merged then retrieve the second dataset (user)
String[][] sortableData_user = new String[0][0];
if(mergeDatasets){
User_Data user_Data = new User_Data();
sortableData_user = user_Data.readUserFile(userData, beginDate, endDate);
}
//Sort the Data by date to remove duplicate date entries
String[][] sortedData = data.removeDuplicateDates(sortableData);
String[][] sortedData_user = data.removeDuplicateDates(sortableData_user);
//Merge the two datasets (if user data is empty nothing will be merged)
DoubleArray doubleArray = new DoubleArray();
String[][] sortedData_combined = doubleArray.mergeData(sortedData, sortedData_user, mergeMethod);
if(sortedData_combined.length == 0){
ArrayList<String> errorMessage = new ArrayList<>();
if(sortableData.length == 0){
String database = "USGS";
if(!organizationName.equals("USGS")){
database = "STORET";
}
errorMessage.add("There is no available flow data in the " + database + "database for station '" + stationID + "' and the specified date range.");
}
if(sortableData_user.length == 0){
errorMessage.add("There is no available uploaded data for station '" + stationID + "' and the specified date range");
}
errorMessage.add("Error: Baseflow0001");
writeError(errorMessage);
}
//Perform drought analysis
String graphTitle = "Station: " + stationID + "-" + stationName + " By: USGS";// + organizationName;
String[] droughtInfo = null;
if(Double.compare(droughtLimit, 0) < 0){
//If no drought limit is provided (aka -1 is provided) then calculate the drought limit = average(annual flows)
droughtInfo = generalDroughtAnalysis(mainFolder, sortedData_combined, lambdaString, action, phiValues, thetaValues, graphTitle);
}else{
//If a drought limit is provided (aka greater than or equal to zero) then use this as the drought limit
droughtInfo = generalDroughtAnalysis(mainFolder, sortedData_combined, droughtLimit, lambdaString, action, phiValues, thetaValues, graphTitle);
}
//Append summary of inputs to the data summary
this.len = String.valueOf(sortedData_combined.length);
this.start = String.valueOf(sortedData_combined[0][0]);
this.end = String.valueOf(sortedData_combined[sortedData_combined.length - 1][0]);
writeSummary(droughtInfo, mainFolder);
}
public static void main(String[] args) throws IOException, InterruptedException, Exception {
guiDrought_Model drought_Model = new guiDrought_Model();
//Set Inputs
// assert args.length > 0;
// drought_Model.setMainFolder(args[0]); //The output location of the graph
// drought_Model.setFileName(args[1]); //The name of the output graph and summary text file
// drought_Model.setOrganizationName(args[2]); //Supervising organization of the station (only used for STORET stations)
// drought_Model.setStationID(args[3]); //The station ID used to retrieve the station's flow data
// drought_Model.setStationName(args[4]); //The station Name for the current station, used to title the graph
// drought_Model.setBeginDate(args[5]); //Begin date of analysis
// drought_Model.setEndDate(args[6]); //End date of analysis
// drought_Model.setLambdaString(args[7]); //the value of lambda for the Box-Cox transformation or the word "optimize" to optimize this value
// drought_Model.setAction(args[8]); //The action to be taken for the drought analysis, either "all" for a full model, "optimizeParameters" to return only the phi and theta values for the AR/ARMA model fit, or useParameters to use the provided phi and theta parameters
// drought_Model.setPhiValues(args[9]); //the number of phi values desired for the AR/ARMA model or a tab-delimited string of the previously calculated phi values
// drought_Model.setThetaValues(args[10]); //the number of theta values desired for the AR/ARMA model or a tab-delimited string of the previously calculated theta values (blank is acceptable and results in an AR(p) model instead of an ARMA(p,q) model)
// drought_Model.setDroughtLimit(Double.parseDouble(args[11])); //If not <0 then this represents the user specified drought limit so use it instead of calculating the long term average as the drought limit
//Run Model
drought_Model.run();
}
}