guiTimeseries_Model.java [src/java/cfa] Revision: 1b69320646d4eeb4b67833c13ef5e0bb175788f9 Date: Mon Sep 30 15:04:28 MDT 2013
package cfa;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
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.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.Month;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.Year;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* Last Updated: 26-August-2013
* @author Tyler Wible
* @since 24-June-2011
*/
public class guiTimeseries_Model {
//Inputs
String mainFolder = "C:/Projects/TylerWible/CodeDirectories/NetBeans/CSIP/data/CFA";
String organizationName = "USGS";//"Colorado Dept. of Public Health & Environment";//
String stationID = "06764880";//"000028";//
String stationName = "South Platte River at Roscoe, Nebr.";//"BIG THOMPSON R NEAR MOUTH";//
String wqTest = "flow";//"00600 Total nitrogen, water, unfiltered, milligrams per liter, mg/L";
String beginDate = "";
String endDate = "";
String timeStep = "Daily";//"Yearly";//"Monthly";//
String method = "Max";//"Min";//"Average";//"Total";//
String userData = "";//"Date\tFlow\n1999-04-29\t8.3\n1999-05-09\t60.2\n1999-05-29\t20.1";//
boolean mergeDatasets = false;//true;//
String mergeMethod = "user";//"public";//"max";//"average";//"min";//
//Outputs
String len = "-1";
String start = "?";
String end = "?";
String units = "?";
double max = -1;
double min = -1;
double upperQuartile = -1;
double lowerQuartile = -1;
double median = -1;
double mean = -1;
double standardDeviation = -1;
//Gets
public File getParagraph() {
return new File(mainFolder, "timeseries_summary.txt");
}
public String getGraph() {
return "timeseries_graph.jpg";
}
public String getBoxplot() {
return "timeseries_boxplot.jpg";
}
public String getLen(){
return len;
}
public String getStart(){
return start;
}
public String getEnd(){
return end;
}
public String getUnits(){
return units;
}
public String getMax(){
return String.valueOf(max);
}
public String getMin(){
return String.valueOf(min);
}
public String getUpperQuartile(){
return String.valueOf(upperQuartile);
}
public String getLowerQuartile(){
return String.valueOf(lowerQuartile);
}
public String getMedian(){
return String.valueOf(median);
}
public String getMean(){
return String.valueOf(mean);
}
public String getStandardDeviation(){
return String.valueOf(standardDeviation);
}
//Sets
public void setMainFolder(String mainFolder) {
this.mainFolder = mainFolder;
}
public void setOrganizationName(String organizationName) {
this.organizationName = organizationName;
}
public void setBeginDate(String beginDate) {
this.beginDate = beginDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
public void setStationID(String stationID) {
this.stationID = stationID;
}
public void setWQtest(String wqTest) {
this.wqTest = wqTest;
}
public void setTimeStep(String timeStep) {
this.timeStep = timeStep;
}
public void setMethod(String method) {
this.method = method;
}
public void setUserData(String userData) {
this.userData = userData;
}
public void setMergeDatasets(boolean mergeDatasets) {
this.mergeDatasets = mergeDatasets;
}
public void setMergeMethod(String mergeMethod) {
this.mergeMethod = mergeMethod;
}
/**
* Computes the daily/monthly/yearly max/min/average/total of the daily dataset provided
* @param dailyData a string array with column1 = dates (yyyy-mm-dd format), column2 = value
* @param timeStep the desired timestep "daily", "monthly", or "yearly"
* @param method the desired method "max", "min", "average", or "total"
* @return a new string[][] with column 1 = dates (yyyy-mm-dd format for "daily" timeStep,
* yyyy-mm format for "monthly" timeStep, and yyyy format for "yearly" timeStep) and column2 = values
*/
public String[][] computeFlowMethod(String[][] dailyData, String timeStep, String method){
String[][] newData = null;
if(timeStep.equalsIgnoreCase("daily")){
return dailyData;
}else if(timeStep.equalsIgnoreCase("monthly")){
//Compute the method on the unique monthYears
newData = computeMethod(dailyData, method, 7);
}else if(timeStep.equalsIgnoreCase("yearly")){
//Compute the method on the unique monthYears
newData = computeMethod(dailyData, method, 4);
}
return newData;
}
/**
* Computes the method on the dailyData provided substring-ing the dates from 0-dateLimit to
* get a unique set of time periods on which to perform the method
* @param dailyData a string array with column1 = dates (yyyy-mm-dd format), column2 = value
* @param method the desired method "max", "min", "average", or "total"
* @param dateLimit an integer for the limit of the date substring (typically 4 or 7 for the
* format yyyy-mm-dd resulting in yyyy and yyyy-mm respectively)
* @return a new string[][] with column 1 = dates (yyyy-mm-dd format for "daily" timeStep,
* yyyy-mm format for "monthly" timeStep, and yyyy format for "yearly" timeStep) and column2 = values
*/
public String[][] computeMethod(String[][] dailyData, String method, int dateLimit){
DoubleMath doubleMath = new DoubleMath();
//Find the unique set of months/years for which the method is desired for
ArrayList<String> uniqueTimeStep = new ArrayList<String>();
String previousMonthYear = dailyData[0][0].substring(0,dateLimit);
uniqueTimeStep.add(previousMonthYear);
for(int i=1; i<dailyData.length; i++){
//Check if current monthYear is the same or different from the previous
String currentMonthYear = dailyData[i][0].substring(0,dateLimit);
if(!previousMonthYear.equalsIgnoreCase(currentMonthYear)){
uniqueTimeStep.add(currentMonthYear);
previousMonthYear = currentMonthYear;
}
}
//Loop through daily data, pull out each uniqueTimeStep's dataset and perform the method on that dataset
String[][] newData = new String[uniqueTimeStep.size()][2];
ArrayList<Double> currentMonthData = new ArrayList<Double>();
int ctr=0;
for(int i=0; i<dailyData.length; i++){
if(uniqueTimeStep.get(ctr).equals(dailyData[i][0].substring(0, dateLimit))){
//If current data = current month, add it to the dataset
currentMonthData.add(Double.parseDouble(dailyData[i][1]));
}else{
//If current data != current month, calculate method on the current month's dataset, save the result and reset the dataset
newData[ctr][0] = uniqueTimeStep.get(ctr);
if(method.equalsIgnoreCase("max")){
newData[ctr][1] = String.valueOf(doubleMath.max(currentMonthData));
}else if(method.equalsIgnoreCase("average")){
newData[ctr][1] = String.valueOf(doubleMath.Average(currentMonthData));
}else if(method.equalsIgnoreCase("min")){
newData[ctr][1] = String.valueOf(doubleMath.min(currentMonthData));
}else if(method.equalsIgnoreCase("total")){
newData[ctr][1] = String.valueOf(doubleMath.sum(currentMonthData));
}
//Reset the dataset and add the current data to it as the new month's data
currentMonthData.clear();
currentMonthData.add(Double.parseDouble(dailyData[i][1]));
ctr++;
}
//If on the last point calculate the method on the current dataset
if(i == dailyData.length-1){
newData[ctr][0] = uniqueTimeStep.get(ctr);
if(method.equalsIgnoreCase("max")){
newData[ctr][1] = String.valueOf(doubleMath.max(currentMonthData));
}else if(method.equalsIgnoreCase("average")){
newData[ctr][1] = String.valueOf(doubleMath.Average(currentMonthData));
}else if(method.equalsIgnoreCase("min")){
newData[ctr][1] = String.valueOf(doubleMath.min(currentMonthData));
}else if(method.equalsIgnoreCase("total")){
newData[ctr][1] = String.valueOf(doubleMath.sum(currentMonthData));
}
}
}
return newData;
}
/**
* Main statistics function calls other functions to calculate each statistic value then returns a list of statistics values
* @param dataList data on which statistical values are desired
* @return list of statistical values (min##max##upperQuartile##lowerQuartile##median##mean##standardDeviation)
*/
public void CalculateStatistics(String[][] sortedData) {
DoubleMath doubleMath = new DoubleMath();
//get data
double[] dataList = new double[sortedData.length];
for(int i=0; i<sortedData.length; i++){
dataList[i] = Double.parseDouble(sortedData[i][1]);
}
//Call Max function
max = Math.round(doubleMath.Min_Max(dataList, true)*1000);
max = max/1000;
//Call Min function
min = Math.round(doubleMath.Min_Max(dataList, false)*1000);
min = min/1000;
//Call Upper Quartile function
upperQuartile = Math.round(doubleMath.Percentile_function(dataList,0.75)*1000);
upperQuartile = upperQuartile/1000;
//Call Lower Quartile function
lowerQuartile = Math.round(doubleMath.Percentile_function(dataList,0.25)*1000);
lowerQuartile = lowerQuartile/1000;
//Call Median function
median = Math.round(doubleMath.Median(dataList)*1000);
median = median/1000;
//Call Mean function
mean = Math.round(doubleMath.Average(dataList)*1000);
mean = mean/1000;
//Call standard deviation
standardDeviation = Math.round(doubleMath.StandardDeviationSample(dataList)*1000);
standardDeviation = standardDeviation/1000;
}
/**
* Graph the timeseries and user data and save the resulting graph to the specified location
* @param sortedData the String[][] containing sorted data for the timeseries
* (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
* timeseries (column 1 = dates (yyyy-mm-dd if timeStep = "daily", yyyy-mm
* if timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
* @param color the color of the merged dataset (Java.Color)
* @param color2 the color of the user dataset (Java.Color)
* @param showLine a boolean, if true lines will be shown on the graph, if false only shapes for the data
* @param yAxisTitle the String label for the y axis of the graph
*/
public void createTimeseriesGraph(String[][] sortedData,
String[][] sortedData_user,
Color color,
Color color2,
boolean showLine,
String yAxisTitle){
DurationCurve durationCurve = new DurationCurve();
//Create TimeSeries graph of merged data
TimeSeries series = new TimeSeries(stationID + " Public Data");
for(int i=0; i < sortedData.length; i++) {
double value = Double.parseDouble(sortedData[i][1]);
String tmpStr = sortedData[i][0];
if(timeStep.equalsIgnoreCase("daily")){
double d = Double.parseDouble(tmpStr.substring(8));
double m = Double.parseDouble(tmpStr.substring(5,7));
double y = Double.parseDouble(tmpStr.substring(0,4));
int day = (int)d;
int month = (int)m;
int year = (int)y;
Day date = new Day(day,month,year);//day,month,year
series.add(date, value);
}else if(timeStep.equalsIgnoreCase("monthly")){
double m = Double.parseDouble(tmpStr.substring(5,7));
double y = Double.parseDouble(tmpStr.substring(0,4));
int month = (int)m;
int year = (int)y;
Month date = new Month(month,year);//month,year
series.add(date, value);
}else if(timeStep.equalsIgnoreCase("yearly")){
double y = Double.parseDouble(tmpStr.substring(0,4));
int year = (int)y;
Year date = new Year(year);//year
series.add(date, value);
}
}
//Create TimeSeries graph of user data
TimeSeries series2 = new TimeSeries(stationID + " User Data");
for(int i=0; i < sortedData_user.length; i++) {
double value = Double.parseDouble(sortedData_user[i][1]);
String tmpStr = sortedData_user[i][0];
if(timeStep.equalsIgnoreCase("daily")){
double d = Double.parseDouble(tmpStr.substring(8));
double m = Double.parseDouble(tmpStr.substring(5,7));
double y = Double.parseDouble(tmpStr.substring(0,4));
int day = (int)d;
int month = (int)m;
int year = (int)y;
Day date = new Day(day,month,year);//day,month,year
series2.add(date, value);
}else if(timeStep.equalsIgnoreCase("monthly")){
double m = Double.parseDouble(tmpStr.substring(5,7));
double y = Double.parseDouble(tmpStr.substring(0,4));
int month = (int)m;
int year = (int)y;
Month date = new Month(month,year);//month,year
series2.add(date, value);
}else if(timeStep.equalsIgnoreCase("yearly")){
double y = Double.parseDouble(tmpStr.substring(0,4));
int year = (int)y;
Year date = new Year(year);//year
series2.add(date, value);
}
}
//Create renderer, and axis for timeseries graph
XYPlot plotTime = new XYPlot();
TimeSeriesCollection dataset = new TimeSeriesCollection(series);
TimeSeriesCollection dataset2 = new TimeSeriesCollection(series2);
XYItemRenderer rendererTime = new XYLineAndShapeRenderer(showLine, !showLine);
XYItemRenderer rendererTime2 = new XYLineAndShapeRenderer(showLine, !showLine);
rendererTime.setSeriesPaint(0, color);
rendererTime2.setSeriesPaint(0, color2);
if(!showLine){
rendererTime.setSeriesShape(0, new Ellipse2D.Double(-2.0, 2.0, 4.0, 4.0));
rendererTime2.setSeriesShape(0, new Ellipse2D.Double(-2.0, 2.0, 4.0, 4.0));
}
//Create X Axis
DateAxis domainTime = new DateAxis("Date");
domainTime.setLowerMargin(0.05);
domainTime.setUpperMargin(0.05);
plotTime.setDomainAxis(0, domainTime);
//Create Y Axis
ValueAxis rangeTime = new NumberAxis(yAxisTitle);
plotTime.setRangeAxis(0, rangeTime);
// Set the timeseries line data, renderer, and axis into plot
plotTime.setDataset(1, dataset);
plotTime.setDataset(0, dataset2);
plotTime.setRenderer(1, rendererTime);
plotTime.setRenderer(0, rendererTime2);
//Map the line to the first Domain and first Range
plotTime.mapDatasetToDomainAxis(0, 0);
plotTime.mapDatasetToRangeAxis(0, 0);
plotTime.setForegroundAlpha(0.5f);
//Set extra plot preferences
plotTime.setOutlinePaint(Color.black);
plotTime.setDomainGridlinePaint(Color.black);
plotTime.setRangeGridlinePaint(Color.black);
plotTime.setRangeMinorGridlinesVisible(true);
plotTime.setRangeMinorGridlinePaint(Color.gray);
setAxisFonts(plotTime);
//Create the chart with the plot and a legend
JFreeChart chart = new JFreeChart("TimeSeries for Station: " + stationID + "; " + stationName, durationCurve.titleFont, plotTime, true);
//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.");
}
}
/**
* Graph the timeseries data and save the resulting graph to the specified location
* @param sortedData the String[][] containing sorted data for the timeseries (column 1 = dates (yyyy-mm-dd if timeStep = "daily",
* yyyy-mm if timeStep = "monthly", yyyy if timeStep = "yearly") column 2 = value
* @param showLine a boolean, if true lines will be shown on the graph, if false only shapes for the data
* @param lineColor the color of the line (Java.Color)
* @param yAxisTitle the String label for the y axis of the graph
*/
public void createTimeseriesGraph(String[][] sortedData,
Color lineColor,
boolean showLine,
String yAxisTitle){
DurationCurve durationCurve = new DurationCurve();
//Create TimeSeries graph
TimeSeries series = new TimeSeries(stationID + " Data Time Series");
for(int i=0; i < sortedData.length; i++) {
double value = Double.parseDouble(sortedData[i][1]);
String tmpStr = sortedData[i][0];
if(timeStep.equalsIgnoreCase("daily")){
double d = Double.parseDouble(tmpStr.substring(8));
double m = Double.parseDouble(tmpStr.substring(5,7));
double y = Double.parseDouble(tmpStr.substring(0,4));
int day = (int)d;
int month = (int)m;
int year = (int)y;
Day date = new Day(day,month,year);//day,month,year
series.add(date, value);
}else if(timeStep.equalsIgnoreCase("monthly")){
double m = Double.parseDouble(tmpStr.substring(5,7));
double y = Double.parseDouble(tmpStr.substring(0,4));
int month = (int)m;
int year = (int)y;
Month date = new Month(month,year);//month,year
series.add(date, value);
}else if(timeStep.equalsIgnoreCase("yearly")){
double y = Double.parseDouble(tmpStr.substring(0,4));
int year = (int)y;
Year date = new Year(year);//year
series.add(date, value);
}
}
//Create renderer, and axis for timeseries graph
XYPlot plotTime = new XYPlot();
TimeSeriesCollection dataset = new TimeSeriesCollection(series);
XYItemRenderer rendererTime = new XYLineAndShapeRenderer(showLine, !showLine);
rendererTime.setSeriesPaint(0, lineColor);
if(!showLine){
rendererTime.setSeriesShape(0, new Ellipse2D.Double(-2.0, 2.0, 4.0, 4.0));
}
//Create X Axis
DateAxis domainTime = new DateAxis("Date");
domainTime.setLowerMargin(0.05);
domainTime.setUpperMargin(0.05);
plotTime.setDomainAxis(0, domainTime);
//Create Y Axis
ValueAxis rangeTime = new NumberAxis(yAxisTitle);
plotTime.setRangeAxis(0, rangeTime);
// Set the timeseries line data, renderer, and axis into plot
plotTime.setDataset(0, dataset);
plotTime.setRenderer(0, rendererTime);
//Map the line to the first Domain and first Range
plotTime.mapDatasetToDomainAxis(0, 0);
plotTime.mapDatasetToRangeAxis(0, 0);
plotTime.setForegroundAlpha(0.5f);
//Set extra plot preferences
plotTime.setOutlinePaint(Color.black);
plotTime.setDomainGridlinePaint(Color.black);
plotTime.setRangeGridlinePaint(Color.black);
plotTime.setRangeMinorGridlinesVisible(true);
plotTime.setRangeMinorGridlinePaint(Color.gray);
setAxisFonts(plotTime);
//Create the chart with the plot and a legend
JFreeChart chart = new JFreeChart("TimeSeries for Station: " + stationID + "; " + stationName, durationCurve.titleFont, plotTime, false);
//Save resulting graph for use later
try{
String path = mainFolder + File.separator + getGraph();
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("JFreeChart created properly at: " + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* Creates a boxplot of the timeseries data to be displayed next to a summary of the statistics of the timeseries data
* @param yAxisTitle a String of the title for the Y axis of the boxplot
*/
public void createTimeseriesBoxplot(String[][] data, String yAxisTitle){
//Create boxplot of the timeseries data
DurationCurve durationCurve = new DurationCurve();
XYPlot plot = new XYPlot();
//Create X Axis
ValueAxis xAxis = new NumberAxis("");
xAxis.setRange(0, 10);
xAxis.setLabelFont(durationCurve.masterFont);
xAxis.setTickLabelFont(durationCurve.masterFont);
xAxis.setTickLabelsVisible(false);
plot.setDomainAxis(0, xAxis);
ValueAxis yAxis = new NumberAxis(yAxisTitle);
yAxis.setLabelFont(durationCurve.masterFont);
yAxis.setTickLabelFont(durationCurve.masterFont);
plot.setRangeAxis(0, yAxis);
//Calculate and add Median to dataset
XYSeries median_series = new XYSeries("Median");
median_series.add(5, median);
//Create median Line
XYDataset median_scatter = new XYSeriesCollection(median_series);
XYItemRenderer renderer_median = new XYLineAndShapeRenderer(false, true);
renderer_median.setSeriesShape(0, new Rectangle2D.Double(-4.0, 0.0, 8.0, 1));//new Ellipse2D.Double(-4, -4, 8, 8));
renderer_median.setSeriesPaint(0, Color.red);
renderer_median.setSeriesVisibleInLegend(0, false);
plot.setDataset(0, median_scatter);
plot.setRenderer(0, renderer_median);
//Create quartile Box shapes for the box plot
//Create XYSeries for the box shape
XYSeries shapeSeries = new XYSeries("Shape");
shapeSeries.add(5, lowerQuartile);
shapeSeries.add(5, upperQuartile);
//Create the quartile rectangle shape
XYDataset shapeDataset = new XYSeriesCollection(shapeSeries);
XYItemRenderer renderer_shape = new XYLineAndShapeRenderer(true, false);
Stroke thickness = new BasicStroke(10, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
renderer_shape.setSeriesStroke(0, thickness);
renderer_shape.setSeriesPaint(0, Color.blue);
renderer_shape.setSeriesVisibleInLegend(0, false);
plot.setDataset(1, shapeDataset);
plot.setRenderer(1, renderer_shape);
//Creates 1.5 * Interquartile Range (IQR) lines
//Create XYSeries for the min-max lines
double IQR = upperQuartile - lowerQuartile;
double lowerLimit = lowerQuartile - 1.5*IQR;
double upperLimit = upperQuartile + 1.5*IQR;
if(lowerLimit < min){
lowerLimit = min;
}
if(upperLimit > max){
upperLimit = max;
}
XYSeries lineSeries = new XYSeries("Line");
lineSeries.add(5, lowerLimit);
lineSeries.add(5, upperLimit);
//Create the 1.5*IQR lines
XYDataset lineDataset = new XYSeriesCollection(lineSeries);
XYItemRenderer lineRenderer = new XYLineAndShapeRenderer(true, true);
Stroke thickness2 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
lineRenderer.setSeriesStroke(0, thickness2);
lineRenderer.setSeriesShape(0, new Rectangle2D.Double(-10.0, 0.0, 20.0, 1));
lineRenderer.setSeriesPaint(0, Color.black);
lineRenderer.setSeriesVisibleInLegend(0, false);
plot.setDataset(2, lineDataset);
plot.setRenderer(2, lineRenderer);
//Calculate and create Outliers (# < lowerQuartile - 1.5*IQR or # > upperQuartile + 1.5*IQR)
//Calculate and create Extreme Outliers (# < lowerQuartile - 3*IQR or # > upperQuartile + 3*IQR)
XYSeries outliers = new XYSeries("Outliers");
XYSeries extremeOutliers = new XYSeries("Extreme Outliers");
for(int i=0; i<data.length; i++){
double value = Double.parseDouble(data[i][1]);
//Lower outliers
if(value < (lowerQuartile - 1.5*IQR) && value > (lowerQuartile - 3*IQR)){
outliers.add(5, value);
}
//Upper outliers
if(value > (upperQuartile + 1.5*IQR) && value < (lowerQuartile + 3*IQR)){
outliers.add(5, value);
}
//Extreme Lower outliers
if(value < (lowerQuartile - 3*IQR)){
extremeOutliers.add(5, value);
}
//Extreme Upper outliers
if(value > (lowerQuartile + 3*IQR)){
extremeOutliers.add(5, value);
}
}
//Create outlier scatter
XYDataset outlier_scatter = new XYSeriesCollection(outliers);
XYItemRenderer renderer_outlier = new XYLineAndShapeRenderer(false, true);
renderer_outlier.setSeriesShape(0, new Ellipse2D.Double(-2.0, 2.0, 4.0, 4.0));
renderer_outlier.setSeriesPaint(0, Color.DARK_GRAY);
if(outliers.isEmpty()){
renderer_outlier.setSeriesVisibleInLegend(0, false);
}
plot.setDataset(3, outlier_scatter);
plot.setRenderer(3, renderer_outlier);
//Create extreme outlier scatter
XYDataset extremeOutlier_scatter = new XYSeriesCollection(extremeOutliers);
XYItemRenderer renderer_ExtremeOutlier = new XYLineAndShapeRenderer(false, true);
renderer_ExtremeOutlier.setSeriesShape(0, new Ellipse2D.Double(-2.0, 2.0, 4.0, 4.0));
renderer_ExtremeOutlier.setSeriesPaint(0, Color.red);
if(extremeOutliers.isEmpty()){
renderer_ExtremeOutlier.setSeriesVisibleInLegend(0, false);
}
plot.setDataset(4, extremeOutlier_scatter);
plot.setRenderer(4, renderer_ExtremeOutlier);
//Put the line on the first Domain and first Range
plot.mapDatasetToDomainAxis(0, 0);
plot.mapDatasetToRangeAxis(0, 0);
//Set extra plot preferences
plot.setOutlinePaint(Color.black);
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.black);
//Create the chart with the plot and a legend
JFreeChart chart = new JFreeChart("Boxplot of Timeseries Data", durationCurve.titleFont, plot, true);
//Save resulting graph for use later
try{
String path = mainFolder + File.separator + getBoxplot();
ChartUtilities.saveChartAsJPEG(new File(path), chart, 200, 400);
System.out.println("JFreeChart created properly at: " + path);
}catch(IOException e){
System.err.println("A problem occurred while trying to creating the chart.");
}
}
/**
* This subfunction takes the provided plot and changes the fonts of the axis to a standardized new font
* @param plot the XYPlot containing the graph on which the fonts will be changed
* @return the original plot with the modifications to the axis fonts
*/
private XYPlot setAxisFonts(XYPlot plot){
DurationCurve durationCurve = new DurationCurve();
//Set Y axis fonts
ValueAxis yAxis = plot.getRangeAxis();
yAxis.setLabelFont(durationCurve.masterFont);
yAxis.setTickLabelFont(durationCurve.masterFont);
//Set X axis fonts
DateAxis xAxis = (DateAxis) plot.getDomainAxis();
xAxis.setLabelFont(durationCurve.masterFont);
xAxis.setTickLabelFont(durationCurve.masterFont);
return plot;
}
/**
* Writes out the dynamically created paragraph to be displayed to the user along with the LDC graph
* @param dynamicParagraph 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
*/
public void writeSummary(String[] dynamicParagraph, String partialpath) throws IOException{
String path = partialpath + File.separator + "timeseries_summary.txt";
FileWriter writer = new FileWriter(path, false);
PrintWriter print_line = new PrintWriter(writer);
//Output data to text file
for(int i = 0; i < dynamicParagraph.length; i++) {
print_line.printf("%s" + "%n", dynamicParagraph[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(String[] error) throws IOException{
//Output data to text file
String errorContents = error[0];
for(int i=1; i<error.length; i++){
errorContents = errorContents + "\n" + error[i];
}
throw new IOException("Error encountered. Please see the following message for details: \n" + errorContents);
}
/**
* Primary TimeSeries
* It calls the subfunctions based on user selection/inputs.
* Calls STORET or USGS database queries and their respective subfunctions
* @throws IOException
* @throws InterruptedException
*/
public void run() throws IOException, InterruptedException {
//Inputs
//assert args.length > 0;
//String mainFolder = args[0];
//String fileName = args[1];
//String organizationName = args[2];
//String stationID = args[3];
//String stationName = args[4];
//String wqTest = args[5];
//String beginDate = args[6];
//String endDate = args[7];
//String timeStep = args[8];
//String method = args[9];
//If no date input, make it the maximum of available data
if(beginDate == null || beginDate.equalsIgnoreCase("")){
beginDate = "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);
}
//Artificial limit due to the properties of the Jfreechart object "Date"
if(beginDate.compareToIgnoreCase("1900-01-01") < 0){
beginDate = "1900-01-01";
}
if(endDate.compareToIgnoreCase("1900-01-01") < 0){
endDate = "1900-01-01";
}
//Initialize graph variables
String[][] sortableData = new String[0][2];
String yAxisTitle = "y axis";
String graphUnits = "??";
Color color = Color.black, color2 = Color.black;
boolean showLine = true;
USGS_Data usgs_Data = new USGS_Data();
User_Data user_Data = new User_Data();
STORET_Data storet_Data = new STORET_Data();
DurationCurve durationCurve = new DurationCurve();
DoubleArray doubleArray = new DoubleArray();
if(mergeDatasets){
//Combine the datasets for an analysis
String[][] sortableData_user = user_Data.readUserFile(userData, beginDate, endDate);
if(wqTest.equalsIgnoreCase("flow")){
//Search for Flow Data
if(organizationName.equalsIgnoreCase("USGS")){
//Search for USGS flow data
sortableData = usgs_Data.USGS_read_FDC(stationID, beginDate, endDate);
//If there is minimal flow data try getting WQ-flow data
if(sortableData.length < 10){
//Retrieve WQ data from USGS website
String[][] WQData = usgs_Data.USGS_read_LDC(stationID);
//Extract USGS water quality code 00061 for dischage in cfs
String[][] WQFlow1 = usgs_Data.minimizeUSGSWQdata(WQData, "00061", beginDate, endDate);
//Extract USGS water quality code 30209 for discharge test in m^3/s (cms)
String[][] WQFlow2 = usgs_Data.minimizeUSGSWQdata(WQData, "30209", beginDate, endDate);
//Convert the m^3/s to ft^3/s
for(int i=0; i<WQFlow2.length; i++){
WQFlow2[i][1] = Double.toString((Double.parseDouble(WQFlow2[i][1])*(3.2808399*3.2808399*3.2808399)));
}
//combine the WQ flows (cfs and converted cms) into a single variable to be used with the Flowdata
String[][] WQDataflows = usgs_Data.mergeMinimizedWQdata(WQFlow1, WQFlow2);
//Combine flow data and WQ flow data into a variable of dates and flow values to be sorted
sortableData = usgs_Data.mergeMinimizedWQdata(sortableData, WQDataflows);
}
}else{
//Search for STORET flow data
String zip_location = storet_Data.downloadSTORET(mainFolder, organizationName, stationID, "flow", beginDate, endDate);
//Unzip results file and extract all flow data
sortableData = storet_Data.Unzip_STORETDownloadFiles(zip_location, "flow", true);
}
graphUnits = "cfs";
yAxisTitle = timeStep + " " + method + " Flow [" + graphUnits + "]";
color = Color.blue;
color2 = Color.DARK_GRAY;
showLine = true;
}else{
//Search for WQ data
if(organizationName.equalsIgnoreCase("USGS")){
//Search for USGS water quality data
//Pull the right portion of the wqTestcode
int endIndex = wqTest.lastIndexOf(", ");
if(endIndex == -1){
endIndex = wqTest.lastIndexOf("--");
}
String WQlabel = wqTest.substring(11,endIndex);//cut off the "98335 " part before the test name and the units after the name
WQlabel = WQlabel.split(",")[0];
wqTest = wqTest.substring(0,5);//pull just the 5 digit USGS WQ code
//Retrieve WQ data from USGS website
sortableData = usgs_Data.USGS_read_LDC(stationID);
sortableData = usgs_Data.minimizeUSGSWQdata(sortableData, wqTest, beginDate, endDate);
//Get Units and conversion for current WQ test
graphUnits = durationCurve.USGSwqUnits(wqTest);
yAxisTitle = timeStep + " " + method + " " + WQlabel + " [" + graphUnits + "]";
}else{
//Search for STORET flow data
String zip_location = storet_Data.downloadSTORET(mainFolder, organizationName, stationID, wqTest, beginDate, endDate);
//Unzip results file and extract all flow data
sortableData = storet_Data.Unzip_STORETDownloadFiles(zip_location, wqTest, true);
graphUnits = "mg/L";
yAxisTitle = timeStep + " " + method + " " + wqTest + " [" + graphUnits + "]";
}
color = Color.magenta;
color2 = Color.BLUE;
showLine = false;
}
//Check if any data exists
if(sortableData.length==0){
String[] errorMessage = {"There is no available data for station '" + stationID + "' and the specified date range"};
writeError(errorMessage);
}
if(sortableData_user.length==0){
String[] errorMessage = {"There is no available uploaded data for station '" + stationID + "' and the specified date range"};
writeError(errorMessage);
}
//Remove sort and remove duplicate days to aid in the graphing
String[][] sortedData = durationCurve.removeDuplicateDates(sortableData);
String[][] sortedData_user = durationCurve.removeDuplicateDates(sortableData_user);
//Perform analysis method on data
sortedData = computeFlowMethod(sortedData, timeStep, method);
sortedData_user = computeFlowMethod(sortedData_user, timeStep, method);
//Calculate stats of data
String[][] combinedData = doubleArray.mergeData(sortedData, sortedData_user, mergeMethod);
CalculateStatistics(combinedData);
//Graph the timeseries data
createTimeseriesGraph(combinedData, sortedData_user, color, color2, showLine, yAxisTitle);
createTimeseriesBoxplot(combinedData, yAxisTitle);
//Create dynamic summary paragraph
this.start = sortedData[0][0];
this.end = sortedData[sortedData.length - 1][0];
this.len = String.valueOf(sortedData.length);
this.units = graphUnits;
String[] dynamicParagraph = durationCurve.dynamicParagraph("Time Series Graph Overview: ", organizationName);
//Write dynamic Paragraph to text file
writeSummary(dynamicParagraph, mainFolder);
}else{
//Run one analysis on one dataset (either USGS, UserData, or STORET)
if(wqTest.equalsIgnoreCase("flow")){
//Search for Flow Data
if(organizationName.equalsIgnoreCase("USGS")){
//Search for USGS flow data
sortableData = usgs_Data.USGS_read_FDC(stationID, beginDate, endDate);
//If there is minimal flow data try getting WQ-flow data
if(sortableData.length < 10){
//Retrieve WQ data from USGS website
String[][] WQData = usgs_Data.USGS_read_LDC(stationID);
//Extract USGS water quality code 00061 for dischage in cfs
String[][] WQFlow1 = usgs_Data.minimizeUSGSWQdata(WQData, "00061", beginDate, endDate);
//Extract USGS water quality code 30209 for discharge test in m^3/s (cms)
String[][] WQFlow2 = usgs_Data.minimizeUSGSWQdata(WQData, "30209", beginDate, endDate);
//Convert the m^3/s to ft^3/s
for(int i=0; i<WQFlow2.length; i++){
WQFlow2[i][1] = Double.toString((Double.parseDouble(WQFlow2[i][1])*(3.2808399*3.2808399*3.2808399)));
}
//combine the WQ flows (cfs and converted cms) into a single variable to be used with the Flowdata
String[][] WQDataflows = usgs_Data.mergeMinimizedWQdata(WQFlow1, WQFlow2);
//Combine flow data and WQ flow data into a variable of dates and flow values to be sorted
sortableData = usgs_Data.mergeMinimizedWQdata(sortableData, WQDataflows);
}
}else if(organizationName.equalsIgnoreCase("UserData")){
//Find the user uploaded data file and uses this for a timeseries graph
sortableData = user_Data.readUserFile(userData, beginDate, endDate);
}else{
//Search for STORET flow data
String zip_location = storet_Data.downloadSTORET(mainFolder, organizationName, stationID, "flow", beginDate, endDate);
//Unzip results file and extract all flow data
sortableData = storet_Data.Unzip_STORETDownloadFiles(zip_location, "flow", true);
}
graphUnits = "cfs";
yAxisTitle = timeStep + " " + method + " Flow [" + graphUnits + "]";
color = Color.blue;
showLine = true;
}else{
//Search for WQ data
if(organizationName.equalsIgnoreCase("USGS")){
//Search for USGS water quality data
//Pull the right portion of the wqTestcode
int endIndex = wqTest.lastIndexOf(", ");
if(endIndex == -1){
endIndex = wqTest.lastIndexOf("--");
}
String WQlabel = wqTest.substring(11,endIndex);//cut off the "98335 " part before the test name and the units after the name
WQlabel = WQlabel.split(",")[0];
wqTest = wqTest.substring(0,5);//pull just the 5 digit USGS WQ code
//Retrieve WQ data from USGS website
sortableData = usgs_Data.USGS_read_LDC(stationID);
sortableData = usgs_Data.minimizeUSGSWQdata(sortableData, wqTest, beginDate, endDate);
//Get Units and conversion for current WQ test
graphUnits = durationCurve.USGSwqUnits(wqTest);
yAxisTitle = timeStep + " " + method + " " + WQlabel + " [" + graphUnits + "]";
}else if(organizationName.equalsIgnoreCase("UserData")){
//Find the user uploaded data file and uses this for a timeseries graph
sortableData = user_Data.readUserFile(userData, beginDate, endDate);
//Use the header to get the WQ test name
String[] headers = user_Data.getHeaders(userData);
graphUnits = durationCurve.USGSwqUnits(wqTest);//Because user uploaded file headers are wqTest
yAxisTitle = timeStep + " " + method + " " + headers[1] + " [" + graphUnits + "]";
}else{
//Search for STORET flow data
String zip_location = storet_Data.downloadSTORET(mainFolder, organizationName, stationID, wqTest, beginDate, endDate);
//Unzip results file and extract all flow data
sortableData = storet_Data.Unzip_STORETDownloadFiles(zip_location, wqTest, true);
graphUnits = "mg/L";
yAxisTitle = timeStep + " " + method + " " + wqTest + "' [" + graphUnits + "]";
}
color = Color.magenta;
showLine = false;
}
//Check if any data exists
if(sortableData.length==0){
String message = "There is no available data for station '" + stationID + "' and the specified date range";
String[] errorMessage = {message};
writeError(errorMessage);
}
//Remove sort and remove duplicate days to aid in the graphing
String[][] sortedData = durationCurve.removeDuplicateDates(sortableData);
//Perform analysis method on data
sortedData = computeFlowMethod(sortedData, timeStep, method);
//Calculate stats of data
CalculateStatistics(sortedData);
//Graph the timeseries data
createTimeseriesGraph(sortedData, color, showLine, yAxisTitle);
createTimeseriesBoxplot(sortedData, yAxisTitle);
//Create dynamic summary paragraph
this.start = sortedData[0][0];
this.end = sortedData[sortedData.length - 1][0];
this.len = String.valueOf(sortedData.length);
this.units = graphUnits;
String[] dynamicParagraph = durationCurve.dynamicParagraph("Time Series Graph Overview: ", organizationName);
//Write dynamic Paragraph to text file
writeSummary(dynamicParagraph, mainFolder);
}
}
public static void main(String[] args) throws IOException, InterruptedException, Exception {
guiTimeseries_Model timeseries_Model = new guiTimeseries_Model();
//Run model
timeseries_Model.run();
}
}