package charts.graphics;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.StatisticalLineAndShapeRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.statistics.StatisticalCategoryDataset;
import org.jfree.text.TextBlock;
import org.jfree.text.TextUtilities;
import org.jfree.ui.RectangleEdge;
import boxrenderer.ContentBoxImpl;
import boxrenderer.TableBox;
import boxrenderer.TableCellBox;
import boxrenderer.TableRowBox;
import boxrenderer.TextBox;
import charts.Drawable;
import charts.jfree.ADSCDataset;
import charts.jfree.Attribute;
public class TrendsSeagrassAbundance {
// This renderer does not break the line in case the previous data point is missing but instead
// searches the dataset until it finds a previous value further back.
// Some code was copied from StatisticalLineAndShapeRenderer::drawItem to make this work.
private static class SLSRenderer extends StatisticalLineAndShapeRenderer {
private Font legendFont;
public SLSRenderer(Font legendFont) {
this.legendFont = legendFont;
}
private Integer findPreviousIndex(StatisticalCategoryDataset statDataset, int row, int column) {
for(int i = column - 1; i >=0;i--) {
if(statDataset.getValue(row, i) != null) {
return i;
}
}
return null;
}
private void drawLegend(Graphics2D g2, Rectangle2D dataArea) {
Graphics2D g0 = null;
try {
TableBox legend = new TableBox();
legend.getMargin().setRight(5);
for(int i = 0; i < 2;i++) {
ContentBoxImpl c = new ContentBoxImpl();
c.setBackground(this.getSeriesPaint(i));
c.setWidth(25);
c.setHeight(2);
c.getMargin().setRight(5);
TableCellBox legendLineBox = new TableCellBox(c);
TableCellBox labelBox = new TableCellBox(
new TextBox(i==0?"Intertidal":"Subtidal", legendFont));
legend.addRow(new TableRowBox(legendLineBox, labelBox));
}
Dimension d = legend.getDimension(g2);
g0 = (Graphics2D)g2.create((int)(dataArea.getMaxX()-d.getWidth()), 80, d.width, d.height);
legend.render(g0);
} catch(Exception e) {
e.printStackTrace();
} finally {
if(g0 != null) {
g0.dispose();
}
}
}
private void _drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea,
CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset,
int row, int column, int pass) {
StatisticalCategoryDataset statDataset = (StatisticalCategoryDataset) dataset;
int visibleRow = state.getVisibleSeriesIndex(row);
int visibleRowCount = state.getVisibleSeriesCount();
PlotOrientation orientation = plot.getOrientation();
// current data point...
double x1;
if (getUseSeriesOffset()) {
x1 = domainAxis.getCategorySeriesMiddle(column,
dataset.getColumnCount(),
visibleRow, visibleRowCount,
getItemMargin(), dataArea, plot.getDomainAxisEdge());
}
else {
x1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
dataArea, plot.getDomainAxisEdge());
}
Number meanValue = statDataset.getMeanValue(row, column);
if (meanValue == null) {
return;
}
double y1 = rangeAxis.valueToJava2D(meanValue.doubleValue(), dataArea,
plot.getRangeAxisEdge());
if (pass == 0 && getItemLineVisible(row, column)) {
if (column != 0) {
Number previousValue = statDataset.getValue(row, column - 1);
if(previousValue != null) {
// ignore this case here because the call to super.drawItem(...) took care if it.
return;
} else {
// previous data point...
Integer pIdx = findPreviousIndex(statDataset, row, column);
if(pIdx == null) {
return;
}
previousValue = statDataset.getValue(row, pIdx.intValue());
double previous = previousValue.doubleValue();
double x0;
if (getUseSeriesOffset()) {
x0 = domainAxis.getCategorySeriesMiddle(
pIdx.intValue(), dataset.getColumnCount(),
visibleRow, visibleRowCount,
getItemMargin(), dataArea,
plot.getDomainAxisEdge());
}
else {
x0 = domainAxis.getCategoryMiddle(pIdx.intValue(),
getColumnCount(), dataArea,
plot.getDomainAxisEdge());
}
double y0 = rangeAxis.valueToJava2D(previous, dataArea,
plot.getRangeAxisEdge());
Line2D line = null;
if (orientation == PlotOrientation.HORIZONTAL) {
line = new Line2D.Double(y0, x0, y1, x1);
}
else if (orientation == PlotOrientation.VERTICAL) {
line = new Line2D.Double(x0, y0, x1, y1);
}
g2.setPaint(getItemPaint(row, column));
g2.setStroke(getItemStroke(row, column));
g2.draw(line);
}
}
}
}
public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea,
CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset,
int row, int column, int pass) {
super.drawItem(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column, pass);
_drawItem(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column, pass);
// draw the legend last and only if the chart contains the subtidal series (row == 1)
if((row == 1) && (pass == 1) && (column == dataset.getColumnCount()-1)) {
drawLegend(g2, dataArea);
}
}
}
public static Drawable createChart(final ADSCDataset dataset, Dimension dimension) {
JFreeChart chart = ChartFactory.createLineChart(
dataset.get(Attribute.TITLE), // title
"", // x-axis label
dataset.get(Attribute.Y_AXIS_LABEL), // y-axis label
dataset, // data
PlotOrientation.VERTICAL,
false, // create legend?
false, // generate tooltips?
false // generate URLs?
);
chart.setBackgroundPaint(Color.white);
CategoryPlot plot = (CategoryPlot)chart.getPlot();
CategoryAxis caxis = plot.getDomainAxis();
StatisticalLineAndShapeRenderer renderer = new SLSRenderer(caxis.getTickLabelFont());
renderer.setErrorIndicatorPaint(ErrorIndicator.ERROR_INDICATOR_COLOR);
renderer.setDrawOutlines(true);
renderer.setUseOutlinePaint(true);
renderer.setBaseOutlinePaint(Color.black);
plot.setRenderer(renderer);
plot.setRangeGridlinePaint(Color.lightGray);
plot.setRangeGridlineStroke(new BasicStroke(1.0f));
plot.setDomainGridlinesVisible(false);
plot.setBackgroundPaint(Color.white);
renderer.setSeriesPaint(0, dataset.get(Attribute.SERIES_COLOR));
final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setTickUnit(new NumberTickUnit(10));
rangeAxis.setRange(0.0, 100.0);
rangeAxis.setTickMarksVisible(false);
chart.getTitle().setFont(rangeAxis.getLabelFont());
CategoryAxis cAxis = new CategoryAxis() {
private boolean last(int col, int colCount) {
return col == (colCount - 1);
}
private boolean nextToLast(int col, int colCount) {
return col == (colCount - 2);
}
@SuppressWarnings("rawtypes")
private TextBlock label(Comparable category, String label) {
return TextUtilities.createTextBlock(label,
getTickLabelFont(category), getTickLabelPaint(category));
}
@SuppressWarnings("rawtypes")
@Override
protected TextBlock createLabel(Comparable category, float width,
RectangleEdge edge, Graphics2D g2) {
final int colCount = dataset.getColumnCount();
final int mod = colCount / 25 + 1;
final int col = dataset.getColumnIndex(category);
if(mod > 1 && nextToLast(col, colCount)) {
return label(category, "");
} else if((col % mod == 0) || last(col, colCount)) {
return label(category, category.toString());
} else {
return label(category, "");
}
}
};
cAxis.setLabel(dataset.get(Attribute.X_AXIS_LABEL));
cAxis.setLabelFont(rangeAxis.getLabelFont());
cAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);
plot.setDomainAxis(cAxis);
chart.addLegend(ErrorIndicatorLegend.createLegend());
return new JFreeChartDrawable(chart, dimension);
}
}