package org.activityinfo.ui.client.component.report.view;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import com.extjs.gxt.charts.client.Chart;
import com.extjs.gxt.charts.client.model.ChartModel;
import com.extjs.gxt.charts.client.model.axis.XAxis;
import com.extjs.gxt.charts.client.model.axis.YAxis;
import com.extjs.gxt.charts.client.model.charts.BarChart.Bar;
import com.extjs.gxt.charts.client.model.charts.FilledBarChart;
import com.extjs.gxt.charts.client.model.charts.LineChart;
import com.extjs.gxt.charts.client.model.charts.PieChart;
import com.extjs.gxt.charts.client.model.charts.PieChart.Slice;
import com.extjs.gxt.charts.client.model.charts.dots.BaseDot;
import com.extjs.gxt.charts.client.model.charts.dots.Dot;
import com.extjs.gxt.charts.client.model.charts.dots.SolidDot;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.layout.MarginData;
import com.google.common.collect.Lists;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Element;
import org.activityinfo.i18n.shared.I18N;
import org.activityinfo.legacy.shared.reports.Theme;
import org.activityinfo.legacy.shared.reports.content.PivotChartContent;
import org.activityinfo.legacy.shared.reports.content.PivotTableData;
import org.activityinfo.legacy.shared.reports.model.PivotChartReportElement;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* A view of the PivotChartElement using Open Flash Charts
*
* @author Alex Bertram (akbertram@gmail.com)
*/
public class ChartOFCView extends ContentPanel implements ChartView {
private static final String TRANSPARENT_BG = "-1";
private Chart chart;
private ChartModel chartModel;
private PivotGridPanel gridPanel;
public ChartOFCView() {
setHeaderVisible(false);
setLayout(new FitLayout());
}
@Override
protected void onRender(Element parent, int pos) {
super.onRender(parent, pos);
if (chartModel != null) {
createChart();
}
}
private void createChart() {
chart = new Chart(GWT.getModuleBaseURL() + "/gxt231/chart/open-flash-chart.swf");
chart.setBorders(false);
chart.setChartModel(chartModel);
if (!chart.isLoaded()) {
this.el().mask(I18N.CONSTANTS.loading());
chart.addListener(Events.Ready, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
chart.removeListener(Events.Ready, this);
ChartOFCView.this.el().unmask();
ChartOFCView.this.chart.repaint();
}
});
}
add(chart, new MarginData(20));
}
private void setModel(ChartModel chartModel) {
this.chartModel = chartModel;
if (this.isRendered()) {
if (chart == null) {
createChart();
layout();
}
chart.setChartModel(chartModel);
}
}
/**
* Updates the view to the given PivotChartContent
*
* @param element
*/
@Override
public void show(PivotChartReportElement element) {
PivotChartContent content = element.getContent();
PivotTableData table = element.getContent().getData();
if (gridPanel != null) {
gridPanel.show(element);
}
List<PivotTableData.Axis> categories = table.getRootRow().getLeaves();
List<PivotTableData.Axis> series = table.getRootColumn().getLeaves();
ChartModel cm = new ChartModel();
cm.setBackgroundColour(TRANSPARENT_BG);
cm.setNumDecimals(0);
cm.setDecimalSeparatorComma(true);
cm.setXAxis(createXAxis(categories));
cm.setYAxis(createYAxis(content, table));
switch (element.getType()) {
case Bar:
case StackedBar:
case ClusteredBar:
addBarSeries(cm, categories, series);
break;
case Pie:
addPieChart(cm, categories, series);
break;
case Line:
addLineSeries(cm, categories, series);
break;
}
setModel(cm);
}
private YAxis createYAxis(PivotChartContent content, PivotTableData table) {
YAxis ya = new YAxis();
double maxValue = table.getRootRow().getMaxValue();
ya.setRange(content.getYMin(), maxValue);
ya.setSteps(content.getYStep());
ya.setGridColour("eeffee");
ya.setColour("009900");
return ya;
}
private XAxis createXAxis(List<PivotTableData.Axis> categories) {
XAxis xa = new XAxis();
List<String> xLabels = PivotTableData.flattenLabels(categories);
xa.setLabels(xLabels);
xa.getLabels().setColour("009900");
xa.setGridColour("eeffee");
xa.setColour("009900");
if (xLabels.size() > 5) {
xa.getLabels().setRotationAngle(45);
}
return xa;
}
private void addBarSeries(ChartModel cm, List<PivotTableData.Axis> categories, List<PivotTableData.Axis> series) {
int index = 0;
for (PivotTableData.Axis s : series) {
String color = Theme.getAccent(index++);
FilledBarChart bchart = new FilledBarChart(color, color);
bchart.setTooltip("#x_label#<br>#val#");
for (PivotTableData.Axis category : categories) {
PivotTableData.Cell cell = category.getCell(s);
double value = cell == null ? 0 : cell.getValue();
Bar bar = new Bar(value);
bar.setValue(value);
bar.setTooltip(formatTooltip(s, category, value));
bar.setValue(value);
bchart.addBars(bar);
}
cm.addChartConfig(bchart);
}
}
private void addLineSeries(ChartModel cm, List<PivotTableData.Axis> categories, List<PivotTableData.Axis> series) {
int index = 0;
for (PivotTableData.Axis s : series) {
String color = Theme.getAccent(index++);
LineChart lineChart = new LineChart();
lineChart.setTooltip("#x_label#<br>#val#");
lineChart.setColour(color);
lineChart.setDotStyle(new SolidDot());
for (PivotTableData.Axis category : categories) {
PivotTableData.Cell cell = category.getCell(s);
double value = cell == null ? 0 : cell.getValue();
BaseDot d = new Dot(5);
d.setSize(5);
d.setColour(color);
d.setTooltip(formatTooltip(s, category, value));
d.setValue(value);
lineChart.addDots(d);
}
cm.addChartConfig(lineChart);
}
}
private String formatTooltip(PivotTableData.Axis s, PivotTableData.Axis category, double value) {
StringBuilder sb = new StringBuilder();
sb.append(s.flattenLabel());
sb.append("<br>");
sb.append(category.flattenLabel());
sb.append(": ");
sb.append(value);
return sb.toString();
}
private void addPieChart(ChartModel cm, List<PivotTableData.Axis> categories, List<PivotTableData.Axis> series) {
PieChart pieChart = new PieChart();
List<PieChart.Slice> slices = Lists.newArrayList();
List<String> colors = Lists.newArrayList();
int colorIndex = 0;
for (PivotTableData.Axis category : categories) {
PivotTableData.Cell cell = category.getCell(series.get(0));
if (cell != null) {
PieChart.Slice slice = new PieChart.Slice(cell.getValue(), category.flattenLabel());
slices.add(slice);
colors.add(Theme.getAccent(colorIndex++));
}
}
Collections.sort(slices, new Comparator<PieChart.Slice>() {
@Override
public int compare(Slice o1, Slice o2) {
double d1 = o1.getValue().doubleValue();
double d2 = o2.getValue().doubleValue();
return Double.compare(d2, d1);
}
});
pieChart.addSlices(slices);
pieChart.setColours(colors);
cm.addChartConfig(pieChart);
}
@Override
public Component asComponent() {
return this;
}
public void bindTable(PivotGridPanel gridPanel) {
this.gridPanel = gridPanel;
}
}