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.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import org.apache.commons.lang3.StringUtils; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.AxisState; 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.plot.CategoryPlot; import org.jfree.chart.plot.DrawingSupplier; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.AbstractRenderer; import org.jfree.chart.renderer.category.StatisticalLineAndShapeRenderer; import org.jfree.data.statistics.DefaultStatisticalCategoryDataset; import org.jfree.text.TextBlock; import org.jfree.text.TextUtilities; import org.jfree.ui.RectangleEdge; import boxrenderer.Align; import boxrenderer.Box; import boxrenderer.TableBox; import boxrenderer.TableCellBox; import boxrenderer.TableRowBox; import boxrenderer.TextBox; import charts.ChartType; import charts.Drawable; import charts.Region; import charts.jfree.ADSCDataset; import charts.jfree.Attribute; public class CoralCover { public static Drawable createChart(final ADSCDataset dataset, ChartType type, Region region, Dimension dimension) { JFreeChart chart; if(region == Region.GBR) { chart = createChart(rearrange(dataset), type); CategoryPlot plot = (CategoryPlot)chart.getPlot(); CategoryAxis cAxis = getSubCategoryAxis(dataset); cAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45); cAxis.setLabelFont(plot.getRangeAxis().getLabelFont()); cAxis.setTickLabelFont(plot.getRangeAxis().getTickLabelFont()); cAxis.setTickMarksVisible(false); cAxis.setLabel(dataset.get(Attribute.X_AXIS_LABEL)); plot.setDomainAxis(cAxis); } else { chart = createChart(dataset, type); } return new JFreeChartDrawable(chart, dimension); } private static JFreeChart createChart(final ADSCDataset dataset, ChartType type) { JFreeChart chart = ChartFactory.createLineChart( dataset.get(Attribute.TITLE), // title dataset.get(Attribute.X_AXIS_LABEL), // 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); StatisticalLineAndShapeRenderer renderer = new StatisticalLineAndShapeRenderer(); renderer.setErrorIndicatorPaint(ErrorIndicator.ERROR_INDICATOR_COLOR); CategoryPlot plot = (CategoryPlot)chart.getPlot(); plot.setRenderer(renderer); plot.setRangeGridlinePaint(Color.lightGray); plot.setDomainGridlinePaint(Color.lightGray); plot.setRangeGridlineStroke(new BasicStroke(1.0f)); plot.setDomainGridlinesVisible(false); plot.setBackgroundPaint(Color.white); renderer.setDrawOutlines(true); renderer.setUseOutlinePaint(true); renderer.setUseFillPaint(false); plot.setDrawingSupplier(new DrawingSupplier() { @Override public Paint getNextPaint() { return dataset.get(Attribute.SERIES_COLOR); } @Override public Paint getNextOutlinePaint() { return Color.black; } @Override public Paint getNextFillPaint() { return null; } @Override public Stroke getNextStroke() { return new BasicStroke(1.0f); } @Override public Stroke getNextOutlineStroke() { return new BasicStroke(1.0f); } @Override public Shape getNextShape() { return AbstractRenderer.DEFAULT_SHAPE; }}); renderer.setBaseOutlinePaint(Color.black); final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); rangeAxis.setTickMarksVisible(false); rangeAxis.setRange(0, parseDouble(dataset.get(Attribute.Y_AXIS_RANGE), 100.0)); rangeAxis.setTickUnit(new NumberTickUnit(parseDouble( dataset.get(Attribute.Y_AXIS_TICKS), 10.0), new DecimalFormat("0"))); chart.getTitle().setFont(rangeAxis.getLabelFont()); chart.addLegend(ErrorIndicatorLegend.createLegend()); return chart; } private static double parseDouble(String s, double def) { try { return Double.parseDouble(s); } catch(NumberFormatException e) { return def; } } // rearrange the dataset so, that the lines are drawn next to each other // (and not on top of each other) private static ADSCDataset rearrange(ADSCDataset d) { ADSCDataset ds = new ADSCDataset(); ds.attrMap().putAll(d.attrMap()); int space = 0; for(int r = 0;r<d.getRowCount();r++) { for(int c = 0;c<d.getColumnCount();c++) { Comparable<?> row = d.getRowKey(r); Comparable<?> col = d.getColumnKey(c); Number mean = d.getMeanValue(row, col); Number stddev = d.getStdDevValue(row, col); ds.add(mean, stddev, row, String.format("%s %s", col.toString(), row.toString())); } if((r+1) < d.getRowCount()) { ds.add(null, null, d.getRowKey(r), "space"+space++); } } return ds; } private static CategoryAxis getSubCategoryAxis(final DefaultStatisticalCategoryDataset dataset) { return new CategoryAxis() { @SuppressWarnings("rawtypes") @Override protected TextBlock createLabel(Comparable category, float width, RectangleEdge edge, Graphics2D g2) { String label = ""; try { label = new Integer(StringUtils.split(category.toString())[0]).toString(); } catch(Exception e) {} return TextUtilities.createTextBlock(label, getTickLabelFont(category), getTickLabelPaint(category)); } @Override protected AxisState drawLabel(String label, Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, AxisState state) { double labely = state.getCursor(); Graphics2D g0 = null; try { Box b = getLabelBox((int)dataArea.getWidth()); int height = (int)b.getDimension(g2).getHeight(); g0 = (Graphics2D)g2.create((int)dataArea.getX(), (int)labely, (int)dataArea.getWidth(), height); b.render(g0); state.cursorDown(height); } catch(Exception e) { throw new RuntimeException(e); } finally { g0.dispose(); } return state; } @Override public AxisSpace reserveSpace(Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space) { AxisSpace s = super.reserveSpace(g2, plot, plotArea, edge, space); Box b = getLabelBox((int)plotArea.getWidth()); try { // workaround, divide by 4 so reduce the large gap to the standard error indicator legend s.add(b.getDimension(g2).getHeight()/4, RectangleEdge.BOTTOM); } catch(Exception e) { throw new RuntimeException(e); } return s; } private Box getLabelBox(int width) { TableRowBox r1 = new TableRowBox(); for(int i=0;i<dataset.getRowCount();i++) { String row = dataset.getRowKey(i).toString(); r1.addCell(newCell(row, width/dataset.getRowCount(), getTickLabelFont())); } TableRowBox r2 = new TableRowBox(); TableCellBox b1 = newCell(getLabel(), width, getLabelFont()); b1.setColspan(0); r2.addCell(b1); TableBox box = new TableBox(); box.setWidth(width); box.addRow(r1); box.addRow(r2); box.getMargin().setTop(4); return box; } private TableCellBox newCell(String label, int width, Font font) { TextBox t = new TextBox(label, font); TableCellBox b = new TableCellBox(t); b.setWidth(width); b.setAlign(Align.CENTER); return b; } }; } }