gui15minTimeseries_Model.java [src/java/cfa] Revision: cb4f727e2ce1fb1fddb7c6c6259950fdaed2df8b Date: Fri Jul 11 16:39:28 MDT 2014
package cfa;
import java.awt.Color;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
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.general.SeriesException;
import org.jfree.data.time.Day;
import org.jfree.data.time.Minute;
import org.jfree.data.time.TimeSeries;
/**
* Last Updated: 10-July-2014
* @author Tyler Wible
* @since 23-June-2014
*/
public class gui15minTimeseries_Model {
String mainFolder = "C:/Projects/TylerWible/CodeDirectories/NetBeans/CSIP/data/CFA/Timeseries";
String database = "USGS";//"CDWR";//"STORET";//"UserData";//
String organizationName = "USGS";//"Co. Division of Water Resources";//"Colorado Dept. of Public Health & Environment";//
String stationID = "06752260";//"CLAGRECO";//"000028";//
String stationName = "CACHE LA POUDRE RIVER AT FORT COLLINS, CO";//"Cache La Poudre Near Greeley";//"BIG THOMPSON R NEAR MOUTH";//
String beginDate = "";//yyyy-MM-dd
String endDate = "";//yyyy-MM-dd
String userData = "";//"Date\tFlow\n1999-04-29 00:00\t8.3\n1999-05-09 00:00\t60.2\n1999-05-29 00:00\t20.1";//
boolean mergeDatasets = false;//true;//
String mergeMethod = "user";//"public";//"max";//"average";//"min";//
//Outputs
String len = "-1";
String start = "?";
String end = "?";
String units = "?";
String dataSource = "?";
double max = -1;
double min = -1;
double range = -1;
double mean = -1;
double standardDeviation = -1;
//Gets
public File getOutputSummary() {
return new File(mainFolder, "timeseries15min_summary.csv");
}
public String getGraph() {
return "timeseries15min_graph.jpg";
}
public File getTimeseriesOutput(){
//This output file is for use with JSHighCharts
return new File(mainFolder, "timeseries15min_graph.out");
}
public String getLen() {
return len;
}
public String getStart() {
return start;
}
public String getEnd() {
return end;
}
public String getUnits() {
return units;
}
public String getDataSource(){
return dataSource;
}
public String getMax() {
return String.valueOf(max);
}
public String getMin() {
return String.valueOf(min);
}
public String getRange() {
return String.valueOf(range);
}
public String getMean() {
return String.valueOf(mean);
}
public String getStandardDeviation() {
return String.valueOf(standardDeviation);
}
//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 setBeginDate(String beginDate) {
this.beginDate = beginDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
// public void setPeriod1Begin(String period1Begin) {
// this.period1Begin = period1Begin;
// }
// public void setPeriod1End(String period1End) {
// this.period1End = period1End;
// }
// public void setPeriod2Begin(String period2Begin) {
// this.period2Begin = period2Begin;
// }
// public void setPeriod2End(String period2End) {
// this.period2End = period2End;
// }
// public void setPeriod3Begin(String period3Begin) {
// this.period3Begin = period3Begin;
// }
// public void setPeriod3End(String period3End) {
// this.period3End = period3End;
// }
// public void setMedianTF(boolean medianTF) {
// this.medianTF = medianTF;
// }
public void setUserData(String userData) {
this.userData = userData;
}
public void setMergeDatasets(boolean mergeDatasets) {
this.mergeDatasets = mergeDatasets;
}
public void setMergeMethod(String mergeMethod) {
this.mergeMethod = mergeMethod;
}
/**
* Main statistics function calls other functions to calculate each statistic value then stores the results as global variables
* @param sortedData_combined data on which statistical values are desired
*/
private void CalculateDailyStatistics(String[][] flowData) throws IOException {
//Calculate flow statistics for the entire analysis period
ArrayList<Double> allData = new ArrayList<Double>();
for(int i=0; i<flowData.length; i++){
allData.add(Double.parseDouble(flowData[i][1]));
}
String[][] resultSummary = new String[6][1];
resultSummary[0][0] = "Method";
resultSummary[1][0] = "Maximum";
resultSummary[2][0] = "Minimum";
resultSummary[3][0] = "Range";
resultSummary[4][0] = "Mean";
resultSummary[5][0] = "Standard Deviation";
resultSummary = CalculateStatistics(allData, resultSummary, "All Data " + start + " to " + end);
max = Double.parseDouble(resultSummary[1][1]);
min = Double.parseDouble(resultSummary[2][1]);
range = Double.parseDouble(resultSummary[3][1]);
mean = Double.parseDouble(resultSummary[4][1]);
standardDeviation = Double.parseDouble(resultSummary[5][1]);
//Calculate Flow statistics for each day in time period
DoubleArray doubleArray = new DoubleArray();
boolean moreDays = flowData.length > 0;
String currentDay = flowData[0][0].substring(0,10);
String finalDay = flowData[flowData.length - 1][0].substring(0,10);
while(moreDays){
//Get current year's data and calculate it's statistics
ArrayList<Double> partialData = doubleArray.getDaysData(flowData, currentDay);
resultSummary = CalculateStatistics(partialData, resultSummary, currentDay);
String nextDay = doubleArray.getNextDay(currentDay);
if(finalDay.compareToIgnoreCase(String.valueOf(nextDay)) >= 0){
currentDay = String.valueOf(nextDay);
}else{
moreDays = false;
}
}
writeSummary(resultSummary);
}
private String[][] CalculateStatistics(ArrayList<Double> dataList, String[][] resultSummary, String header){
DoubleMath doubleMath = new DoubleMath();
DoubleArray doubleArray = new DoubleArray();
double maxVal = doubleMath.max(dataList);
double minVal = doubleMath.min(dataList);
double rangeVal = maxVal - minVal;
double aveVal = doubleMath.Average(dataList);
double stDev = doubleMath.StandardDeviationSample(dataList);
//Append current results to summary
String[] resultArray = {header,
String.valueOf(doubleMath.round(maxVal,3)),
String.valueOf(doubleMath.round(minVal,3)),
String.valueOf(doubleMath.round(rangeVal,3)),
String.valueOf(doubleMath.round(aveVal,3)),
String.valueOf(doubleMath.round(stDev,3))};
resultSummary = doubleArray.appendcolumn_Matrix(resultSummary, resultArray);
return resultSummary;
}
/**
* Graph the time series and user data and save the resulting graph to the specified location
* @param sortedData the String[][] containing sorted data for the time series
* (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm if
* timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
* @param sortedData_user the String[][] containing sorted user data for the
* time series (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm
* if timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
* @param yAxisTitle the String label for the y axis of the graph
*/
private void createTimeseriesGraph(String[][] sortedData,
String[][] sortedData_user) throws ParseException, IOException {
//Change analysis period dates into Date objects
// SimpleDateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd");
// Date period1Begin_date = new Date();
// Date period1End_date = new Date();
// Date period2Begin_date = new Date();
// Date period2End_date = new Date();
// Date period3Begin_date = new Date();
// Date period3End_date = new Date();
//
// if(!period1Begin.isEmpty() && !period1End.isEmpty()){
// period1Begin_date = desiredDateFormat.parse(period1Begin);
// period1End_date = desiredDateFormat.parse(period1End);
// }
// if(!period2Begin.isEmpty() && !period2End.isEmpty()){
// period2Begin_date = desiredDateFormat.parse(period2Begin);
// period2End_date = desiredDateFormat.parse(period2End);
// }
// if(!period3Begin.isEmpty() && !period3End.isEmpty()){
// period3Begin_date = desiredDateFormat.parse(period3Begin);
// period3End_date = desiredDateFormat.parse(period3End);
// }
//Create TimeSeries graph of merged data
TimeSeries series = new TimeSeries(stationID + ": Data");
// ArrayList<Double> period1 = new ArrayList<Double>();
// ArrayList<Double> period2 = new ArrayList<Double>();
// ArrayList<Double> period3 = new ArrayList<Double>();
DateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String[][] graphData = new String[sortedData.length][2];
for(int i=0; i < sortedData.length; i++) {
double value = Double.parseDouble(sortedData[i][1]);
Date currentDate = desiredDateFormat.parse(sortedData[i][0]);
Minute date = new Minute(currentDate);
try{
series.add(date, value);
graphData[i][0] = sortedData[i][0];
graphData[i][1] = sortedData[i][1];
}catch(SeriesException e){
//Some times there are multiple data points for a single yyyy-MM-dd HH:mm so catch the error here and continue
graphData[i][0] = "1900-01-01 00:00";
graphData[i][1] = "-1";
}
// //Check periods
// Date newDate = desiredDateFormat.parse(tmpStr);
// if(newDate.compareTo(period1Begin_date) >= 0 && newDate.compareTo(period1End_date) <= 0){
// period1.add(value);
// }else if(newDate.compareTo(period2Begin_date) >= 0 && newDate.compareTo(period2End_date) <= 0){
// period2.add(value);
// }else if(newDate.compareTo(period3Begin_date) >= 0 && newDate.compareTo(period3End_date) <= 0){
// period3.add(value);
// }
}
//Create Time Series graph of user data
TimeSeries series2 = new TimeSeries(stationID + ": User Data");
for(int i=0; i < sortedData_user.length; i++) {
double value = Double.parseDouble(sortedData_user[i][1]);
Date currentDate = desiredDateFormat.parse(sortedData_user[i][0]);
Day date = new Day(currentDate);
series2.add(date, value);
}
//Output XY data for use with JHighCharts
DoubleArray doubleArray = new DoubleArray();
doubleArray.writeTimeSeries(mainFolder, graphData, "15-min", getTimeseriesOutput().getName(), false);
//Create renderer, and axis for timeseries graph
Graphing graphing = new Graphing();
XYPlot plotTime = new XYPlot();
boolean showLegend = false;
//Create user data points
if(sortedData_user.length != 0){//only show user points if it is not zero
plotTime = graphing.graphTimeData(plotTime, series, true, Color.BLUE, false, false, false, true, 0);
plotTime = graphing.graphTimeData(plotTime, series2, true, Color.DARK_GRAY, false, false, false, true, 1);
showLegend = true;
}else{
plotTime = graphing.graphTimeData(plotTime, series, true, Color.BLUE, false, false, false, true, 0);
}
// //Create period analysis average lines
// if(!period1Begin.isEmpty() && !period1End.isEmpty()){
// CalculateStatistics(period1,"period1");
// this.len_period1 = String.valueOf(period1.size());
// double value = mean_period1;
// String label = "Average";
// if(medianTF){
// value = median_period1;
// label = "Median";
// }
// TimeSeries periodSeries = new TimeSeries("Period 1 " + label + ": " + value + " " + units);
//
// //Set Start Point
// double d = Double.parseDouble(period1Begin.substring(8));
// double m = Double.parseDouble(period1Begin.substring(5,7));
// double y = Double.parseDouble(period1Begin.substring(0,4));
// int day = (int)d;
// int month = (int)m;
// int year = (int)y;
// Day date = new Day(day,month,year);//day,month,year
// periodSeries.add(date, value);
//
// //Set End Point
// d = Double.parseDouble(period1End.substring(8));
// m = Double.parseDouble(period1End.substring(5,7));
// y = Double.parseDouble(period1End.substring(0,4));
// day = (int)d;
// month = (int)m;
// year = (int)y;
// date = new Day(day,month,year);//day,month,year
// periodSeries.add(date, value);
//
// plotTime = graphing.graphTimeData(plotTime, periodSeries, true, Color.RED, false, false, true, true, 2);
// showLegend = true;
// }
// if(!period2Begin.isEmpty() && !period2End.isEmpty()){
// CalculateStatistics(period2,"period2");
// this.len_period2 = String.valueOf(period2.size());
// double value = mean_period2;
// String label = "Average";
// if(medianTF){
// value = median_period2;
// label = "Median";
// }
// TimeSeries periodSeries = new TimeSeries("Period 2 " + label + ": " + value + " " + units);
//
// //Set Start Point
// double d = Double.parseDouble(period2Begin.substring(8));
// double m = Double.parseDouble(period2Begin.substring(5,7));
// double y = Double.parseDouble(period2Begin.substring(0,4));
// int day = (int)d;
// int month = (int)m;
// int year = (int)y;
// Day date = new Day(day,month,year);//day,month,year
// periodSeries.add(date, value);
//
// //Set End Point
// d = Double.parseDouble(period2End.substring(8));
// m = Double.parseDouble(period2End.substring(5,7));
// y = Double.parseDouble(period2End.substring(0,4));
// day = (int)d;
// month = (int)m;
// year = (int)y;
// date = new Day(day,month,year);//day,month,year
// periodSeries.add(date, value);
//
// plotTime = graphing.graphTimeData(plotTime, periodSeries, true, Color.BLACK, false, false, true, true, 3);
// showLegend = true;
// }
// if(!period3Begin.isEmpty() && !period3End.isEmpty()){
// CalculateStatistics(period3,"period3");
// this.len_period3 = String.valueOf(period3.size());
// double value = mean_period3;
// String label = "Average";
// if(medianTF){
// value = median_period3;
// label = "Median";
// }
// TimeSeries periodSeries = new TimeSeries("Period 3 " + label + ": " + value + " " + units);
//
// //Set Start Point
// double d = Double.parseDouble(period3Begin.substring(8));
// double m = Double.parseDouble(period3Begin.substring(5,7));
// double y = Double.parseDouble(period3Begin.substring(0,4));
// int day = (int)d;
// int month = (int)m;
// int year = (int)y;
// Day date = new Day(day,month,year);//day,month,year
// periodSeries.add(date, value);
//
// //Set End Point
// d = Double.parseDouble(period3End.substring(8));
// m = Double.parseDouble(period3End.substring(5,7));
// y = Double.parseDouble(period3End.substring(0,4));
// day = (int)d;
// month = (int)m;
// year = (int)y;
// date = new Day(day,month,year);//day,month,year
// periodSeries.add(date, value);
//
// plotTime = graphing.graphTimeData(plotTime, periodSeries, true, Color.LIGHT_GRAY, false, false, true, true, 4);
// showLegend = true;
// }
//Create Y Axis
ValueAxis rangeTime = new NumberAxis("Discharge [cfs]");
plotTime.setRangeAxis(0, rangeTime);
//Create X Axis
DateAxis domainTime = new DateAxis("Date");
domainTime.setLowerMargin(0.05);
domainTime.setUpperMargin(0.05);
plotTime.setDomainAxis(0, domainTime);
//Set extra plot preferences
plotTime = graphing.setTimeAxisPreferences(plotTime);
//Create the chart with the plot and a legend
String graphTitle = "Time Series for " + database + " Station " + stationID + "; " + stationName;
JFreeChart chart = new JFreeChart(graphTitle, graphing.titleFont, plotTime, showLegend);
//Set legend Font
if(showLegend){
LegendTitle legendTitle = chart.getLegend();
legendTitle.setItemFont(graphing.masterFont);
}
//Save resulting graph for use later
try{
String path = mainFolder + File.separator + getGraph();
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("JFreeChart created properly at: " + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* 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);
}
/**
* Writes out the dynamically created stats summary of the 15min data
* @param resultsSummary string array to be written as each line and column of the csv file
* @throws IOException
*/
private void writeSummary(String[][] resultsSummary) throws IOException{
//Open a file writer for the summary of the flow statistcs
String path = mainFolder + File.separator + getOutputSummary().getName();
FileWriter newFile = new FileWriter(path, false);
PrintWriter writer = new PrintWriter(newFile);
for(int i=0; i<resultsSummary.length; i++){
String currentLine = resultsSummary[i][0];
for(int j=1; j<resultsSummary[i].length; j++){
currentLine = currentLine + "," + resultsSummary[i][j];
}
writer.printf("%s" + "%n", currentLine);
}
//Close file writer
newFile.close();
writer.close();
System.out.println("Text File located at:\t" + path);
}
public void run() throws IOException, InterruptedException, ParseException, Exception{
//If no date input, make it the maximum of available data
if(beginDate == null || beginDate.equalsIgnoreCase("")){
beginDate = "2007-10-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.extractInstantaneousFlowData(mainFolder, database, 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.read15minUserFile(userData, "flow", beginDate, endDate);
}
//Sort the Data by date to remove duplicate date entries
DoubleArray doubleArray = new DoubleArray();
Arrays.sort(sortableData, new DateComparator());
Arrays.sort(sortableData_user, new DateComparator());
//Merge the two datasets (if user data is empty nothing will be merged)
String[][] sortedData_combined = doubleArray.mergeData(sortableData, sortableData_user, mergeMethod);
if(sortedData_combined.length == 0){
ArrayList<String> errorMessage = new ArrayList<String>();
if(sortableData.length == 0){
errorMessage.add("There is no available 15-minute (instantaneous) 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(sortableData_user.length == 0){
errorMessage.add("There is no available 15-minute (instantaneous) uploaded data for station '" + stationID + "' and the specified date range");
}
writeError(errorMessage);
}
//Save analysis results
this.start = sortedData_combined[0][0];
this.end = sortedData_combined[sortedData_combined.length - 1][0];
//Calculate stats of data
CalculateDailyStatistics(sortedData_combined);
//Graph the timeseries data
createTimeseriesGraph(sortedData_combined, sortableData_user);
//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("CDWR")){
this.dataSource = "Stream flow data courtesy of the Colorado Division of Water Resources, CDWR. http://www.dwr.state.co.us accessed: " + today;
}
this.len = String.valueOf(sortedData_combined.length);
this.units = "cfs";
}
public static void main(String[] args) throws IOException, InterruptedException, Exception {
gui15minTimeseries_Model Timeseries15min_Model = new gui15minTimeseries_Model();
//Run model
Timeseries15min_Model.run();
}
}