package charts.builder.spreadsheet; import static com.google.common.collect.Lists.newLinkedList; import java.awt.Color; import java.awt.Dimension; import java.io.IOException; import java.util.List; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.jfree.data.category.CategoryDataset; import org.supercsv.io.CsvListWriter; import play.Logger; import charts.ChartType; import charts.Drawable; import charts.Region; import charts.builder.DataSource.MissingDataException; import charts.builder.Value; import charts.builder.csv.Csv; import charts.builder.csv.CsvWriter; import charts.graphics.Groundcover; import charts.jfree.ADCDataset; 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.ImmutableSet; public abstract class AbstractGroundCoverBuilder extends JFreeBuilder { protected static final String GC_SHEETNAME = "avgCover_ordered"; protected static final String GCB50_SHEETNAME = "areaBelow50_ordered"; private static final int MAX_ROW = 27; private static final ImmutableMap<Region, Set<String>> CATCHMENTS = new ImmutableMap.Builder<Region, Set<String>>() .put(Region.WET_TROPICS, ImmutableSet.of("Herbert")) .put(Region.BURDEKIN, ImmutableSet.of("Black", "Burdekin", "Don", "Haughton", "Ross")) .put(Region.MACKAY_WHITSUNDAY, ImmutableSet.of("Proserpine", "Pioneer", "Plane", "Oconnell", "O'Connell")) .put(Region.FITZROY, ImmutableSet.of("Boyne", "Calliope", "Fitzroy", "Shoalwater", "Styx", "Waterpark")) .put(Region.BURNETT_MARY, ImmutableSet.of("Baffle", "Burnett", "Burrum", "Kolan", "Mary")) .build(); private static final SubstitutionKey FIRST_YEAR = new SubstitutionKey("firstYear", "first year of groundcover data in the spreadsheet", new SubstitutionKey.Val() { @Override public String value(Context ctx) { try { return getFirstYear(ctx.datasource()); } catch (MissingDataException e) { throw new RuntimeException(e); } } }); private static final SubstitutionKey LAST_YEAR = new SubstitutionKey("lastYear", "last year of groundcover data in the spreadsheet", new SubstitutionKey.Val() { @Override public String value(Context ctx) { try { return getLastYear(ctx.datasource()); } catch (MissingDataException e) { throw new RuntimeException(e); } } }); private static final SubstitutionKey GC_REGION = new SubstitutionKey("gcRegion", "groundcover region copied from spreadsheet A2-A28", new SubstitutionKey.Val() { @Override public String value(Context ctx) { Integer row = findRow(ctx, ctx.region()); if(row != null) { try { return ctx.datasource().select(row, 0).asString(); } catch (MissingDataException e) {} } Logger.debug("gcRegion not found for region "+ctx.region().name()); return ctx.region().getProperName(); } }); public AbstractGroundCoverBuilder(ChartType type) { super(type); } @Override public boolean canHandle(SpreadsheetDataSource datasource) { try { if(datasource.hasSheet(GC_SHEETNAME) || datasource.hasSheet(GCB50_SHEETNAME)) { List<Value> row0 = datasource.selectRow(0); List<Value> col1 = datasource.selectColumn(1); return SpreadsheetDataSource.containsString(row0, "Region") && SpreadsheetDataSource.containsString(row0, "Type") && SpreadsheetDataSource.containsString(row0, "AllYearMean") && SpreadsheetDataSource.containsString(col1, "Catchment") && SpreadsheetDataSource.containsString(col1, "Region"); } } catch (MissingDataException e) {} return false; } private static Integer findRow(Context ctx, Region region) { for(int row=1;row<=MAX_ROW;row++) { try { String type = ctx.datasource().select(row, 1).asString(); if(StringUtils.isBlank(type) || StringUtils.equalsIgnoreCase("Region", type)) { String r = ctx.datasource().select(row, 0).asString(); if(StringUtils.containsIgnoreCase(r, StringUtils.split(region.getProperName())[0])) { return row; } } } catch(MissingDataException e) {} } return null; } private boolean contains(Region region, String catchment) { if(CATCHMENTS.get(region) != null) { for(String s : CATCHMENTS.get(region)) { if(StringUtils.containsIgnoreCase(catchment, s)) { return true; } } } return false; } private void addRow(Context ctx, ADCDataset dataset, Integer row) throws MissingDataException { if(row != null) { SpreadsheetDataSource ds = ctx.datasource(); String rowKey = ds.select(row, 0).asString(); for(int column = 2;column<=getLastColumn(ds);column++) { String columnKey = ds.select(0, column).asInteger() + ""; if(ds.select(row, column).getValue() == null) { dataset.addValue(null, rowKey, columnKey); } else { dataset.addValue(ds.select(row, column).asDouble(), rowKey, columnKey); } } } } @Override protected ADCDataset createDataset(Context ctx) { try { SpreadsheetDataSource ds = ctx.datasource(); ADCDataset dataset = null; if(ctx.region() == Region.GBR) { dataset = new ADCDataset(); for(Region region : Region.values()) { addRow(ctx, dataset, findRow(ctx, region)); } } else { Set<String> catchments = CATCHMENTS.get(ctx.region()); if(catchments != null) { dataset = new ADCDataset(); if(catchments.size() > 1) { addRow(ctx, dataset, findRow(ctx, ctx.region())); } for(int row=1;row<=MAX_ROW;row++) { String type = ds.select(row, 1).asString(); if(StringUtils.equalsIgnoreCase("catchment", type)) { String catchment = ds.select(row, 0).asString(); if(contains(ctx.region(), catchment)) { addRow(ctx, dataset, row); } } } } } return dataset; } catch(MissingDataException e) { throw new RuntimeException(e); } } @Override public AttributeMap defaults(ChartType type) { return new AttributeMap.Builder(). put(Attribute.X_AXIS_LABEL, "Year"). put(Attribute.SERIES_COLORS, new Color[] { new Color(29,107,171), new Color(134,177,56), new Color(87,88,71), new Color(150,116,52), new Color(103,42,4), new Color(208,162,33)}). build(); } private static String getFirstYear(SpreadsheetDataSource ds) throws MissingDataException { return ds.select(0, 2).asInteger() + ""; } private static String getLastYear(SpreadsheetDataSource ds) throws MissingDataException { return ds.select(0, getLastColumn(ds)).asInteger() + ""; } private static int getLastColumn(SpreadsheetDataSource ds) throws MissingDataException { for(int col=ds.getColumns(0)-1;col > 0;col--) { if(ds.select(0, col).asInteger() != null) { return col; } } return 0; } @Override protected Drawable getDrawable(JFreeContext ctx) { return Groundcover.createChart( (ADCDataset)ctx.dataset(), ctx.type(), new Dimension(750,500)); } @Override protected String getCsv(final JFreeContext ctx) { final CategoryDataset dataset = (CategoryDataset)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.region(), ctx.type())) .addAll(columnKeys) .build(); csv.write(heading); for (String row : rowKeys) { List<String> line = newLinkedList(); line.add(row); for (String col : columnKeys) { line.add(formatNumber("%.1f", dataset.getValue(row, col))); } csv.write(line); } }}); } @Override public Set<SubstitutionKey> substitutionKeys() { return ImmutableSet.<SubstitutionKey>builder(). addAll(super.substitutionKeys()).add(FIRST_YEAR).add(LAST_YEAR).add(GC_REGION).build(); } }