package charts.builder.spreadsheet; import java.awt.Color; import java.awt.Dimension; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.jfree.data.statistics.DefaultStatisticalCategoryDataset; import org.supercsv.io.CsvListWriter; import charts.ChartType; import charts.Drawable; import charts.Region; import charts.builder.DataSource.MissingDataException; import charts.builder.csv.Csv; import charts.builder.csv.CsvWriter; import charts.graphics.CoralCover; import charts.jfree.ADSCDataset; import charts.jfree.Attribute; import charts.jfree.AttributeMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; public abstract class CoralCoverBuilder extends JFreeBuilder { protected static final String HC_MEAN = "HC mean"; protected static final String HC_SE = "HC se"; protected static final String SC_MEAN = "SC mean"; protected static final String SC_SE = "SC se"; protected static final String MA_MEAN = "MA mean"; protected static final String MA_SE = "MA se"; protected static final String JUV_DEN = "Juv density"; protected static final String JUV_DEN_SE = "Juv Den (se)"; protected static final String COVER = "Cover (%)"; protected static final String JUVENILE = "Juveniles/m\u00b2"; private static final ImmutableMap<String, Region> REGIONS = ImmutableMap.of( "Wet Tropics", Region.WET_TROPICS, "Burdekin", Region.BURDEKIN, "Whitsunday", Region.MACKAY_WHITSUNDAY, "Fitzroy", Region.FITZROY); protected abstract String getMeanColumn(); protected abstract String getSeColumn(); public CoralCoverBuilder(ChartType type) { super(type); } @Override public boolean canHandle(SpreadsheetDataSource ds) { try { return StringUtils.equalsIgnoreCase(ds.select(0, 2).asString(), HC_MEAN) && StringUtils.equalsIgnoreCase(ds.select(0, 3).asString(), HC_SE); } catch(MissingDataException e) { return false; } } private boolean eof(SpreadsheetDataSource ds, int row) throws MissingDataException { if(StringUtils.isBlank(ds.select(row, 0).asString()) && StringUtils.isBlank(ds.select(row+1, 0).asString())) { return true; } else { return false; } } private List<Region> regions(SpreadsheetDataSource ds) { try { Set<Region> regions = Sets.newLinkedHashSet(); for(int row=0;!eof(ds, row); row++) { if(REGIONS.containsKey(ds.select(row, 0).asString())) { regions.add(REGIONS.get(ds.select(row, 0).asString())); } } return Lists.newArrayList(regions); } catch(MissingDataException e) { throw new RuntimeException(e); } } private boolean containsRegion(SpreadsheetDataSource ds, Region region) { return regions(ds).contains(region); } private boolean containsChart(SpreadsheetDataSource ds, ChartType type) { return getColumn(ds, getMeanColumn()) != null && getColumn(ds, getSeColumn()) != null; } @Override protected ADSCDataset createDataset(Context ctx) { SpreadsheetDataSource ds = ctx.datasource(); ChartType type = ctx.type(); Region region = ctx.region(); if (((region == Region.GBR) || containsRegion(ds, region)) && containsChart(ds, type)) { Integer meanColumn = getColumn(ds, getMeanColumn()); Integer deviationColumn = getColumn(ds, getSeColumn()); if(meanColumn == null || deviationColumn == null) { throw new RuntimeException(String.format("data not found for ChartType %s", type.name())); } List<Region> regions; if(region == Region.GBR) { regions = regions(ds); } else { if(containsRegion(ds, region)) { regions = Collections.singletonList(region); } else { throw new RuntimeException(String.format("no entries for region %s", region.getProperName())); } } try { ADSCDataset d = new ADSCDataset(); for(Region r : regions) { for(int row = getRegionStart(ds, r); StringUtils.isNotBlank(ds.select(row, 0).asString());row++) { Double mean = ds.select(row, meanColumn).asDouble(); Double deviation = ds.select(row, deviationColumn).asDouble(); String series = ds.select(row, 0).asString(); String year = ds.select(row, 1).asInteger().toString(); d.add(mean, deviation, series, year); } } return d; } catch(MissingDataException e) { throw new RuntimeException(e); } } else { return null; } } private int getRegionStart(SpreadsheetDataSource ds, Region region) throws MissingDataException { for(int row = 0;!eof(ds, row);row++) { if(REGIONS.get(ds.select(row, 0).asString()) == region) { return row; } } throw new RuntimeException(String.format("region %s not found", region.getProperName())); } private Integer getColumn(SpreadsheetDataSource ds, String heading) { Integer cols = ds.getColumnCount(0); if(cols != null) { for(int c=0;c<cols;c++) { try { if(StringUtils.equalsIgnoreCase(ds.select(0, c).asString(), heading)) { return c; } } catch (MissingDataException e) {} } } return null; } @Override protected Drawable getDrawable(JFreeContext ctx) { return CoralCover.createChart((ADSCDataset)ctx.dataset(), ctx.type(), ctx.region(), new Dimension(750, 500)); } @Override protected String getCsv(final JFreeContext ctx) { final DefaultStatisticalCategoryDataset dataset = (DefaultStatisticalCategoryDataset)ctx.dataset(); return Csv.write(new CsvWriter() { @Override public void write(CsvListWriter csv) throws IOException { @SuppressWarnings("unchecked") List<String> columnKeys = dataset.getColumnKeys(); @SuppressWarnings("unchecked") List<String> rowKeys = dataset.getRowKeys(); final List<String> heading = ImmutableList .<String> builder() .add(String.format("%s %s", ctx.type().getLabel(), ctx.region())) .addAll(columnKeys) .build(); csv.write(heading); for (String row : rowKeys) { { List<String> line = Lists.newLinkedList(); line.add(row + " (Mean)"); for (String col : columnKeys) { line.add(formatNumber("%.3f", dataset.getMeanValue(row, col))); } csv.write(line); } { List<String> line = Lists.newLinkedList(); line.add(row + " (Std Dev)"); for (String col : columnKeys) { line.add(formatNumber("%.3f", dataset.getStdDevValue(row, col))); } csv.write(line); } } }}); } @Override public AttributeMap defaults(ChartType type) { return new AttributeMap.Builder(). put(Attribute.SERIES_COLOR, new Color(187, 34, 51)). build(); } }