package charts.builder.spreadsheet;
import static charts.ChartType.MARINE_CT;
import static charts.ChartType.MARINE_ST;
import static charts.ChartType.MARINE_WQT;
import static org.apache.commons.lang.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang.StringUtils.isBlank;
import java.awt.Color;
import java.awt.Dimension;
import java.io.IOException;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.jfree.data.category.CategoryDataset;
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.Colors;
import charts.graphics.MarineTrends;
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.Lists;
public class MarineTrendsBuilder extends JFreeBuilder {
private static final String WATER_QUALITY = "Water Quality";
private static final String SEAGRASS = "Seagrass";
private static final String CORAL = "Coral";
private static final ImmutableMap<ChartType, Integer> ROWS = ImmutableMap.of(
MARINE_WQT, 2, MARINE_ST, 14, MARINE_CT, 26);
private static enum Indicator {
OWQ_SCORE("Overall water quality score", new String[]
{"Water quality index", "WQI"}, MARINE_WQT),
CHLOROPHYLL_A("Chlorophyll a", "Chlorophyll a", MARINE_WQT),
TOTAL_SUSPENDED_SOLIDS("Total suspended solids", new String[]
{"Total suspended solids", "TSS"}, MARINE_WQT),
OS_SCORE("Overall seagrass score", "Seagrass Index", MARINE_ST),
REPRODUCTION("Reproduction", "Reproductive Effort", MARINE_ST),
ABUNDANCE("Abundance", "Abundance", MARINE_ST),
NUTRIENT_STATUS("Nutrient status", "C:N Ratio", MARINE_ST),
OC_SCORE("Overall coral score", "Coral Index", MARINE_CT),
COVER("Cover", "Cover", MARINE_CT),
CHANGE("Change", "Change", MARINE_CT),
MACROALGAE("Macroalgae", "Algal Cover", MARINE_CT),
JUVENILE("Juveniles", new String[] {"Juvenile", "Juvenile density"}, MARINE_CT),
COMPOSITION("Composition", "Community composition", MARINE_CT),
;
private String label;
private String[] headers;
private ChartType type;
private Indicator(String label, String heading, ChartType type) {
this(label, new String[] {heading}, type);
}
private Indicator(String label, String[] headers, ChartType type) {
this.label = label;
this.headers = headers;
this.type = type;
}
public String getLabel() {
return label;
}
public ChartType getChartType() {
return type;
}
public boolean match(String header) {
for(String h : headers) {
if(equalsIgnoreCase(h, header)) {
return true;
}
}
return equalsIgnoreCase(header, label);
}
public static List<Indicator> forType(ChartType type) {
List<Indicator> l = Lists.newArrayList();
for(Indicator i : Indicator.values()) {
if(i.getChartType() == type) {
l.add(i);
}
}
return l;
}
}
public MarineTrendsBuilder() {
super(ImmutableList.of(MARINE_CT, MARINE_ST, MARINE_WQT));
}
@Override
public boolean canHandle(SpreadsheetDataSource datasource) {
try {
return equalsIgnoreCase(datasource.select("B2").asString(), WATER_QUALITY) &&
equalsIgnoreCase(datasource.select("B14").asString(), SEAGRASS) &&
equalsIgnoreCase(datasource.select("B26").asString(), CORAL);
} catch(MissingDataException e) {}
return false;
}
private int getRow(SpreadsheetDataSource ds, ChartType type) {
return ROWS.get(type);
}
private int getRow(SpreadsheetDataSource ds, ChartType type, Region region) {
int offset = 2;
if(region == Region.GBR) {
offset += 6;
} else {
offset += region.ordinal() - 1;
}
return getRow(ds, type) + offset;
}
private int getColumnStart(SpreadsheetDataSource ds, Indicator indicator) {
int row = getRow(ds, indicator.getChartType());
for(int col = 1;col < 100;col++) {
try {
if(indicator.match(ds.select(row, col).asString())) {
return col;
}
} catch(MissingDataException e) {}
}
throw new RuntimeException(String.format(
"column start for indicator '%s' not found", indicator.getLabel()));
}
private boolean hasIndicator(SpreadsheetDataSource ds, Indicator indicator) {
try {
getColumnStart(ds, indicator);
return true;
} catch(RuntimeException e) {
return false;
}
}
private int getColumnEnd(SpreadsheetDataSource ds, Indicator indicator) {
int row = getRow(ds, indicator.getChartType()) + 1;
for(int col = getColumnStart(ds, indicator) + 1; true; col++) {
try {
String s = ds.select(row, col).asString();
if(isBlank(s)) {
return col - 1;
}
String s2 = ds.select(row-1, col).asString();
if(!isBlank(s2)) {
return col - 1;
}
} catch(MissingDataException e) {}
}
}
private List<String> getCategories(SpreadsheetDataSource ds, Indicator indicator) throws MissingDataException {
List<String> l = Lists.newArrayList();
int row = getRow(ds, indicator.getChartType()) + 1;
for(int col = getColumnStart(ds, indicator);col <= getColumnEnd(ds, indicator);col++) {
l.add(StringUtils.substringBefore(ds.select(row, col).asString(), "."));
}
return l;
}
@Override
protected ADCDataset createDataset(Context ctx) {
if (ctx.type() == MARINE_CT
&& (ctx.region() == Region.CAPE_YORK || ctx.region() == Region.BURNETT_MARY)) {
return null;
}
SpreadsheetDataSource ds = ctx.datasource();
try {
ADCDataset dataset = new ADCDataset();
int row = getRow(ds, ctx.type(), ctx.region());
for(Indicator i : Indicator.forType(ctx.type())) {
if(hasIndicator(ds, i)) {
int col = getColumnStart(ds, i);
for(String s : getCategories(ds, i)) {
try {
double d = ds.select(row, col).asDouble();
dataset.addValue(d, i.getLabel(), s);
} catch(Exception e) {
dataset.addValue(null, i.getLabel(), s);
}
col++;
}
}
}
return dataset;
} catch(MissingDataException e) {
throw new RuntimeException(e);
}
}
@Override
protected Drawable getDrawable(JFreeContext ctx) {
return MarineTrends.createChart((ADCDataset)ctx.dataset(), new Dimension(750, 500), ctx);
}
@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().getProperName(), ctx.type().getLabel()))
.addAll(columnKeys)
.build();
csv.write(heading);
for (String row : rowKeys) {
List<String> line = Lists.newArrayList();
line.add(row);
for (String col : columnKeys) {
final Number n = dataset.getValue(row, col);
line.add(n == null ? "" : String.format("%.1f", n.doubleValue()));
}
csv.write(line);
}
}});
}
@Override
public AttributeMap defaults(ChartType type) {
return new AttributeMap.Builder().
put(Attribute.TITLE, type.equals(ChartType.MARINE_WQT)?"${region} remote sensed water"
+ " quality score":"${region} inshore ${type}").
put(Attribute.Y_AXIS_LABEL, "Score").
put(Attribute.X_AXIS_LABEL, "Reporting Year").
put(Attribute.SERIES_COLORS, new Color[] {Colors.BLUE, Colors.DARK_RED,
Colors.RED, Colors.VIOLET, Colors.GREEN}).
build();
}
}