guiDC_Model.java [src/java/m/cfa/durationcurve] Revision: Date:
package m.cfa.durationcurve;
import WaterData.WaterData;
import WaterData.WaterDataException;
import WaterData.WaterDataInterface;
import WaterData.WaterQualityInfo;
import m.cfa.DoubleArray;
import m.cfa.DoubleMath;
import m.cfa.FlowStatistics;
import m.cfa.Graphing;
import java.awt.Color;
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.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import org.codehaus.jettison.json.JSONException;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.LogarithmicAxis;
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.chart.title.LegendTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* Last Updated: 28-August-2020
* @author Tyler Wible
* @since 12-June-2011
*/
public class guiDC_Model {
//Inputs
String directory = "C:/Projects/TylerWible_repos/NetBeans/data/CFA/DurationCurve";
String database = "UserData";//"CDWR";//"STORET";//"CDSN";//"UserData";//
String orgId = "n/a";//"n/a";//"21COL001";//"CITYFTCO_WQX";//"n/a";//
String stationId = "09505800";//"CLAGRECO";//"000028";//"1EFF";//"n/a";//
String stationName = "WestClearCreek_";//"South Platte River at Roscoe, Nebr.";//"Cache La Poudre Near Greeley";//"BIG THOMPSON R NEAR MOUTH";//"n/a";//"n/a";//
String wqTest = "flow";//"00600 Total nitrogen, water, unfiltered, milligrams per liter -- mg/L";//"00625 Ammonia-nitrogen as N -- mg/L";//
double wqTarget = 10;//in units of current test
String startDate = "";
String endDate = "";
String seasonBegin = "04-01";//"MM-dd"
String seasonEnd = "04-30";//"MM-dd"
boolean seasonalOnly = false;//true;//
String period1Begin = "";
String period1End = "";
String period2Begin = "";
String period2End = "";
String period3Begin = "";
String period3End = "";
double highPercentile = 0.75;
double lowPercentile = 0.25;
boolean showMonthlyStatsTF = false;
boolean calcFlowStatisticsFileTF = false;
String mQnPeriod = "false";//"7Q10";//
boolean mergeDatasets = false;//true;//
String mergeMethod = "user";//"public";//"max";//"average";//"min";//
String userData = "";//"Date\tFlow\n1999-04-29\t8.3\n1999-05-09\t60.2\n1999-05-29\t20.1$$Date\t00600\n1999-04-29\t8.3\n1999-05-09\t60.2\n1999-05-29\t20.1";//"Date\tFlow\n1999-04-29\t8.3\n1999-05-09\t60.2\n1999-05-29\t20.1";//
// String userData = "Date\tFlow\n"+
// "2020-09-23\t6.15204415970915\n"+
// "";
//Outputs
String flowLen = "-1";
double flowMean = -1;
double flowMedian = -1;
String flowLen_period1 = "-1";
String flowLen_period2 = "-1";
String flowLen_period3 = "-1";
String wqLen = "-1";
String wqUnits = "?";
String start = "?";
String end = "?";
String summaryParagraph = "?";
String summaryTable = "?";
String dataSource = "?";
String lowFlowErrorMessage = "";
double mQnVal = -1;
//Gets
public File getDurationCurve_results(){ return new File(directory, "duration_curve_results.txt"); }
public File getFlowStatistics_summary(){
FlowStatistics flowStats = new FlowStatistics();
return new File(directory, flowStats.getFlowStatistics_summary());
}
public String getGraph() { return "duration_curve_graph.jpg"; }
public File getDCgraphOutput(){ return new File(directory, "duration_curve_graph.out"); }//for use with JSHighCharts
public String getFlowLen() { return flowLen; }
public String getMedian(){ return String.valueOf(flowMedian); }
public String getMean(){ return String.valueOf(flowMean); }
public String getFlowLen_period1() { return flowLen_period1; }
public String getFlowLen_period2() { return flowLen_period2; }
public String getFlowLen_period3() { return flowLen_period3; }
public String getWQLen() { return wqLen; }
public String getWQUnits() { return wqUnits; }
public String getStart() { return start; }
public String getEnd() { return end; }
public String getSummaryParagraph(){ return summaryParagraph; }
public String getSummaryTable(){ return summaryTable; }
public String getDataSource(){ return dataSource; }
public boolean getCalcFlowStatisticsFileTF(){ return calcFlowStatisticsFileTF; }
public String getLowFlowErrorMessage(){ return lowFlowErrorMessage; }
public double getMQNval(){ return mQnVal; }
//Sets
public void setDirectory(String directory_str){ directory = directory_str; }
public void setDatabase(String database_str){ database = database_str; }
public void setOrganizationID(String orgId_str){ orgId = orgId_str; }
public void setStationId(String stationId_str){ stationId = stationId_str; }
public void setStationName(String stationName_str){ stationName = stationName_str; }
public void setWaterQualityTest(String wqTest_str){ wqTest = wqTest_str; }
public void setWaterQualityTarget(double wqTarget_dbl){ wqTarget = wqTarget_dbl; }
public void setStartDate(String startDate_str){ startDate = startDate_str; }
public void setEndDate(String endDate_str){ endDate = endDate_str; }
public void setSeasonBegin(String seasonBegin_str){ seasonBegin = seasonBegin_str; }
public void setSeasonEnd(String seasonEnd_str){ seasonEnd = seasonEnd_str; }
public void setSeasonalAnalysisOnlyTF(boolean seasonalOnly_TF){ seasonalOnly = seasonalOnly_TF; }
public void setPeriod1Begin(String period1Begin_str){ period1Begin = period1Begin_str; }
public void setPeriod1End(String period1End_str){ period1End = period1End_str; }
public void setPeriod2Begin(String period2Begin_str){ period2Begin = period2Begin_str; }
public void setPeriod2End(String period2End_str){ period2End = period2End_str; }
public void setPeriod3Begin(String period3Begin_str){ period3Begin = period3Begin_str; }
public void setPeriod3End(String period3End_str){ period3End = period3End_str; }
public void setHighPercentile(double highPercentile_dbl){ highPercentile = highPercentile_dbl; }
public void setLowPercentile(double lowPercentile_dbl){ lowPercentile = lowPercentile_dbl; }
public void setShowMonthlyStatsTF(boolean showMonthlyStats_TF){ showMonthlyStatsTF = showMonthlyStats_TF; }
public void setCalcFlowStatisticsFileTF(boolean calcFlowStatisticsFile_TF){ calcFlowStatisticsFileTF = calcFlowStatisticsFile_TF; }
public void setMQNperiod(String mQnPeriod_str){ mQnPeriod = mQnPeriod_str; }
public void setMergeDatasets(boolean mergeDatasets_TF){ mergeDatasets = mergeDatasets_TF; }
public void setMergeMethod(String mergeMethod_str){ mergeMethod = mergeMethod_str; }
public void setUserData(String userData_str){ userData = userData_str; }
/**
* Main Flow Duration Curve (FDC) function
* Creates a graph and dynamic summary for the graph, returns an image and text file in
* location "directory"/"fileName"graph.jpg and "directory"/"fileName"paragraph.txt
* @throws IOException
* @throws InterruptedException
* @throws ParseException
* @throws Exception
*/
private void createFDC() throws IOException, ParseException, Exception{
//Check if user wants mQnFlow calculated and displayed
boolean mQnHide = mQnPeriod.contains("false");
//Check if any flow data exists
WaterDataInterface waterLib = WaterData.getNewWaterDataInterface(database, userData);
String[][] sortableData = waterLib.extractFlowData_formatted(directory, orgId, stationId, startDate, endDate);
dataSource = waterLib.getDataSourceCitation();
//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){
WaterDataInterface waterLibUser = WaterData.getNewWaterDataInterface("UserData", userData);
sortableData_user = waterLibUser.extractFlowData_formatted(directory, orgId, stationId, startDate, endDate);
}
//Sort the Data by date to remove duplicate date entries
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<>();
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);
}
//Get period data for analyses
String[][] period1Data = DoubleArray.getPeriodData(sortedData_combined, period1Begin, period1End);
String[][] period2Data = DoubleArray.getPeriodData(sortedData_combined, period2Begin, period2End);
String[][] period3Data = DoubleArray.getPeriodData(sortedData_combined, period3Begin, period3End);
//Check if the user only wants to perform a seasonal analysis (aka a seasonal flow duration curve)
if(seasonalOnly){
sortedData_combined = DoubleArray.getSeasonalData(sortedData_combined, seasonBegin, seasonEnd);
period1Data = DoubleArray.getSeasonalData(period1Data, seasonBegin, seasonEnd);
period2Data = DoubleArray.getSeasonalData(period2Data, seasonBegin, seasonEnd);
period3Data = DoubleArray.getSeasonalData(period3Data, seasonBegin, seasonEnd);
}
//While the dataset is still sorted by date, pull out the mQn value and start/end dates for analysis summary
double mQn = 0;
int m=0, n=0;
if(mQnHide == false){
//Pull out mQn-period variables
String mString = mQnPeriod.substring(0,mQnPeriod.indexOf("Q"));
String nString = mQnPeriod.substring(mQnPeriod.indexOf("Q")+1);
m = Integer.parseInt(mString);
n = Integer.parseInt(nString);
calcFlowStatisticsFileTF = true;
}
//Calculate the flow stats file
FlowStatistics flowStats = new FlowStatistics();
if(calcFlowStatisticsFileTF){
Object[] returnValue = flowStats.calculateAllStatisticsSummaries(directory, stationId, stationName, sortedData_combined, highPercentile, lowPercentile,
m, n, showMonthlyStatsTF, seasonBegin, seasonEnd, period1Begin, period1End, period2Begin, period2End, period3Begin, period3End, false);
//Calculate low flow
mQn = (Double) returnValue[0];
String MQNmessage = (String) returnValue[1];
if(!mQnHide){
lowFlowErrorMessage = MQNmessage;
mQnVal = DoubleMath.round(mQn, 2);
}
}
start = sortedData_combined[0][0];
end = sortedData_combined[sortedData_combined.length - 1][0];
flowLen = String.valueOf(sortedData_combined.length);
flowLen_period1 = String.valueOf(period1Data.length);
flowLen_period2 = String.valueOf(period2Data.length);
flowLen_period3 = String.valueOf(period3Data.length);
CalculateStatistics(sortedData_combined);
//Create Custom Flow Duration Curve graph
graphFDC(sortedData_combined, sortedData_user, period1Data, period2Data, period3Data, mQn, m, n, mQnHide);
//Attach analysis summary to return object
summaryParagraph = dynamicParagraph("Flow Duration Curve Overview: ");
}
/**
* Main graphing function for FDC analysis, plots the provided xyRanks as x,y points on a graph
* with or without low flow analysis (mQn) added depending if it is non-zero or not
* @param sortedData_combined String[][] containing 2 columns, first column dates (yyyy-mm-dd) second column y values of flow of all the data
* @param sortedData_user String[][] containing 2 columns, first column dates (yyyy-mm-dd) second column y values of flow of the user data only
* @param period1Data the String[][] containing sorted data for the time
* series contained by period1 (column 1 = dates yyyy-mm-dd, column 2 = value)
* @param period2Data the String[][] containing sorted data for the time
* series contained by period2 (column 1 = dates yyyy-mm-dd, column 2 = value)
* @param period3Data the String[][] containing sorted data for the time
* series contained by period3 (column 1 = dates yyyy-mm-dd, column 2 = value)
* @param mQn the low flow analysis value, if it is not zero it will be graphed, otherwise it will not be graphed
* @param m the "m" value of an mQn flow analysis (like 7Q10)
* @param n the "n" value of an mQn flow analysis (like 7Q10)
* @param mQnHide if true then mQn wil be not graphed, otherwise it will be shown in the legend
* @throws IOException
*/
private void graphFDC(String[][] sortedData_combined,
String[][] sortedData_user,
String[][] period1Data,
String[][] period2Data,
String[][] period3Data,
double mQn,
double m,
double n,
boolean mQnHide) throws IOException{
//Perform Weibull Plotting Position Ranking for the Flow Duration Curve Method
double[][] xyRanks = DoubleArray.weibullPlottingPosition(sortedData_combined);
double[][] xyRanks_period1 = DoubleArray.weibullPlottingPosition(period1Data);
double[][] xyRanks_period2 = DoubleArray.weibullPlottingPosition(period2Data);
double[][] xyRanks_period3 = DoubleArray.weibullPlottingPosition(period3Data);
//Determine summary table from xyRanks for duration curve
calculatePercentileSummmary(xyRanks, xyRanks_period1, xyRanks_period2, xyRanks_period3, "Flow (cfs)");
//Graph the complete flow duration curve for the time period
XYSeries FDC_xy = new XYSeries("FDC");
XYSeries mQn_xy = new XYSeries(String.valueOf(m) + "Q" + String.valueOf(n) + " low flow");
XYSeries FDC_user = new XYSeries("User Data");
XYSeries FDC_xy_period1 = new XYSeries("Period 1 FDC: " + period1Begin + " to " + period1End);
XYSeries FDC_xy_period2 = new XYSeries("Period 2 FDC: " + period2Begin + " to " + period2End);
XYSeries FDC_xy_period3 = new XYSeries("Period 3 FDC: " + period3Begin + " to " + period3End);
String[][] graphData = new String[xyRanks.length][8];
int ctr = 0, seriesIndex = 0;
for(int i=0; i<xyRanks.length; i++){
FDC_xy.add(xyRanks[i][0], xyRanks[i][1]);//x,y
graphData[i][0] = String.valueOf(xyRanks[i][0]);
graphData[i][1] = String.valueOf(xyRanks[i][1]);
if(i != 0){//get low flow intersection point for mQn flow
if(xyRanks[i][1] < mQn && xyRanks[i-1][1] >= mQn){
mQn_xy.add(xyRanks[i][0], mQn);
}
}
//Create an XYSeries only if userdata is not empty
if(sortedData_user.length > 0 && sortedData_user.length > ctr){
double userValue = Double.parseDouble(sortedData_user[ctr][1]);
if(Double.compare(xyRanks[i][1], userValue) == 0){
FDC_user.add(xyRanks[i][0], xyRanks[i][1]);
ctr++;
}
}
//Create period 1 line
if(i < xyRanks_period1.length){
FDC_xy_period1.add(xyRanks_period1[i][0], xyRanks_period1[i][1]);//x,y
graphData[i][2] = String.valueOf(xyRanks_period1[i][0]);
graphData[i][3] = String.valueOf(xyRanks_period1[i][1]);
}else{
graphData[i][2] = "-1";
graphData[i][3] = "-1";
}
//Create period 2 line
if(i < xyRanks_period2.length){
FDC_xy_period2.add(xyRanks_period2[i][0], xyRanks_period2[i][1]);//x,y
graphData[i][4] = String.valueOf(xyRanks_period2[i][0]);
graphData[i][5] = String.valueOf(xyRanks_period2[i][1]);
}else{
graphData[i][4] = "-1";
graphData[i][5] = "-1";
}
//Create period 3 line
if(i < xyRanks_period3.length){
FDC_xy_period3.add(xyRanks_period3[i][0], xyRanks_period3[i][1]);//x,y
graphData[i][6] = String.valueOf(xyRanks_period3[i][0]);
graphData[i][7] = String.valueOf(xyRanks_period3[i][1]);
}else{
graphData[i][6] = "-1";
graphData[i][7] = "-1";
}
}
//Graph resulting FDC and flow value data
XYPlot plot = new XYPlot();
boolean showLegend = false;
//Create X Axis
ValueAxis xAxis = new NumberAxis("Percent of Time Flow is Exceeded [%]");
xAxis.setRange(0,100);
plot.setDomainAxis(0, xAxis);
//Set log-scale y axis
LogarithmicAxis yAxis = new LogarithmicAxis("Discharge [cfs]");
yAxis.setAllowNegativesFlag(true);
plot.setRangeAxis(0, yAxis);
//Create a graph with the FDC (line)
XYDataset FDC_data = new XYSeriesCollection(FDC_xy);
XYItemRenderer renderer1 = new XYLineAndShapeRenderer(true, false);
renderer1.setSeriesPaint(0, Color.black);
//Set the FDC line data, renderer, and axis into plot
plot.setDataset(seriesIndex, FDC_data);
plot.setRenderer(seriesIndex, renderer1);
//Map the line to the first Domain and first Range
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
//Graph period 1 data
if(period1Data.length > 0){
XYDataset FDC_period = new XYSeriesCollection(FDC_xy_period1);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, Color.red);
plot.setDataset(seriesIndex, FDC_period);
plot.setRenderer(seriesIndex, renderer);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
showLegend = true;
}
//Graph period 2 data
if(period2Data.length > 0){
XYDataset FDC_period = new XYSeriesCollection(FDC_xy_period2);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, new Color(255, 135, 0));//gold
plot.setDataset(seriesIndex, FDC_period);
plot.setRenderer(seriesIndex, renderer);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
showLegend = true;
}
//Graph period 3 data
if(period3Data.length > 0){
XYDataset FDC_period = new XYSeriesCollection(FDC_xy_period3);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, Color.green);
plot.setDataset(seriesIndex, FDC_period);
plot.setRenderer(seriesIndex, renderer);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
showLegend = true;
}
//Create low mQn point
if(!mQnHide && Double.compare(mQn, 0) != 0){//only show mQn if it is not zero
XYDataset mQn_data = new XYSeriesCollection(mQn_xy);
XYItemRenderer renderer = new XYLineAndShapeRenderer(false, true);
renderer.setSeriesShape(0, new Ellipse2D.Double(-3.0, 3.0, 6.0, 6.0));
renderer.setSeriesPaint(0, Color.red);
plot.setDataset(seriesIndex,mQn_data);
plot.setRenderer(seriesIndex,renderer);
plot.mapDatasetToDomainAxis(seriesIndex,0);
plot.mapDatasetToRangeAxis(seriesIndex,0);
seriesIndex++;
showLegend = true;
}
//Create user data points
if(sortedData_user.length != 0){//only show user points if it is not zero
XYDataset user_data = new XYSeriesCollection(FDC_user);
XYItemRenderer renderer = new XYLineAndShapeRenderer(false, true);
renderer.setSeriesPaint(0, Color.black);
plot.setDataset(seriesIndex,user_data);
plot.setRenderer(seriesIndex,renderer);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
showLegend = true;
}
//Graph a FDC for each year in time period
String currentYear = start.substring(0,4);
String finalYear = end.substring(0,4);
boolean moreYears = xyRanks.length > 0;
while(moreYears){
//Get current years data and graph it
String[][] partialData = DoubleArray.getYearsData(sortedData_combined, currentYear);
double[][] partialRanks = DoubleArray.weibullPlottingPosition(partialData);
Graphing.addXYSeries(plot, partialRanks, Color.lightGray, seriesIndex);
seriesIndex++;
//Save results for output for JHighCharts
String[] partialFDCData_x = new String[xyRanks.length];
String[] partialFDCData_y = new String[xyRanks.length];
for(int i=0; i<xyRanks.length; i++){
partialFDCData_x[i] = "-1";//value
partialFDCData_y[i] = "-1";//value
try{
partialFDCData_x[i] = String.valueOf(partialRanks[i][0]);//x
partialFDCData_y[i] = String.valueOf(partialRanks[i][1]);//y
}catch(IndexOutOfBoundsException e){
//do nothing as it already has a -1 value
}
}
graphData = DoubleArray.appendcolumn_Matrix(graphData, partialFDCData_x);//Add x points
graphData = DoubleArray.appendcolumn_Matrix(graphData, partialFDCData_y);//Add y points
//Determine the next data year to continue looping over
int nextYear = Integer.parseInt(currentYear) + 1;
if(finalYear.compareToIgnoreCase(String.valueOf(nextYear)) >= 0){
currentYear = String.valueOf(nextYear);
}else{
moreYears = false;
}
}
//Output XY data for use with JHighCharts
DoubleArray.writeXYseries(directory, graphData, getDCgraphOutput().getName());
//Set extra graphing preferences
plot = Graphing.setLogYaxisPreferences(plot);
//Add Flow Range Labels:
plot = Graphing.addIntervalLabel(plot, "High Flow", 0, 10);
plot = Graphing.addIntervalLabel(plot, "Moist Conditions", 10, 40);
plot = Graphing.addIntervalLabel(plot, "Mid-Range Flows", 40, 60);
plot = Graphing.addIntervalLabel(plot, "Dry Conditions", 60, 90);
plot = Graphing.addIntervalLabel(plot, "Low Flow", 90, 100);
//Graph plot onto JfreeChart
String graphTitle = "Flow Duration Curve for " + database + " Station " + stationId + "; " + stationName;
JFreeChart chart = new JFreeChart(graphTitle, Graphing.titleFont, plot, showLegend);
//Write a results file containing the flow duration curve interval (exceedance values) and their corresponding discharge values
writeResults(graphData, "Discharge [cfs]", false);
//Save resulting graph for proof it works
try{
String path = directory + File.separator + getGraph();
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("JFreeChart created properly at: " + path);
}catch(IOException e){
System.out.println("A problem occurred while trying to creating the chart for station " + stationName + ". Error: USGSFDC0003");
System.out.println(e.toString());
}
}
/**
* Main Load Duration Curve (LDC) function
* Creates a graph and dynamic summary for the graph, returns an image and text file in
* location "directory"/"fileName"graph.jpg and "directory"/"fileName"paragraph.txt
* @throws IOException
* @throws InterruptedException
* @throws ParseException
* @throws JSONException
*/
private void createLDC() throws IOException, InterruptedException, ParseException, JSONException, WaterDataException {
//Check if any flow and water quality data exists
//Check if any flow and water quality data exists
String flowData_raw = "", wqData_raw = "";
if(userData.length() > 0){
String[] userDataList = userData.split("\\$\\$");
flowData_raw = userDataList[0];
wqData_raw = userDataList[1];
}
WaterDataInterface waterLib = WaterData.getNewWaterDataInterface(database, flowData_raw);
String[][] sortableData = waterLib.extractFlowData_formatted(directory, orgId, stationId, startDate, endDate);
WaterDataInterface waterLib2 = WaterData.getNewWaterDataInterface(database, wqData_raw);
String[][] WQdata = waterLib2.extractWaterQualityData_formatted(directory, orgId, stationId, startDate, endDate, wqTest);
dataSource = waterLib.getDataSourceCitation();
//Find the user uploaded data file and uses this for a timeseries graph
String[][] sortableData_user = new String[0][0];
String[][] WQdata_user = new String[0][0];
if(mergeDatasets){
WaterDataInterface waterLibUser = WaterData.getNewWaterDataInterface("UserData", flowData_raw);
sortableData_user = waterLibUser.extractFlowData_formatted(directory, orgId, stationId, startDate, endDate);
WaterDataInterface waterLibUser2 = WaterData.getNewWaterDataInterface("UserData", wqData_raw);
WQdata_user = waterLibUser2.extractWaterQualityData_formatted(directory, orgId, stationId, startDate, endDate, wqTest);
}
//Sort the Data by date to remove duplicate date entries
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);
String[][] WQdata_combined = DoubleArray.mergeData(WQdata, WQdata_user, mergeMethod);
//Check if any data exists
ArrayList<String> errorMessage = new ArrayList<>();
if(sortedData_combined.length == 0){
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 uploaded flow data for station " + stationId + " and the specified date range.");
}
}
if(WQdata_combined.length == 0){
if(WQdata.length == 0){
errorMessage.add("There are no available " + wqTest + " water quality tests in the " + database + " database for station " + stationId + " and the specified date range.");
}
if(WQdata_user.length==0){
errorMessage.add("There are no uploaded " + wqTest + " water quality tests for station " + stationId+ " and the specified date range");
}
}
if(errorMessage.size() > 0){
writeError(errorMessage);
}
//Get Units and conversion for current WQ test
String[] resultArray = WaterQualityInfo.getWqTestDataInfo(wqTest, database);
String wqCode = resultArray[0];
String wqLabel = resultArray[1];
//String units = resultArray[2];
double ldcConversion = Double.parseDouble(resultArray[3]);
String ldcEndUnits = resultArray[4];
//Get period data for analyses
String[][] period1Data = DoubleArray.getPeriodData(sortedData_combined, period1Begin, period1End);
String[][] period2Data = DoubleArray.getPeriodData(sortedData_combined, period2Begin, period2End);
String[][] period3Data = DoubleArray.getPeriodData(sortedData_combined, period3Begin, period3End);
//Check if the user only wants to perform a seasonal analysis (aka a seasonal flow duration curve)
if(seasonalOnly){
sortedData_combined = DoubleArray.getSeasonalData(sortedData_combined, seasonBegin, seasonEnd);
WQdata_combined = DoubleArray.getSeasonalData(WQdata_combined, seasonBegin, seasonEnd);
period1Data = DoubleArray.getSeasonalData(period1Data, seasonBegin, seasonEnd);
period2Data = DoubleArray.getSeasonalData(period2Data, seasonBegin, seasonEnd);
period3Data = DoubleArray.getSeasonalData(period3Data, seasonBegin, seasonEnd);
}
//While the dataset is still sorted by date, pull out the mQn value and start/end dates for analysis summary
boolean mQnHide = mQnPeriod.contains("false");
double mQn = 0;
int m=0, n=0;
if(mQnHide == false){
//Pull out mQn-period variables
String mString = mQnPeriod.substring(0,mQnPeriod.indexOf("Q"));
String nString = mQnPeriod.substring(mQnPeriod.indexOf("Q")+1);
m = Integer.parseInt(mString);
n = Integer.parseInt(nString);
calcFlowStatisticsFileTF = true;
}
//Calculate the flow stats file
FlowStatistics flowStats = new FlowStatistics();
if(calcFlowStatisticsFileTF){
Object[] returnValue = flowStats.calculateAllStatisticsSummaries(directory, stationId, stationName, sortedData_combined, highPercentile, lowPercentile, m, n,
showMonthlyStatsTF, seasonBegin, seasonEnd, period1Begin, period1End, period2Begin, period2End, period3Begin, period3End, false);
//Calculate low flow
mQn = (Double) returnValue[0];
String MQNmessage = (String) returnValue[1];
//Also multiply the mQn flow value(cfs) by the conversion and water quality target
mQn = mQn*ldcConversion*wqTarget;
if(!mQnHide){
if(!MQNmessage.equalsIgnoreCase("")){//if the MQNmessage is not blank add it to the dynamic summary
lowFlowErrorMessage = MQNmessage;
}else{
mQnVal = DoubleMath.round(mQn, 2);
}
}
}
start = sortedData_combined[0][0];
end = sortedData_combined[sortedData_combined.length - 1][0];
flowLen = String.valueOf(sortedData_combined.length);
flowLen_period1 = String.valueOf(period1Data.length);
flowLen_period2 = String.valueOf(period2Data.length);
flowLen_period3 = String.valueOf(period3Data.length);
CalculateStatistics(sortedData_combined);
String[][] seasonalWQ_combined = DoubleArray.getSeasonalData(WQdata_combined, seasonBegin, seasonEnd);
String[][] seasonalWQ_user = DoubleArray.getSeasonalData(WQdata_user, seasonBegin, seasonEnd);
//Graph resulting LDC data
Object[] returnArray = graphLDC(wqCode, wqLabel, ldcEndUnits, ldcConversion, sortedData_combined, sortedData_user, period1Data, period2Data, period3Data, WQdata_combined, WQdata_user, seasonalWQ_combined, seasonalWQ_user, mQn, m, n);
String paragraphTitle = (String) returnArray[0];
double totalCount = (Double) returnArray[1];
//Attach analysis summary to begin of dynamic paragraph
wqLen = String.valueOf(totalCount);
wqUnits = ldcEndUnits;
summaryParagraph = dynamicParagraph(paragraphTitle);
}
/**
* Sub-graphing function to create combined-range graph
* @param WQlabel the title of the water quality test being graphed
* @param endUnits the final load units of the water quality test * cfs * conversion = load/day
* @param conversion
* @param sortedData_combined the load duration curve XY line data of combined data
* @param sortedData_user the load duration curve XY line data of user data only
* @param period1Data the String[][] containing sorted data for the time
* series contained by period1 (column 1 = dates yyyy-mm-dd, column 2 = value)
* @param period2Data the String[][] containing sorted data for the time
* series contained by period2 (column 1 = dates yyyy-mm-dd, column 2 = value)
* @param period3Data the String[][] containing sorted data for the time
* series contained by period3 (column 1 = dates yyyy-mm-dd, column 2 = value)
* @param WQdata_combined the observed water quality points XY scatter of combined data
* @param WQdata_user the user data water quality points XY scatter of user data only
* @param seasonalWQ_combined the observed water quality points within the "season" XY scatter of combined data
* @param seasonalWQ_user the user data water quality points within the "season" XY scatter of user data only
* @param mQn the value of the mQn flow (if it is zero then dont show this line in the graph)
* @param m the value of m from mQn flow calculations
* @param n the value of n from mqn flow calculations
* @return the name/title of the dynamic paragraph to be created to accompany the graph created during this function
* @throws IOException
*/
private Object[] graphLDC(String wqCode,
String WQlabel,
String endUnits,
double conversion,
String[][] sortedData_combined,
String[][] sortedData_user,
String[][] period1Data,
String[][] period2Data,
String[][] period3Data,
String[][] WQdata_combined,
String[][] WQdata_user,
String[][] seasonalWQ_combined,
String[][] seasonalWQ_user,
double mQn,
double m,
double n) throws IOException {
//Perform Weibull Plotting Position Ranking for the Load Duration Curve Method
//Note: this data is already converted to loads, see "guiDC_Model.createLDC"
double[][] xyRanks = DoubleArray.weibullPlottingPosition(sortedData_combined);
double[][] xyRanks_period1 = DoubleArray.weibullPlottingPosition(period1Data);
double[][] xyRanks_period2 = DoubleArray.weibullPlottingPosition(period2Data);
double[][] xyRanks_period3 = DoubleArray.weibullPlottingPosition(period3Data);
//Determine summary table from xyRanks for duration curve
double[][] loadRanks = new double[xyRanks.length][2];
double[][] loadRanks_period1 = new double[xyRanks_period1.length][2];
double[][] loadRanks_period2 = new double[xyRanks_period2.length][2];
double[][] loadRanks_period3 = new double[xyRanks_period3.length][2];
for(int i=0; i<xyRanks.length; i++){
loadRanks[i][0] = xyRanks[i][0];
loadRanks[i][1] = xyRanks[i][1] * conversion * wqTarget;
if(i < xyRanks_period1.length){
loadRanks_period1[i][0] = xyRanks_period1[i][0];
loadRanks_period1[i][1] = xyRanks_period1[i][1] * conversion * wqTarget;
}
if(i < xyRanks_period2.length){
loadRanks_period2[i][0] = xyRanks_period2[i][0];
loadRanks_period2[i][1] = xyRanks_period2[i][1] * conversion * wqTarget;
}
if(i < xyRanks_period3.length){
loadRanks_period3[i][0] = xyRanks_period3[i][0];
loadRanks_period3[i][1] = xyRanks_period3[i][1] * conversion * wqTarget;
}
}
calculatePercentileSummmary(loadRanks, loadRanks_period1, loadRanks_period2, loadRanks_period3, "Load (" + endUnits +")");
//Break scattered WQ points data into the 5 flow sub classifications 0-10, 10-40, 40-60, 60-90, 90-100
//which are High flows, Moist conditions, Mid-range flows, Dry conditions, and low flows respectively Assimilative
String yaxis_title = WQlabel + " [" + endUnits + "]";
XYSeries LDC_xy = new XYSeries("LDC");
XYSeries LDC_user = new XYSeries("LDC User Data");
XYSeries LDC_xy_period1 = new XYSeries("Period 1 LDC: " + period1Begin + " to " + period1End);
XYSeries LDC_xy_period2 = new XYSeries("Period 2 LDC: " + period1Begin + " to " + period1End);
XYSeries LDC_xy_period3 = new XYSeries("Period 3 LDC: " + period1Begin + " to " + period1End);
XYSeries WQ_pts = new XYSeries("Water Quality Obs.");
XYSeries WQ_pts_user = new XYSeries("Water Quality Obs. User Data");
XYSeries Seasonal_WQ_pts = new XYSeries("Seasonal Water Quality Obs.");
XYSeries Seasonal_WQ_pts_user = new XYSeries("Seasonal Water Quality Obs. User Data");
XYSeries mQn_xy = new XYSeries("Current " + String.valueOf(m) + "Q" + String.valueOf(n) + " water quality standard");
String[][] graphData = new String[xyRanks.length][8];
int ctr = 0;
for (int i=0; i<xyRanks.length; i++){
double y = xyRanks[i][1] * conversion * wqTarget;
double x = xyRanks[i][0];
LDC_xy.add(x, y);
graphData[i][0] = String.valueOf(x);
graphData[i][1] = String.valueOf(y);
if(y > mQn){
mQn_xy.add(x,mQn);
}else{
mQn_xy.add(x, y);
}
//Create an XYSeries only if userdata is not empty
if(sortedData_user.length > 0 && sortedData_user.length > ctr){
double userValue = Double.parseDouble(sortedData_user[ctr][1]);
if(Double.compare(xyRanks[i][1], userValue) == 0){
LDC_user.add(x, y);
ctr++;
}
}
//Create period 1 line
if(i < xyRanks_period1.length){
LDC_xy_period1.add(xyRanks_period1[i][0], xyRanks_period1[i][1] * conversion * wqTarget);//x,y
graphData[i][2] = String.valueOf(xyRanks_period1[i][0]);
graphData[i][3] = String.valueOf(xyRanks_period1[i][1] * conversion * wqTarget);
}else{
graphData[i][2] = "-1";
graphData[i][3] = "-1";
}
//Create period 2 line
if(i < xyRanks_period2.length){
LDC_xy_period2.add(xyRanks_period2[i][0], xyRanks_period2[i][1] * conversion * wqTarget);//x,y
graphData[i][4] = String.valueOf(xyRanks_period2[i][0]);
graphData[i][5] = String.valueOf(xyRanks_period2[i][1] * conversion * wqTarget);
}else{
graphData[i][4] = "-1";
graphData[i][5] = "-1";
}
//Create period 3 line
if(i < xyRanks_period3.length){
LDC_xy_period3.add(xyRanks_period3[i][0], xyRanks_period3[i][1] * conversion * wqTarget);//x,y
graphData[i][6] = String.valueOf(xyRanks_period3[i][0]);
graphData[i][7] = String.valueOf(xyRanks_period3[i][1] * conversion * wqTarget);
}else{
graphData[i][6] = "-1";
graphData[i][7] = "-1";
}
}
//Add WQ test data to graph series and generate box plot data
ArrayList<Double> high_data = new ArrayList<>();
ArrayList<Double> moist_data = new ArrayList<>();
ArrayList<Double> mid_data = new ArrayList<>();
ArrayList<Double> dry_data = new ArrayList<>();
ArrayList<Double> low_data = new ArrayList<>();
double highexceed_count = 0, moistexceed_count =0, midexceed_count =0, dryexceed_count =0,
lowexceed_count =0, totalexceed_count =1, totalCount = 0;
double WQ_load = 0, Seasonal_WQ_load=0;
ctr = 0;
for(int i=0;i<WQdata_combined.length;i++){
for(int j=0; j<sortedData_combined.length;j++){
if(WQdata_combined[i][0].equalsIgnoreCase(sortedData_combined[j][0])){
totalCount = totalCount+1;
//Create an XYSeries for WQ points
double tempFlowValue = Double.parseDouble(sortedData_combined[j][1]);
double tempWQValue = Double.parseDouble(WQdata_combined[i][1]);
WQ_load = tempFlowValue * conversion * tempWQValue;
//Find what exceedance rank matches with which flow rate
double xyRankValue = xyRanks[j][0];
double xyLoadValue = loadRanks[j][1]; //Same formate/order as xyRanks but flow was converted to a load value
for(int k=0; k<xyRanks.length; k++){
if(xyRanks[k][1] == Double.parseDouble(sortedData_combined[j][1])){
xyRankValue = xyRanks[k][0];
xyLoadValue = loadRanks[k][1];
break;
}
}
WQ_pts.add(xyRankValue,WQ_load);
//Create an XYSeries only if user WQ data is not empty
if(WQdata_user.length > 0 && WQdata_user.length > ctr){
if(WQdata_user[ctr][0].equalsIgnoreCase(sortedData_combined[j][0])){
WQ_pts_user.add(xyRankValue,WQ_load);
ctr++;
}
}
//Also add to category box plot data
if((xyRankValue <= 10) & (xyRankValue >= 0)){
high_data.add(WQ_load);
if(WQ_load > xyLoadValue){
highexceed_count++;
}
}
if((xyRankValue <= 40) & (xyRankValue > 10)){
moist_data.add(WQ_load);
if(WQ_load > xyLoadValue){
moistexceed_count++;
}
}
if((xyRankValue <= 60) & (xyRankValue > 40)){
mid_data.add(WQ_load);
if(WQ_load > xyLoadValue){
midexceed_count++;
}
}
if((xyRankValue <= 90) & (xyRankValue > 60)){
dry_data.add(WQ_load);
if(WQ_load > xyLoadValue){
dryexceed_count++;
}
}
if((xyRankValue <= 100) & (xyRankValue > 90)){
low_data.add(WQ_load);
if(WQ_load > xyLoadValue){
lowexceed_count++;
}
}
for(int k=0;k<seasonalWQ_combined.length;k++){
if(seasonalWQ_combined[k][0].equalsIgnoreCase(sortedData_combined[j][0])){
double tempSeasonalWQValue = Double.parseDouble(seasonalWQ_combined[k][1]);
Seasonal_WQ_load = (Double.parseDouble(sortedData_combined[j][1]))*conversion*tempSeasonalWQValue;
Seasonal_WQ_pts.add(xyRankValue,Seasonal_WQ_load);
}
}
for(int k=0;k<seasonalWQ_user.length;k++){
if(seasonalWQ_user[k][0].equalsIgnoreCase(sortedData_combined[j][0])){
double tempSeasonalWQValue = Double.parseDouble(seasonalWQ_user[k][1]);
Seasonal_WQ_load = (Double.parseDouble(sortedData_combined[j][1]))*conversion*tempSeasonalWQValue;
Seasonal_WQ_pts_user.add(xyRankValue,Seasonal_WQ_load);
}
}
break;
}
}
}
//Determine which pollution source is likely based on location of exceeded water quality tests.
totalexceed_count = highexceed_count + moistexceed_count + midexceed_count + dryexceed_count + lowexceed_count;
if (totalexceed_count == 0){
totalexceed_count = 1;
}
//Takes the counts of exceedance points and makes them relative to the total count of exceedances.
highexceed_count = highexceed_count/totalexceed_count;
moistexceed_count = moistexceed_count/totalexceed_count;
midexceed_count = midexceed_count/totalexceed_count;
dryexceed_count = dryexceed_count/totalexceed_count;
lowexceed_count = lowexceed_count/totalexceed_count;
//Pollution Source paragraph is only for nutrient tests so check if the current test is for nutrients:
boolean nutrient = false;
if (wqCode.equalsIgnoreCase("00597") || wqCode.equalsIgnoreCase("00600") || wqCode.equalsIgnoreCase("00601") || wqCode.equalsIgnoreCase("00602") ||
wqCode.equalsIgnoreCase("00604") || wqCode.equalsIgnoreCase("00605") || wqCode.equalsIgnoreCase("00606") || wqCode.equalsIgnoreCase("00607") ||
wqCode.equalsIgnoreCase("00608") || wqCode.equalsIgnoreCase("00610") || wqCode.equalsIgnoreCase("00613") || wqCode.equalsIgnoreCase("00615") ||
wqCode.equalsIgnoreCase("00618") || wqCode.equalsIgnoreCase("00619") || wqCode.equalsIgnoreCase("00620") || wqCode.equalsIgnoreCase("00623") ||
wqCode.equalsIgnoreCase("00624") || wqCode.equalsIgnoreCase("00625") || wqCode.equalsIgnoreCase("00628") || wqCode.equalsIgnoreCase("00630") ||
wqCode.equalsIgnoreCase("00631") || wqCode.equalsIgnoreCase("00635") || wqCode.equalsIgnoreCase("00636") || wqCode.equalsIgnoreCase("00639") ||
wqCode.equalsIgnoreCase("00650") || wqCode.equalsIgnoreCase("00653") || wqCode.equalsIgnoreCase("00660") || wqCode.equalsIgnoreCase("00665") ||
wqCode.equalsIgnoreCase("00666") || wqCode.equalsIgnoreCase("00667") || wqCode.equalsIgnoreCase("00669") || wqCode.equalsIgnoreCase("00670") ||
wqCode.equalsIgnoreCase("00671") || wqCode.equalsIgnoreCase("00672") || wqCode.equalsIgnoreCase("00673") || wqCode.equalsIgnoreCase("00674") ||
wqCode.equalsIgnoreCase("00675") || wqCode.equalsIgnoreCase("00676") || wqCode.equalsIgnoreCase("00677") || wqCode.equalsIgnoreCase("00678") ||
wqCode.equalsIgnoreCase("01425") || wqCode.equalsIgnoreCase("01465") || wqCode.equalsIgnoreCase("49567") || wqCode.equalsIgnoreCase("49570") ||
wqCode.equalsIgnoreCase("62854") || wqCode.equalsIgnoreCase("62855") || wqCode.equalsIgnoreCase("64832") || wqCode.equalsIgnoreCase("70507") ||
wqCode.equalsIgnoreCase("71845") || wqCode.equalsIgnoreCase("71846") || wqCode.equalsIgnoreCase("71850") || wqCode.equalsIgnoreCase("71851") ||
wqCode.equalsIgnoreCase("71855") || wqCode.equalsIgnoreCase("71856") || wqCode.equalsIgnoreCase("71886") || wqCode.equalsIgnoreCase("71887") ||
wqCode.equalsIgnoreCase("71888") || wqCode.equalsIgnoreCase("76008") || wqCode.equalsIgnoreCase("76009") || wqCode.equalsIgnoreCase("76010") ||
wqCode.equalsIgnoreCase("82046") || wqCode.equalsIgnoreCase("83044") || wqCode.equalsIgnoreCase("83047") || wqCode.equalsIgnoreCase("83050") ||
wqCode.equalsIgnoreCase("83053") || wqCode.equalsIgnoreCase("83056") || wqCode.equalsIgnoreCase("83059") || wqCode.equalsIgnoreCase("83062") ||
wqCode.equalsIgnoreCase("83065") || wqCode.equalsIgnoreCase("83068") || wqCode.equalsIgnoreCase("83071") || wqCode.equalsIgnoreCase("83074") ||
wqCode.equalsIgnoreCase("83077") || wqCode.equalsIgnoreCase("83080") || wqCode.equalsIgnoreCase("83083") || wqCode.equalsIgnoreCase("83086") ||
wqCode.equalsIgnoreCase("83089") || wqCode.equalsIgnoreCase("83092") || wqCode.equalsIgnoreCase("83095") || wqCode.equalsIgnoreCase("83098") ||
wqCode.equalsIgnoreCase("83101") || wqCode.equalsIgnoreCase("83108") || wqCode.equalsIgnoreCase("83111") || wqCode.equalsIgnoreCase("83114") ||
wqCode.equalsIgnoreCase("83117") || wqCode.equalsIgnoreCase("83326") || wqCode.equalsIgnoreCase("83329") || wqCode.equalsIgnoreCase("83332") ||
wqCode.equalsIgnoreCase("83335") || wqCode.equalsIgnoreCase("83338") || wqCode.equalsIgnoreCase("83341") || wqCode.equalsIgnoreCase("83344") ||
wqCode.equalsIgnoreCase("83347") || wqCode.equalsIgnoreCase("83350") || wqCode.equalsIgnoreCase("83353") || wqCode.equalsIgnoreCase("83356") ||
wqCode.equalsIgnoreCase("83359") || wqCode.equalsIgnoreCase("83362") || wqCode.equalsIgnoreCase("83365") || wqCode.equalsIgnoreCase("83368") ||
wqCode.equalsIgnoreCase("83371") || wqCode.equalsIgnoreCase("83374") || wqCode.equalsIgnoreCase("83377") || wqCode.equalsIgnoreCase("83380") ||
wqCode.equalsIgnoreCase("83383") || wqCode.equalsIgnoreCase("83390") || wqCode.equalsIgnoreCase("83393") || wqCode.equalsIgnoreCase("83396") ||
wqCode.equalsIgnoreCase("83399") || wqCode.equalsIgnoreCase("90859") || wqCode.equalsIgnoreCase("91003") || wqCode.equalsIgnoreCase("91004") ||
wqCode.equalsIgnoreCase("99116") || wqCode.equalsIgnoreCase("99120") || wqCode.equalsIgnoreCase("99121") || wqCode.equalsIgnoreCase("99122") ||
wqCode.equalsIgnoreCase("99123") || wqCode.equalsIgnoreCase("99124") || wqCode.equalsIgnoreCase("99125") || wqCode.equalsIgnoreCase("99126") ||
wqCode.equalsIgnoreCase("99133") || wqCode.equalsIgnoreCase("99410") || wqCode.equalsIgnoreCase("99411") || wqCode.equalsIgnoreCase("99412") ||
wqCode.equalsIgnoreCase("99413") || wqCode.equalsIgnoreCase("99414") || wqCode.equalsIgnoreCase("99415") || wqCode.equalsIgnoreCase("99416") ||
wqCode.equalsIgnoreCase("99889") || wqCode.equalsIgnoreCase("99891") || wqCode.equalsIgnoreCase("99892") || wqCode.equalsIgnoreCase("99893") ||
wqCode.equalsIgnoreCase("99894")){
nutrient = true;
}
//Decide based on properties of the data which pollution source is likely and display the paragraph
//about that pollution source.
String paragraphTitle = "";
if (nutrient == true && (lowexceed_count > 0 || dryexceed_count > 0) &&
(midexceed_count < 0.1 && moistexceed_count < 0.075 && highexceed_count < 0.05) &&
((totalexceed_count/totalCount) > 0.15 || totalexceed_count > 5)){
paragraphTitle = "Point Sources and Wastewater Sources: ";
//If the pollutant target is exceeded under low flows, it is probably a "point source".
}else if (nutrient == true && (moistexceed_count > 0 && midexceed_count > 0) &&
(dryexceed_count < 0.1 && dryexceed_count > 0 && lowexceed_count < 0.05) &&
((totalexceed_count/totalCount) > 0.15 || totalexceed_count > 5)){
paragraphTitle = "Upper Flow Sources: ";
//If the pollutant target is exceeded primarily beginning around 55% flow interval,
//it is probably an "upper-flow" driven non-point-source pollution.
}else if (nutrient == true && (moistexceed_count > 0 && midexceed_count > 0 && dryexceed_count > 0 && lowexceed_count < 0.12) &&
(moistexceed_count < 0.12 && midexceed_count < 0.12 && dryexceed_count < 0.1) &&
((totalexceed_count/totalCount) > 0.15 || totalexceed_count > 5)){
paragraphTitle = "Wet-Weather Sources: ";
//If the pollutant target is exceeded primarily beginning around 70% flow interval,
//it is probably a "wet-weather" driven non-point-source pollution.
}else if (nutrient == true && (highexceed_count > 0 || (highexceed_count > 0 && moistexceed_count > 0)) &&
midexceed_count < 0.2 &&
dryexceed_count < 0.1 &&
lowexceed_count < 0.1 &&
((totalexceed_count/totalCount) > 0.15 || totalexceed_count > 5)){
paragraphTitle = "Erosion Sources: ";
//If the pollutant target is exceeded primarily in the high flow interval,
//"erosion" processes are the likely pollution source.
}else if (nutrient == true && (dryexceed_count > 0.1 && midexceed_count > 0.1 && moistexceed_count > 0.1) ||
(totalexceed_count/totalCount) > 0.5){
paragraphTitle = "Multiple Pollution Sources: ";
//if the target is almost always exceeded in most of the flow intervals,
//display that multiple sources are likely.
}else{
paragraphTitle = "No Apparent Pollution Source: ";
//If there is no easily identified pollution source, display that information along
//with instructions to get to more information.
}
//Create a graph with the LDC (line) and WQ points (scatter)
XYPlot plot = new XYPlot();
XYDataset LDC_data = new XYSeriesCollection(LDC_xy);
XYItemRenderer renderer1 = new XYLineAndShapeRenderer(true, false);
int seriesIndex = 0;
//Create X Axis
ValueAxis xAxis = new NumberAxis("Percent of Time Load is Exceeded [%]");
xAxis.setRange(0,100);
plot.setDomainAxis(0, xAxis);
//Set log-scale Y axis with scientific notation
LogarithmicAxis yAxis = new LogarithmicAxis(yaxis_title);
yAxis.setAllowNegativesFlag(true);
yAxis.setLog10TickLabelsFlag(true);
plot.setRangeAxis(0, yAxis);
//Put the LDC line data, renderer, and axis into plot
plot.setDataset(seriesIndex, LDC_data);
plot.setRenderer(seriesIndex, renderer1);
//Put the line on the first Domain and first Range
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
//Graph period 1 data
if(period1Data.length > 0){
XYDataset LDC_period = new XYSeriesCollection(LDC_xy_period1);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, new Color(255, 135, 0));//gold
plot.setDataset(seriesIndex, LDC_period);
plot.setRenderer(seriesIndex, renderer);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
}
//Graph period 2 data
if(period2Data.length > 0){
XYDataset LDC_period = new XYSeriesCollection(LDC_xy_period2);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, Color.green);
plot.setDataset(seriesIndex, LDC_period);
plot.setRenderer(seriesIndex, renderer);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
}
//Graph period 3 data
if(period3Data.length > 0){
XYDataset LDC_period = new XYSeriesCollection(LDC_xy_period3);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, Color.cyan);
plot.setDataset(seriesIndex, LDC_period);
plot.setRenderer(seriesIndex, renderer);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
}
//Create mQn Line
if(mQn != 0){//only show mQn if it is not zero
XYDataset mQn_data = new XYSeriesCollection(mQn_xy);
XYItemRenderer renderer2 = new XYLineAndShapeRenderer(true, false);
renderer2.setSeriesPaint(0, Color.black);
plot.setDataset(seriesIndex,mQn_data);
plot.setRenderer(seriesIndex,renderer2);
plot.mapDatasetToDomainAxis(seriesIndex,0);
plot.mapDatasetToRangeAxis(seriesIndex,0);
seriesIndex++;
}
//Create user data points
if(sortedData_user.length != 0){//only show user points if it is not zero
//User LDC line
XYDataset user_data = new XYSeriesCollection(LDC_user);
XYItemRenderer renderer = new XYLineAndShapeRenderer(false, true);
renderer.setSeriesPaint(0, Color.red);
plot.setDataset(seriesIndex,user_data);
plot.setRenderer(seriesIndex,renderer);
plot.mapDatasetToDomainAxis(seriesIndex,0);
plot.mapDatasetToRangeAxis(seriesIndex,0);
seriesIndex++;
}
if(seasonalWQ_user.length != 0){//only show user points if it is not zero
//User seasonal WQ points
XYDataset user_seasonal_wq_data = new XYSeriesCollection(Seasonal_WQ_pts_user);
XYItemRenderer renderer = new XYLineAndShapeRenderer(false, true);
renderer.setSeriesShape(0, new Rectangle2D.Double(-1.5, 4.5, 3.0, 3.0));
renderer.setSeriesPaint(0, Color.gray);
plot.setDataset(seriesIndex,user_seasonal_wq_data);
plot.setRenderer(seriesIndex,renderer);
plot.mapDatasetToDomainAxis(seriesIndex,0);
plot.mapDatasetToRangeAxis(seriesIndex,0);
seriesIndex++;
}
if(WQdata_user.length != 0){//only show user points if it is not zero
//User WQ points
XYDataset user_wq_data = new XYSeriesCollection(WQ_pts_user);
XYItemRenderer renderer = new XYLineAndShapeRenderer(false, true);
renderer.setSeriesShape(0, new Rectangle2D.Double(-3.0, 3.0, 6.0, 6.0));
renderer.setSeriesPaint(0, new Color(105, 0, 255));
plot.setDataset(seriesIndex,user_wq_data);
plot.setRenderer(seriesIndex,renderer);
plot.mapDatasetToDomainAxis(seriesIndex,0);
plot.mapDatasetToRangeAxis(seriesIndex,0);
seriesIndex++;
}
//Graph the seasonal scatter wq data
XYDataset scatter_data4 = new XYSeriesCollection(Seasonal_WQ_pts);
XYItemRenderer renderer4 = new XYLineAndShapeRenderer(false, true);
renderer4.setSeriesShape(0, new Rectangle2D.Double(-1.5, 4.5, 3.0, 3.0));
renderer4.setSeriesPaint(0, Color.black);
plot.setDataset(seriesIndex, scatter_data4);
plot.setRenderer(seriesIndex, renderer4);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
//Graph the complete scatter wq data
XYDataset scatter_data3 = new XYSeriesCollection(WQ_pts);
XYItemRenderer renderer3 = new XYLineAndShapeRenderer(false, true);
renderer3.setSeriesShape(0, new Rectangle2D.Double(-3.0, 3.0, 6.0, 6.0));
renderer3.setSeriesPaint(0, Color.green);
plot.setDataset(seriesIndex, scatter_data3);
plot.setRenderer(seriesIndex, renderer3);
plot.mapDatasetToDomainAxis(seriesIndex, 0);
plot.mapDatasetToRangeAxis(seriesIndex, 0);
seriesIndex++;
//Create box plot of WQ points
boolean showOutliers = false, showExtremeOutliers = false;
if(high_data.size() > 4){
//Create quartile rectangle, min-max line, and median line
Object[] returnArray = Graphing.boxplot_shapes(plot, 5, high_data, seriesIndex, showOutliers, showExtremeOutliers);
plot = (XYPlot) returnArray[0];
showOutliers = (Boolean) returnArray[1];
showExtremeOutliers = (Boolean) returnArray[2];
seriesIndex = (Integer) returnArray[3];
}
if(moist_data.size() > 4){
//Create quartile rectangle, min-max line, and median line
Object[] returnArray = Graphing.boxplot_shapes(plot, 25, moist_data, seriesIndex, showOutliers, showExtremeOutliers);
plot = (XYPlot) returnArray[0];
showOutliers = (Boolean) returnArray[1];
showExtremeOutliers = (Boolean) returnArray[2];
seriesIndex = (Integer) returnArray[3];
}
if(mid_data.size() > 4){
//Create quartile rectangle, min-max line, and median line
Object[] returnArray = Graphing.boxplot_shapes(plot, 50, mid_data, seriesIndex, showOutliers, showExtremeOutliers);
plot = (XYPlot) returnArray[0];
showOutliers = (Boolean) returnArray[1];
showExtremeOutliers = (Boolean) returnArray[2];
seriesIndex = (Integer) returnArray[3];
}
if(dry_data.size() > 4){
//Create quartile rectangle, min-max line, and median line
Object[] returnArray = Graphing.boxplot_shapes(plot, 75, dry_data, seriesIndex, showOutliers, showExtremeOutliers);
plot = (XYPlot) returnArray[0];
showOutliers = (Boolean) returnArray[1];
showExtremeOutliers = (Boolean) returnArray[2];
seriesIndex = (Integer) returnArray[3];
}
if(low_data.size() > 4){
//Create quartile rectangle, min-max line, and median line
Object[] returnArray = Graphing.boxplot_shapes(plot, 95, low_data, seriesIndex, showOutliers, showExtremeOutliers);
plot = (XYPlot) returnArray[0];
showOutliers = (Boolean) returnArray[1];
showExtremeOutliers = (Boolean) returnArray[2];
seriesIndex = (Integer) returnArray[3];
}
//Reformat WQ and Seasonal WQ data for use with JHighCharts
String[] partialWQdata_x = new String[xyRanks.length];
String[] partialWQdata_y = new String[xyRanks.length];
String[] partialSeasonalWQdata_x = new String[xyRanks.length];
String[] partialSeasonalWQdata_y = new String[xyRanks.length];
for(int i=0; i<xyRanks.length; i++){
//Try WQ data
partialWQdata_x[i] = "-1";//value
partialWQdata_y[i] = "-1";//value
partialSeasonalWQdata_x[i] = "-1";//value
partialSeasonalWQdata_y[i] = "-1";//value
try{
partialWQdata_x[i] = String.valueOf(WQ_pts.getX(i));//x
partialWQdata_y[i] = String.valueOf(WQ_pts.getY(i));//y
}catch(IndexOutOfBoundsException e){
//do nothing as it already has a -1 value
}
//Try Seasonal WQ data
try{
partialSeasonalWQdata_x[i] = String.valueOf(Seasonal_WQ_pts.getX(i));//x
partialSeasonalWQdata_y[i] = String.valueOf(Seasonal_WQ_pts.getY(i));//y
}catch(IndexOutOfBoundsException e){
//do nothing as it already has a -1 value
}
}
graphData = DoubleArray.appendcolumn_Matrix(graphData, partialSeasonalWQdata_x);//Add Seasonal WQ x points
graphData = DoubleArray.appendcolumn_Matrix(graphData, partialSeasonalWQdata_y);//Add Seasonal WQ y points
graphData = DoubleArray.appendcolumn_Matrix(graphData, partialWQdata_x);//Add WQ x points
graphData = DoubleArray.appendcolumn_Matrix(graphData, partialWQdata_y);//Add WQ y points
//Graph a LDC for each year in time period
String currentYear = start.substring(0,4);
String finalYear = end.substring(0,4);
boolean moreYears = xyRanks.length > 0;
while(moreYears){
//Get current years data and graph it
String[][] partialData = DoubleArray.getYearsData(sortedData_combined, currentYear);
double[][] partialRanks = DoubleArray.weibullPlottingPosition(partialData);
for(int i=0; i<partialRanks.length; i++){
partialRanks[i][1] = partialRanks[i][1] * conversion * wqTarget;
}
plot = Graphing.addXYSeries(plot, partialRanks, Color.lightGray, seriesIndex);
seriesIndex++;
//Save results for output for JHighCharts
String[] partialLDCData_x = new String[xyRanks.length];
String[] partialLDCData_y = new String[xyRanks.length];
for(int i=0; i<xyRanks.length; i++){
partialLDCData_x[i] = "-1";//value
partialLDCData_y[i] = "-1";//value
try{
partialLDCData_x[i] = String.valueOf(partialRanks[i][0]);//x
partialLDCData_y[i] = String.valueOf(partialRanks[i][1]);//y
}catch(IndexOutOfBoundsException e){
//do nothing as it already has a -1 value
}
}
graphData = DoubleArray.appendcolumn_Matrix(graphData, partialLDCData_x);//Add x points
graphData = DoubleArray.appendcolumn_Matrix(graphData, partialLDCData_y);//Add y points
//Determine the next data year to continue looping over
int nextYear = Integer.parseInt(currentYear) + 1;
if(finalYear.compareToIgnoreCase(String.valueOf(nextYear)) >= 0){
currentYear = String.valueOf(nextYear);
}else{
moreYears = false;
}
}
//Output XY data for use with JHighCharts
DoubleArray.writeXYseries(directory, graphData, getDCgraphOutput().getName());
//Add Flow Range Labels
plot = Graphing.addIntervalLabel(plot, "High Flow", 0, 10);
plot = Graphing.addIntervalLabel(plot, "Moist Conditions", 10, 40);
plot = Graphing.addIntervalLabel(plot, "Mid-Range Flows", 40, 60);
plot = Graphing.addIntervalLabel(plot, "Dry Conditions", 60, 90);
plot = Graphing.addIntervalLabel(plot, "Low Flow", 90, 100);
//Set extra plot preferences
plot = Graphing.setLogYaxisPreferences(plot);
//Graph plot onto JfreeChart
String graphTitle = "Load Duration Curve for " + database + " Station " + stationId + "; " + stationName;
JFreeChart chart = new JFreeChart(graphTitle, Graphing.titleFont, plot, true);
//Set legend Font
LegendTitle legendTitle = chart.getLegend();
legendTitle.setItemFont(Graphing.masterFont);
//Write a results file containing the flow duration curve interval (exceedance values) and their corresponding discharge values
writeResults(graphData, yaxis_title, true);
//Save resulting graph
try{
String path = directory + File.separator + getGraph();
ChartUtilities.saveChartAsJPEG(new File(path), chart, 1280, 800);
System.out.println("JFreeChart created properly at: " + path);
}catch(IOException e){
System.out.println("A problem occurred while trying to creating the chart for station " + stationName + ". Error: USGSLDC0003");
System.out.println(e.toString());
}
Object[] returnArray = {paragraphTitle, totalCount};
return returnArray;
}
/**
* Main statistics function calls other functions to calculate each statistic value then stores the results as global variables
* @param dataList data on which statistical values are desired
*/
private void CalculateStatistics(String[][] data) {
//Convert into double list
ArrayList<Double> dataList = new ArrayList<>();
for(int i=0; i<data.length; i++){
dataList.add(Double.parseDouble(data[i][1]));
}
//Calculate statistics
flowMedian = DoubleMath.round(DoubleMath.median(dataList),3);//Call Median function
flowMean = DoubleMath.round(DoubleMath.meanArithmetic(dataList),3);//Call Mean function
}
/**
* Calculates a summary table of standardized percentiles based on the data
* @param xyRanks a double[][] array with column1 = x (0-100 percentages), column2 = values
* @param xyRanks_period1 a double[][] array with column1 = x (0-100 percentages), column2 = values
* @param xyRanks_period2 a double[][] array with column1 = x (0-100 percentages), column2 = values
* @param xyRanks_period3 a double[][] array with column1 = x (0-100 percentages), column2 = values
* @param title
*/
private void calculatePercentileSummmary(double[][] xyRanks, double[][] xyRanks_period1, double[][] xyRanks_period2, double[][] xyRanks_period3, String title){
//Hard coded percentile summary table
double[] frqDisp = {99, 95, 90, 75, 50, 25, 10, 5, 1};
String[] frqDispString = {"99", "95", "90", "75", "50", "25", "10", "5", "1"};
//Interpolate desired percentiles
double[] yFinal = DoubleMath.linearInterpolation(DoubleArray.getColumn(xyRanks,0), DoubleArray.getColumn(xyRanks,1), frqDisp);
double[] period1 = {-1, -1, -1, -1, -1, -1, -1, -1, -1};
double[] period2 = {-1, -1, -1, -1, -1, -1, -1, -1, -1};
double[] period3 = {-1, -1, -1, -1, -1, -1, -1, -1, -1};
if(xyRanks_period1.length > 0){
period1 = DoubleMath.linearInterpolation(DoubleArray.getColumn(xyRanks_period1,0), DoubleArray.getColumn(xyRanks_period1,1), frqDisp);
}
if(xyRanks_period2.length > 0){
period2 = DoubleMath.linearInterpolation(DoubleArray.getColumn(xyRanks_period2,0), DoubleArray.getColumn(xyRanks_period2,1), frqDisp);
}
if(xyRanks_period3.length > 0){
period3 = DoubleMath.linearInterpolation(DoubleArray.getColumn(xyRanks_period3,0), DoubleArray.getColumn(xyRanks_period3,1), frqDisp);
}
//Round results for summary table
yFinal = DoubleMath.roundColumn(yFinal, 3);
period1 = DoubleMath.roundColumn(period1, 3);
period2 = DoubleMath.roundColumn(period2, 3);
period3 = DoubleMath.roundColumn(period3, 3);
//Create summary table in a single string
summaryTable = "Exceedance Percentile\t" + title + "\tPeriod 1 " + title + "\tPeriod 2 " + title + "\tPeriod 3 " + title;
for(int i=0; i<frqDisp.length; i++){
summaryTable = summaryTable + "\n"
+ frqDispString[i] + "\t"
+ String.valueOf(yFinal[i])+ "\t"
+ String.valueOf(period1[i])+ "\t"
+ String.valueOf(period2[i])+ "\t"
+ String.valueOf(period3[i]);
}
}
/**
* Writes out the non-zero results of the duration curve analysis which are the flow duration curve intervale and their corresponding flow/load values
* @param results double[][] array to be written as each line of the text file
* @param yColumnHeader the header data label for y column data (i.e. flow or load)
* @param ldcTF true if load duration curve, false otherwise
* @throws IOException
*/
private void writeResults(String[][] results, String yColumnHeader, boolean ldcTF) throws IOException{
//Combine column data
ArrayList<String> finalResults = new ArrayList<>();
for(int i=0; i<results.length; i++){
String currentRow = results[i][0];
for(int j=1; j<results[i].length; j++){
currentRow += "\t" + results[i][j];
}
finalResults.add(currentRow);
}
//Create header
int currentYear = Integer.parseInt(start.substring(0,4));
String header = null;
boolean xDataTF = true;
for(int j=0; j<results[0].length; j++){
switch (j) {
case 0:
header = "Percent of Time Flow is Exceeded [%] (overall)";
break;
case 1:
header += "\t" + yColumnHeader + " (overall)";
break;
case 2:
header += "\t" + "Exceedance (Period 1: " + period1Begin + " to " + period1End + ")";
break;
case 3:
header += "\t" + yColumnHeader + " (Period 1: " + period1Begin + " to " + period1End + ")";
break;
case 4:
header += "\t" + "Exceedance (Period 2: " + period2Begin + " to " + period2End + ")";
break;
case 5:
header += "\t" + yColumnHeader + " (Period 2: " + period2Begin + " to " + period2End + ")";
break;
case 6:
header += "\t" + "Exceedance (Period 3: " + period3Begin + " to " + period3End + ")";
break;
case 7:
header += "\t" + yColumnHeader + " (Period 3: " + period3Begin + " to " + period3End + ")";
break;
default:
if(xDataTF){
if(ldcTF && j == 8){
header += "\t" + "Exceedance (Seasonal Water Quality Obs.)";
}else if(ldcTF && j == 10){
header += "\t" + "Exceedance (Water Quality Obs.)";
}else{
header += "\t" + "Exceedance (" + currentYear + ")";
}
xDataTF = false;
}else{
if(ldcTF && j == 9){
header += "\t" + yColumnHeader + " (Seasonal Water Quality Obs.)";
}else if(ldcTF && j == 11){
header += "\t" + yColumnHeader + " (Water Quality Obs.)";
}else{
header += "\t" + yColumnHeader + " (" + currentYear + ")";
currentYear++;
}
xDataTF = true;
}
break;
}
}
String path = directory + File.separator + getDurationCurve_results().getName();
FileWriter writer = new FileWriter(path, false);
PrintWriter printLine = new PrintWriter(writer);
//Add Headers to text file
printLine.printf("%s" + "\r\n", header);
//Output data to text file
for(int i=0; i < finalResults.size(); i++) {
printLine.printf("%s" + "\r\n", finalResults.get(i));
}
System.out.println("Text File located at:\t" + path);
printLine.close();
writer.close();
}
/**
* Create the dynamic paragraph requested
* @param paragraphTitle title of the desired paragraph
* @return the dynamic paragraph to be displayed to the user
*/
public String dynamicParagraph(String paragraphTitle) {
String dynamic_paragraph = paragraphTitle + "\n\n";
if(paragraphTitle.equals("Point Sources and Wastewater Sources: ")){
dynamic_paragraph = dynamic_paragraph + "When the target load duration curve is exceeded primarily under Low Flow and Dry Conditions flow intervals; point sources and wastewater source pollution are the likely cause. Note: in order to have wastewater pollution source there must be an upstream wastewater plant discharging into the watershed. Solutions to this problem may include but are not limited to: point source controls, septic system inspection programs, and sanitary sewer overflow repair (Cleland 2003). In urban areas solutions might involve detecting illicit connections from storm water programs (Cleland 2007). In agricultural settings, solutions may include fencing livestock from riparian areas along waterways, or other similar basic management practices (Cleland 2007).\n";
dynamic_paragraph = dynamic_paragraph + "The grey graphed lines are duration curves for each individual year within the analysis period.\n";
}else if(paragraphTitle.equals("Upper Flow Sources: ")){
dynamic_paragraph = dynamic_paragraph + "Upper flow sources are likely when the target is met except under the High Flows, Moist Conditions, and the upper end of Mid-Range Flows. The higher flows under these regions, primarily above 55 percent, are likely due to large amounts of storm runoff. These flows and sources are similar to those of wet-weather, but are due to larger runoff events and higher flows. The increased pollution may be due to combined sewer overflows, storm water runoff from both up river and nearby, and pasture runoff from agricultural areas (Cleland 2003). Solutions to this pollutant problem may include but are not limited to: combined sewer overflow repair, pasture management, and up river and nearby storm runoff solutions such as riparian buffers (Cleland 2003). In agricultural areas remediation efforts aimed towards grassed waterways, conservation tillage, pasture management practices, and contour strips may help alleviate these pollutions issues (Cleland 2007).\n";
dynamic_paragraph = dynamic_paragraph + "The grey graphed lines are duration curves for each individual year within the analysis period.\n";
}else if(paragraphTitle.equals("Wet-Weather Sources: ")){
dynamic_paragraph = dynamic_paragraph + "When the target load duration curve for pollution concentration is satisfied under the Low Flow and Dry Conditions flow intervals but becomes exceeded during Mid-Range Flows and Moist Conditions primarily above 70 percent flow interval, wet-weather related pollution sources are likely. These mid-size flow rates tend to be the result of light storm runoff with increased pollutant transportation through riparian areas and from impervious regions. Some solutions to these problems may include but are not limited to: riparian buffer zones, agricultural conservation tillage, contour strips, and grassed waterways (Cleland 2007). Combined sewer overflow repair, pet waste ordinances, and hobby farm livestock education may also alleviate the pollution problems due to these wet-weather sources (Cleland 2003).\n";
dynamic_paragraph = dynamic_paragraph + "The grey graphed lines are duration curves for each individual year within the analysis period.\n";
}else if(paragraphTitle.equals("Erosion Sources: ")){
dynamic_paragraph = dynamic_paragraph + "Erosion based pollutant sources are likely when the target is met under all the flow intervals except under the High Flows and upper end of the Moist Conditions. Due to the higher velocities of the water during these flows, bank erosion and channel scour are more likely to occur and release pollutants (particularly sediment) into the water. Solutions to this pollutant problem may include but are not limited to: river bank stabilization efforts and channel protection policies (Cleland 2007). Pollutants related to sediment concentration will be those most affected by these efforts.\n";
dynamic_paragraph = dynamic_paragraph + "The grey graphed lines are duration curves for each individual year within the analysis period.\n";
}else if(paragraphTitle.equals("Multiple Pollution Sources: ")){
dynamic_paragraph = dynamic_paragraph + "Most of the flow intervals contain many points which exceed the target. No single pollution source is likely. Please click Further Information for more pollutant identification help.\n";
dynamic_paragraph = dynamic_paragraph + "The grey graphed lines are duration curves for each individual year within the analysis period.\n";
}else if(paragraphTitle.equals("Flow Duration Curve Overview: ")){
dynamic_paragraph = dynamic_paragraph + "A flow duration curve (FDC) is the ranked graphing of river flows on a scale of percent exceedance. For example a flow value associated with the flow interval of 15% means that particular flow value is met or exceeded only 15% of the time. This graph is meant to give a quick overview of the flow ranges, variability, and probability of flows of a river segment during the different flow periods of a river; which are High Flows from 0 to 10 percent flow interval, Moist Conditions 10-40, Mid-Range Flows 40-60, Dry Conditions 60-90, and Low Flows 90-100 (Cleland 2003).\n";
dynamic_paragraph = dynamic_paragraph + "The grey graphed lines are duration curves for each individual year within the analysis period or annual flow duration curves.\n";
}else{
dynamic_paragraph = dynamic_paragraph + "Although some observed points may exceed the target curves concentration there is no single apparent pollutant source.\n";
dynamic_paragraph = dynamic_paragraph + "Please click Further Model Information for more pollutant source identification help.\n";
}
//Create references for paragraph
dynamic_paragraph = dynamic_paragraph + "References:\n";
dynamic_paragraph = dynamic_paragraph + dataSource + "\n";
dynamic_paragraph = dynamic_paragraph + "Cleland, B. R. November 2003. TMDL Development from the Bottom Up Part III: Duration Curves and Wet-Weather Assessments. National TMDL Science and Policy 2003.\n";
dynamic_paragraph = dynamic_paragraph + "Cleland, B. R. August 2007. An Approach for Using Load Duration Curves in the Development of TMDLs. National TMDL Science and Policy 2007.";
return dynamic_paragraph;
}
/**
* 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);
}
/**
* Primary FDC/LDC model
* It calls the subfunctions based on user selection/inputs.
* Calls CDWR, STORET, or USGS database queries and their respective subfunctions
* @throws IOException
* @throws InterruptedException
*/
public void run() throws IOException, InterruptedException, ParseException, Exception {
//If no date input, make it the maximum of available data
if(startDate == null || startDate.equalsIgnoreCase("")){
startDate = "1850-01-01";
}
if(endDate == null || endDate.equalsIgnoreCase("")){
// Pull current date for upper limit of data search
DateFormat desiredDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date currentDate = new Date();
endDate = desiredDateFormat.format(currentDate);
}
//Determine which Duration Curve method to run, FDC or LDC
if(wqTest.equalsIgnoreCase("flow")){
createFDC();
}else{
createLDC();
}
}
public static void main(String[] args) throws IOException, InterruptedException, Exception {
//Define and run model
guiDC_Model dc_Model = new guiDC_Model();
dc_Model.run();
}
}