guiBaseflow_Model.java [src/java/cfa] Revision: bb973886fb62cd031649c46729c475e05585bb3c Date: Wed Jul 16 11:40:53 MDT 2014
package cfa;
import csip.Config;
import csip.utils.Binaries;
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 oms3.util.ProcessComponent;
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: 16-July-2014
* @author Tyler Wible
* @since 15-June-2012
*/
public class guiBaseflow_Model {
//Inputs
String mainFolder = "C:/Projects/TylerWible/CodeDirectories/NetBeans/CSIP/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;
String userData = "";//"Date\tFlow\n1999-04-29\t8.3\n1999-05-09\t60.2\n1999-05-29\t20.1";
boolean mergeDatasets = false;//true;//
String mergeMethod = "user";//"public";//"max";//"average";//"min";//
//Outputs
String len = "-1";
String start = "?";
String end = "?";
String dataSource = "?";
//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;
}
//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 setUserData(String userData) {
this.userData = userData;
}
public void setMergeDatasets(boolean mergeDatasets) {
this.mergeDatasets = mergeDatasets;
}
public void setMergeMethod(String mergeMethod) {
this.mergeMethod = mergeMethod;
}
/**
* 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 courtesy of the U.S. Geological Survey, National Water Information System: Web Interface. http://waterdata.usgs.gov/nwis, accessed: " + today;
}else if(database.equalsIgnoreCase("STORET")){
this.dataSource = "Stream flow data courtesy of the U.S. Environmental Protection Agency, STORET. http://www.epa.gov/storet/index.html accessed: " + today;
}else if(database.equalsIgnoreCase("CDWR")){
this.dataSource = "Stream flow data courtesy of the Colorado Division of Water Resources, CDWR. http://www.dwr.state.co.us accessed: " + today;
}
this.len = String.valueOf(allFlowData.length);
this.start = allFlowData[0][0];
this.end = allFlowData[allFlowData.length - 1][0];
return reformatedData;
}
/**
* 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
* @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 writeBFLOWinputFile(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" + "%n", "Date Flow");
for (int i = 0; i < dynamicSummary.length; i++) {
print_line.printf("%s" + "%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" + "%n", "!!Input for baseflow program:");
print_line2.printf(" %d !NDMIN: minimum number of days for alpha calculation" + "%n", ndmin);
print_line2.printf(" %d !NDMAX: maximum number of days for alpha calculation" + "%n", ndmax);
print_line2.printf("%s" + "%n", " 1 !IPRINT: daily print option (0-no; 1-yes)");
print_line2.printf("%s" + "%n", "");
print_line2.printf("%s" + "%n", "!!Daily stream data files");
print_line2.printf("%s" + "%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{
DoubleMath doubleMath = new DoubleMath();
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");
ArrayList<Double> baseflowPass1 = new ArrayList<Double>();
String[][] graphData = new String[baseFlowResults.length][5];
int day = 1, month = 1, year = 1900, ctr = 0;
double d =1, m =1, y=1;
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]);
baseflowPass1.add(baseFlow1);
//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);
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)){
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++;
}
}
}
double baseflowMax = doubleMath.max(baseflowPass1);
//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.LIGHT_GRAY, 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.DARK_GRAY, 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.LIGHT_GRAY, 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.DARK_GRAY, 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.LIGHT_GRAY, 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.DARK_GRAY, 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, baseflowMax);
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;
}
/**
* 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 courtesy of the U.S. Geological Survey, National Water Information System: Web Interface. http://waterdata.usgs.gov/nwis, accessed: " + today;
}else if(database.equalsIgnoreCase("STORET")){
this.dataSource = "Stream flow data courtesy of the U.S. Environmental Protection Agency, STORET. http://www.epa.gov/storet/index.html accessed: " + today;
}else if(database.equalsIgnoreCase("CDWR")){
this.dataSource = "Stream flow data and water quality test data courtesy of 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;
}
/**
* 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" + "%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() throws IOException, InterruptedException, Exception {
//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, database, 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, "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
writeBFLOWinputFile(inputFlowData, ndmin, ndmax);
//Call BFLOW model
//Expected Input: "baseflow.txt" and "file.lst"
String binDir = Config.getString("m.bin.dir", "/tmp/csip/bin");
File bflow = Binaries.unpackResource("/bin/win-x86/bflow.exe", new File(binDir));
ProcessComponent pc = new ProcessComponent();
if (File.pathSeparatorChar == ':') {
pc.exe = Config.getString("wine.path", "/usr/bin/wine");
pc.args = new String[]{bflow.toString()};
} else {
pc.exe = bflow.toString();
pc.args = new String[]{};
}
pc.working_dir = mainFolder;
pc.execute();
//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();
//Set inputs
// assert args.length > 0;
// baseflow_Model.setMainFolder(args[0]); //The output location of the graph
// baseflow_Model.setFileName(args[1]); //The name of the output graph and summary text file
// baseflow_Model.setOrganizationName(args[2]);//Supervising organization of the station (only used for STORET stations)
// baseflow_Model.setStationID(args[3]); //The station ID used to retrieve the station's flow data
// baseflow_Model.setDrainageArea(Double.parseDouble(args[4])); //The drainage area of the basin to the site of interest in square miles
// baseflow_Model.setModelType(args[5]); //The baseflow model to be run ("BFLOW" by the SWAT group or "HYSEP" by USGS)
// baseflow_Model.setBeginDate(args[6]); //Begin date of analysis
// baseflow_Model.setEndDate(args[7]); //End date of analysis
//Run model
baseflow_Model.run();
}
}