Graphing.java [src/java/cfa] Revision: ffc412d47d72be5013c8a0a59c535082523410cb Date: Tue Dec 16 12:11:46 MST 2014
package cfa;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.IntervalMarker;
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;
import org.jfree.ui.Layer;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.TextAnchor;
/**
* Last Updated: 16-December-2014
* @author Tyler Wible
* @since 3-February-2014
*/
public class Graphing {
private String fontFamily = "SansSerif";
Font titleFont = new Font(fontFamily, Font.BOLD, 30);
Font masterFont = new Font(fontFamily, Font.PLAIN, 22);
Font DClabelFont = new Font(fontFamily, Font.ITALIC, 20);
Font floodLabelFont = new Font(fontFamily, Font.PLAIN, 18);
//Calibri
//Georgia
//SansSerif
//Serif
//Tahoma
//Verdana
/**
* Sub-graphing function to add graph x-interval marker/label
* @param plot the graph on which to add the interval
* @param Label_title the desired label title.
* @param lowerlimit the lower limit of the x-interval marker.
* @param upperlimit the upper limit of the x-interval marker.
* @return An Interval Marker.
*/
public XYPlot addIntervalLabel (XYPlot plot, String Label_title, double lowerlimit, double upperlimit){
//Create a new interval marker and set its properties
final IntervalMarker newLabel = new IntervalMarker(lowerlimit,upperlimit);
newLabel.setLabel(Label_title);
newLabel.setLabelFont(DClabelFont);
newLabel.setLabelAnchor(RectangleAnchor.BOTTOM);
newLabel.setLabelTextAnchor(TextAnchor.BASELINE_CENTER);
newLabel.setPaint(plot.getBackgroundPaint());//new Color(250, 250, 250, 100));//new Color(222, 222, 255, 130));
newLabel.setOutlinePaint(Color.black);
//Add the interval to the provided plot
plot.addDomainMarker(newLabel, Layer.BACKGROUND);
return plot;
}
/**
* Sub-function to create lines/rectangle to mimic a box plot
* @param plot the plot in which the objects are being graphed
* @param x_coord x coordinate of the correct boxplot
* @param data water quality data within the current flow interval
* @param series_int which element in the plot series is being created
* @param existingOutliers a variable indicating if there are already
* existing outliers (true) which are already labeled in the legend,
* therefore don't label these outliers in the legend
* @param existingExtremeOutliers a variable indicating if there are already
* existing extreme outliers (true) which are already labeled in the legend,
* therefore don't label these outliers in the legend
* @return the original plot with new object in it.
*/
public Object[] boxplot_shapes(XYPlot plot,
double x_coord,
ArrayList<Double> data,
int series_int,
boolean existingOutliers,
boolean existingExtremeOutliers){
DoubleMath doubleMath = new DoubleMath();
//Calculate and add Median to dataset
XYSeries median_series = new XYSeries("Medians");
double mid_median = doubleMath.Percentile_function(data,0.50);
median_series.add(x_coord,mid_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, 0.5));//new Ellipse2D.Double(-4, -4, 8, 8));
renderer_median.setSeriesPaint(0, Color.red);
renderer_median.setSeriesVisibleInLegend(0, false);
plot.setDataset(series_int, median_scatter);
plot.setRenderer(series_int, renderer_median);
series_int++;
//Create quartile Box shapes for the box plot
//Create XYSeries for the box shape
XYSeries shapeSeries = new XYSeries("Shape");
double lowerQuartile = doubleMath.Percentile_function(data,0.25);
double upperQuartile = doubleMath.Percentile_function(data,0.75);
shapeSeries.add(x_coord, lowerQuartile);
shapeSeries.add(x_coord, upperQuartile);
//Create the quartile rectangle shape
XYDataset shapeDataset = new XYSeriesCollection(shapeSeries);
XYItemRenderer renderer_shape = new XYLineAndShapeRenderer(true, false);
Stroke thickness = new BasicStroke(8);
renderer_shape.setSeriesStroke(0, thickness);
renderer_shape.setSeriesPaint(0, Color.blue);
renderer_shape.setSeriesVisibleInLegend(0, false);
plot.setDataset(series_int, shapeDataset);
plot.setRenderer(series_int, renderer_shape);
series_int++;
//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 < doubleMath.min(data)){
lowerLimit = doubleMath.min(data);
}
if(upperLimit > doubleMath.max(data)){
upperLimit = doubleMath.max(data);
}
XYSeries lineSeries = new XYSeries("Line");
lineSeries.add(x_coord, lowerLimit);
lineSeries.add(x_coord, upperLimit);
//Create the 1.5*IQR lines
XYDataset lineDataset = new XYSeriesCollection(lineSeries);
XYItemRenderer lineRenderer = new XYLineAndShapeRenderer(true, true);
Stroke thickness2 = new BasicStroke(1);
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(series_int, lineDataset);
plot.setRenderer(series_int, lineRenderer);
series_int++;
//Create additional output for use with JHighCharts instead/in addition to JFreeCharts
double[] boxplotData = {upperLimit,upperQuartile,mid_median,lowerQuartile,lowerLimit};
ArrayList<Double> outliersJHighCharts = new ArrayList<Double>();// Get daily outliers
//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.size(); i++){
double value = data.get(i);
//Lower outliers
if(value < (lowerQuartile - 1.5*IQR) && value > (lowerQuartile - 3*IQR)){
outliers.add(x_coord, value);
outliersJHighCharts.add(value);
}
//Upper outliers
if(value > (upperQuartile + 1.5*IQR) && value < (lowerQuartile + 3*IQR)){
outliers.add(x_coord, value);
outliersJHighCharts.add(value);
}
//Extreme Lower outliers
if(value < (lowerQuartile - 3*IQR)){
extremeOutliers.add(x_coord, value);
outliersJHighCharts.add(value);
}
//Extreme Upper outliers
if(value > (lowerQuartile + 3*IQR)){
extremeOutliers.add(x_coord, value);
outliersJHighCharts.add(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.darkGray);
if(outliers.isEmpty() || existingOutliers == true){
renderer_outlier.setSeriesVisibleInLegend(0, false);
}else{
existingOutliers = true;
}
plot.setDataset(series_int, outlier_scatter);
plot.setRenderer(series_int, renderer_outlier);
series_int++;
//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() || existingExtremeOutliers == true){
renderer_ExtremeOutlier.setSeriesVisibleInLegend(0, false);
}else{
existingExtremeOutliers = true;
}
plot.setDataset(series_int, extremeOutlier_scatter);
plot.setRenderer(series_int, renderer_ExtremeOutlier);
series_int++;
Object[] returnArray = {plot, existingOutliers, existingExtremeOutliers, series_int, outliersJHighCharts, boxplotData};
return returnArray;
}
/**
* Graphs the provided "series" as an XY series with x as the first column y as the second column in the provided color
* on the provided XYPlot, this series is not visible in the legend
* @param plot the XYPlot to add the "series" to
* @param series a double[][] array with series[all][0] = x-values, series[all][1] = y-values
* @param lineColor the color of the line to be graphed
* @param seriesIndex the graph index of the series to be plotted
* @return the provided XYPlot with the series added to it with the above properties
*/
public XYPlot graphSeries(XYPlot plot, double[][] series, Color lineColor, int seriesIndex){
XYSeries xySeries = new XYSeries("");
for(int i=0; i<series.length; i++){
double y = series[i][1];
double x = series[i][0];
xySeries.add(x, y);
}
//Create a graph with the line
XYDataset xyDataset = new XYSeriesCollection(xySeries);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, lineColor);
renderer.setSeriesVisibleInLegend(0, false);
//Set the FDC line data, renderer, and axis into plot
plot.setDataset(seriesIndex, xyDataset);
plot.setRenderer(seriesIndex, renderer);
//Map the line to the first Domain and first Range
plot.mapDatasetToDomainAxis(0, 0);
plot.mapDatasetToRangeAxis(0, 0);
return plot;
}
/**
* Graphs the provided "series" as an XY series with x as the first column y as the second column in the provided color
* on the provided XYPlot, this series is not visible in the legend
* @param plot the XYPlot to add the "series" to
* @param series a String[][] array with series[all][0] = dates in yyyy-MM-dd format, series[all][1] = y-values
* @param timeStep a flag for what data is being graphed ("Daily", "Monthly", or "Yearly")
* @param seriesName the name of the series to appear in the legend (if showInLegend is true)
* @param lineColor the color of the line to be graphed
* @param seriesIndex the graph index of the series to be plotted
* @param showInLegend a flag to show this series in the legend (true) or not (false)
* @param leapYearTF a flag to check if the main dataset is a leap year or not so that if graphing data repeatedly for one year (see timeseries envelope graph) Feb. 29th does or doesnot get plotted
* @return the provided XYPlot with the series added to it with the above properties
*/
public XYPlot graphSeries(XYPlot plot,
String[][] series,
String timeStep,
String seriesName,
Color lineColor,
int seriesIndex,
boolean showInLegend,
boolean leapYearTF){
TimeSeries timeSeries = new TimeSeries(seriesName);
for(int i=0; i<series.length; i++){
String tmpStr = series[i][0];
double value = Double.parseDouble(series[i][1]);
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;
try{
Day date = new Day(day,month,year);//day,month,year
timeSeries.add(date, value);
}catch(IllegalArgumentException e){
//If there is an error with the date and it is not due to trying
//to plot Feb. 29th in a non-leap year then throw the error
if(!leapYearTF && month!=2 && day!=29){
throw new IllegalArgumentException (e);
}
}
}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
timeSeries.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
timeSeries.add(date, value);
}
}
//Create a graph with the line
TimeSeriesCollection currentDataset = new TimeSeriesCollection(timeSeries);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, lineColor);
renderer.setSeriesVisibleInLegend(0, showInLegend);
//Set the line data, renderer, and axis into plot
plot.setDataset(seriesIndex, currentDataset);
plot.setRenderer(seriesIndex, renderer);
//Map the line to the first Domain and first Range
plot.mapDatasetToDomainAxis(0, 0);
plot.mapDatasetToRangeAxis(0, 0);
return plot;
}
/**
* Graphs the TimeSeries on to the provided plot with the below properties for rendering, color, series index
* @param plot The XYPlot to graph the XYSeries on
* @param currentLine the XYSeries containing the x,y points of the current line series
* @param lineTrue a boolean, true if the series is to have lines connecting the points, otherwise no line
* @param seriesColor The desired color of the series (Java.Color)
* @param dashedLine if true then the XYSeries will have a dashed line instead of a solid one, if false it will be the normal solid line
* @param shortDash if true a dashed line like the major grid lines will be drawn, otherwise a long dashed line will be used
* @param thickLine if true the line will have a thicker stroke, if false it will be left to the defaults
* @param visibleInLegend if true then the XYSeries' name will be visible in the legend, if false this XYSeries' name will not be visible in the legend
* @param graphIndex the series index for the current line, this should be incrementally increased
* each time this function is call so that the new dataset does not replace the old one
* @return the XYPlot with the added TimeSeries
*/
public XYPlot graphTimeData(XYPlot plot,
TimeSeries currentLine,
boolean lineTrue,
Color seriesColor,
boolean dashedLine,
boolean shortDash,
boolean thickLine,
boolean visibleInLegend,
int graphIndex){
//Create Line Data and renderer
TimeSeriesCollection currentDataset = new TimeSeriesCollection(currentLine);
//Check if this series should have a dashed line or solid line and change the renderer accordingly
XYItemRenderer currentRenderer = new XYLineAndShapeRenderer(lineTrue, !lineTrue);
currentRenderer.setSeriesPaint(0, seriesColor);
currentRenderer.setSeriesVisibleInLegend(0, visibleInLegend);
//Check if this series should have a dashed line, if so change the renderer
if(dashedLine){
if(shortDash){
//Change the renderer's stroke to a short dashed line
currentRenderer.setSeriesStroke(0,
new BasicStroke(
1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
1.0f, new float[] {1.0f, 2.0f}, 0.0f
));
}else{
//Change the renderer's stroke to a long dashed line
currentRenderer.setSeriesStroke(0,
new BasicStroke(
1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
1.0f, new float[] {6.0f, 6.0f}, 0.0f
));
}
}
//Check if the series should have a thicker line
if(thickLine){
currentRenderer.setSeriesStroke(0,
new BasicStroke(
3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
1.0f, new float[] {6.0f, 0.0f}, 0.0f
));
}
//Put the line data, renderer, and axis into plot
plot.setDataset(graphIndex, currentDataset);
plot.setRenderer(graphIndex, currentRenderer);
//Put the line on the first Domain and first Range
plot.mapDatasetToDomainAxis(0, 0);
plot.mapDatasetToRangeAxis(0, 0);
return plot;
}
/**
* 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
*/
public XYPlot setTimeAxisPreferences(XYPlot plot){
//Set Y axis fonts
ValueAxis yAxis = plot.getRangeAxis();
yAxis.setLabelFont(masterFont);
yAxis.setTickLabelFont(masterFont);
//Set X axis fonts
DateAxis xAxis = (DateAxis) plot.getDomainAxis();
xAxis.setLabelFont(masterFont);
xAxis.setTickLabelFont(masterFont);
//Set extra plot preferences
plot.setOutlinePaint(Color.black);
plot.setDomainGridlinePaint(Color.black);
plot.setRangeGridlinePaint(Color.black);
plot.setRangeMinorGridlinesVisible(true);
plot.setRangeMinorGridlinePaint(Color.gray);
return plot;
}
/**
* 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
*/
public XYPlot setAxisPreferences(XYPlot plot){
//Set Y axis fonts
ValueAxis yAxis = plot.getRangeAxis();
yAxis.setLabelFont(masterFont);
yAxis.setTickLabelFont(masterFont);
//Set X axis fonts
ValueAxis xAxis = plot.getDomainAxis();
xAxis.setLabelFont(masterFont);
xAxis.setTickLabelFont(masterFont);
//Set extra plot preferences
plot.setOutlinePaint(Color.black);
plot.setDomainGridlinePaint(Color.black);
plot.setRangeGridlinePaint(Color.black);
plot.setRangeMinorGridlinesVisible(true);
plot.setRangeMinorGridlinePaint(Color.gray);
return plot;
}
/**
* 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
*/
public XYPlot setLogYaxisPreferences(XYPlot plot){
//Set Y axis fonts
LogarithmicAxis yAxis = (LogarithmicAxis) plot.getRangeAxis();
yAxis.setLabelFont(masterFont);
yAxis.setTickLabelFont(masterFont);
//Set X axis fonts
ValueAxis xAxis = (ValueAxis) plot.getDomainAxis();
xAxis.setLabelFont(masterFont);
xAxis.setTickLabelFont(masterFont);
//Set extra plot preferences
plot.setOutlinePaint(Color.black);
plot.setDomainGridlinePaint(Color.black);
plot.setRangeGridlinePaint(Color.black);
return plot;
}
/**
* 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
*/
public XYPlot setLogXaxisPreferences(XYPlot plot){
//Set Y axis fonts
ValueAxis xAxis = (ValueAxis) plot.getRangeAxis();
xAxis.setLabelFont(masterFont);
xAxis.setTickLabelFont(masterFont);
//Set X axis fonts
LogarithmicAxis yAxis = (LogarithmicAxis) plot.getDomainAxis();
yAxis.setLabelFont(masterFont);
yAxis.setTickLabelFont(masterFont);
//Set extra plot preferences
plot.setOutlinePaint(Color.black);
plot.setDomainGridlinePaint(Color.black);
plot.setRangeGridlinePaint(Color.black);
return plot;
}
/**
* This subfunction takes the provided plot and changes the fonts of the axis to a standardized new font
* @param plot the CategoryPlot containing the graph on which the fonts will be changed
* @return the original plot with the modifications to the axis fonts
*/
public CategoryPlot setCategoryAxisPreferences(CategoryPlot plot){
//Set Y axis fonts
ValueAxis yAxis = plot.getRangeAxis();
yAxis.setLabelFont(masterFont);
yAxis.setTickLabelFont(masterFont);
//Set X axis fonts
CategoryAxis xAxis = plot.getDomainAxis();
xAxis.setLabelFont(masterFont);
xAxis.setTickLabelFont(masterFont);
//Set extra plot preferences
plot.setOutlinePaint(Color.black);
plot.setDomainGridlinePaint(Color.black);
plot.setDomainGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.black);
plot.setRangeMinorGridlinesVisible(true);
plot.setRangeMinorGridlinePaint(Color.gray);
return plot;
}
}