package com.c2c.controller;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.Axis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.entity.StandardEntityCollection;
import org.jfree.chart.imagemap.StandardToolTipTagFragmentGenerator;
import org.jfree.chart.imagemap.StandardURLTagFragmentGenerator;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.labels.StandardPieToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.MultiplePiePlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.util.TableOrder;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.type.AttributeDescriptor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.c2c.data.DataQueryFeatureSource;
/**
*
* @author pmauduit, stolen from tbonfort's code from cdc_geodecisionnel project
*/
@Controller
@RequestMapping("/getchart")
public class GetChart extends AbstractQueryingController {
// TODO : use colors from a registered style ?
public static Color[] DEFAULT_COLORS = {
new Color(141,211,199),
new Color(255,255,179),
new Color(190,186,218),
new Color(251,128,114),
new Color(128,177,211),
new Color(253,180,98),
new Color(179,222,105),
new Color(252,205, 229)
};
private class ChartDataElem {
private String geographicRegion ;
private ArrayList<String> indicators;
private ArrayList<Double> datas ;
public ChartDataElem(String reg, Double value)
{
this.geographicRegion = reg;
datas = new ArrayList<Double>();
this.addDatas(value);
}
public ChartDataElem(String reg, String indicator, Double value)
{
this.geographicRegion = reg;
datas = new ArrayList<Double>();
indicators = new ArrayList<String>();
indicators.add(indicator);
this.addDatas(value);
}
public void addDatas(Double value)
{
datas.add(value);
}
public void addDatas(Double value, String indicator)
{
datas.add(value);
indicators.add(indicator);
}
public Double[] getDatas()
{
Double[] ret = new Double[datas.size()];
return datas.toArray(ret);
}
public String getGeographicRegion() { return geographicRegion; }
public String[] getIndicators() {return indicators.toArray(new String[indicators.size()]);}
}
private class ChartDatas
{
private ArrayList<ChartDataElem> elements ;
public ChartDatas() { elements = new ArrayList<ChartDataElem>() ; }
public void addElement(String key, String indicator, Double value)
{
for (Iterator<ChartDataElem> i = elements.iterator() ; i.hasNext() ; )
{
ChartDataElem cur = i.next();
if (cur.getGeographicRegion().equals(key))
{
cur.addDatas(value, indicator);
return;
}
}
/* else */
elements.add(new ChartDataElem(key, indicator, value));
}
public Double[][] getDatas()
{
ArrayList<Double[]> ret = new ArrayList<Double[]> ();
for (ChartDataElem cur : elements)
{
ret.add((Double[]) cur.getDatas());
}
Double[][] ret2 = new Double[elements.size()][];
return (Double[][]) ret.toArray(ret2);
}
public double[][] getdoubleDatas()
{
Double[][] tmp = getDatas();
double[][] ret = new double[tmp.length][];
for (int i = 0; i < tmp.length ; i++)
{
ret[i] = new double[tmp[i].length];
for (int j =0 ; j < tmp[i].length; j++)
{
ret[i][j] = tmp[i][j].doubleValue();
}
}
return ret;
}
public double[][] getSwappedDoubleDatas()
{
Double[][] tmp = getDatas();
double[][] ret = new double[tmp[0].length][];
for (int i = 0; i < tmp[0].length ; i++) // from 0 to b
{
ret[i] = new double[tmp.length]; // new array of dimension a
for (int j =0 ; j < tmp.length; j++) // from 0 to a
{
ret[i][j] = tmp[j][i].doubleValue();
}
}
return ret;
}
public String[] getRowKeys()
{
String[] ret = new String[elements.size()];
for (int i = 0 ; i < ret.length ; i++)
{
ret[i] = elements.get(i).getGeographicRegion();
}
return ret;
}
public String[] getColumnKeys()
{
return elements.get(0).getIndicators();
}
public String[] getTranslatedIndicators(DataQueryFeatureSource dqfs)
{
HashMap<String, String> lkp = Util.lookupIndicators(dqfs);
String[] ret = getColumnKeys();
for (int i = 0 ; i < ret.length ; i++)
{
ret[i] = lkp.get(ret[i]) == null ? ret[i] : lkp.get(ret[i]);
}
return ret;
}
}
protected JFreeChart createPieChart(HttpServletRequest request, HttpServletResponse response,
String queryId, CategoryDataset newSet, int width, int height, String format) throws Exception
{
final JFreeChart multiChart = ChartFactory.createMultiplePieChart(null, // chart title
newSet, // dataset
TableOrder.BY_ROW,
true, // include legend
true, // include tooltip
false);
final MultiplePiePlot mPlot = (MultiplePiePlot) multiChart.getPlot();
final JFreeChart subchart = mPlot.getPieChart();
final PiePlot p = (PiePlot) subchart.getPlot();
p.setSectionOutlinesVisible(false);
p.setLabelGenerator(null);
p.setShadowXOffset(0);
p.setShadowYOffset(0);
subchart.setBorderVisible(false);
multiChart.setBorderVisible(false);
p.setBackgroundPaint(new Color(0xd6e4f1));
subchart.getTitle().setFont(new Font("SansSerif", Font.PLAIN, 12));
List l = newSet.getColumnKeys();
for (int i = 0; i < Math.min(newSet.getColumnCount(), 8); i++) {
p.setSectionPaint((Comparable) l.get(i), GetChart.DEFAULT_COLORS[i]);
}
return multiChart;
}
protected JFreeChart createBarChart(HttpServletRequest request, HttpServletResponse response,
String queryId, CategoryDataset datas, int width, int height, String format, PlotOrientation orientation) throws Exception {
JFreeChart chart = ChartFactory.createBarChart(null, null, null, datas, orientation,
true, true, false);
CategoryPlot plot = (CategoryPlot) chart.getPlot();
java.util.List l = datas.getColumnKeys();
BarRenderer r = (BarRenderer) plot.getRenderer();
for (int i = 0; i < Math.min(datas.getColumnCount(), 8); i++) {
r.setSeriesPaint(i, GetChart.DEFAULT_COLORS[i]);
}
r.setBarPainter(new StandardBarPainter());
r.setDefaultShadowsVisible(false);
r.setDrawBarOutline(false);
r.setShadowVisible(false);
chart.setBorderVisible(false);
plot.setOutlineVisible(false);
plot.setBackgroundPaint(new Color(0xd6e4f1));
return chart;
}
@RequestMapping(method = RequestMethod.GET)
public void getchart(HttpServletRequest request,
HttpServletResponse response,
@RequestParam("QUERYID") String queryId,
@RequestParam("TYPE") String typeChart,
@RequestParam("INDICATORS") String indicators,
@RequestParam(value = "WIDTH", required = false) Integer width,
@RequestParam(value = "HEIGHT", required = false) Integer height,
@RequestParam(value = "IMAGEMAPID", required = false) String imagemapId,
@RequestParam(value = "FORMAT", required = false) String format) throws Exception
{
List<String> _indicators = Arrays.asList(indicators.split(","));
width = width == null ? 1200 : width;
height = height == null ? 800 : height;
boolean imagemap = (imagemapId != null);
DataQueryFeatureSource dqfs = getCache().getResults(queryId);
SimpleFeatureSource results = (SimpleFeatureSource) dqfs.getFeatureSource();
ChartDatas mpd = new ChartDatas();
SimpleFeatureIterator it = results.getFeatures().features();
try
{
while (it.hasNext()) // iteration on the features
{
SimpleFeature current = it.next();
String columnName = "";
// iteration on the attributes
for (AttributeDescriptor d : results.getSchema().getAttributeDescriptors())
{
String name = d.getName().toString();
if (current.getAttribute(name) instanceof String)
{
columnName += (current.getAttribute(name) + " ");
}
// name is an asked indicator
if (indicators.contains(name)) {
if (current.getAttribute(name) instanceof Double)
{
mpd.addElement(columnName, name, (Double)current.getAttribute(name));
}
else if (current.getAttribute(name) == null)
{
mpd.addElement(columnName, name, (Double) 0.0);
}
}
}
}
}
finally{
it.close();
}
final CategoryDataset newSet;
if (! typeChart.equalsIgnoreCase("piebyrow"))
{
newSet = DatasetUtilities.createCategoryDataset(mpd.getTranslatedIndicators(dqfs),
mpd.getRowKeys(),
mpd.getSwappedDoubleDatas());
}
else
{
newSet = DatasetUtilities.createCategoryDataset(mpd.getRowKeys(),
mpd.getTranslatedIndicators(dqfs),
mpd.getdoubleDatas());
}
JFreeChart chart = null;
if (typeChart.equalsIgnoreCase("pie") || typeChart.equalsIgnoreCase("piebyrow"))
{
chart = createPieChart(request, response, queryId, newSet, width, height, format);
}
else if (typeChart.equalsIgnoreCase("bar"))
{
chart = createBarChart(request, response, queryId, newSet, width, height, format, PlotOrientation.VERTICAL);
}
else if (typeChart.equalsIgnoreCase("horizontalbar"))
{
chart = createBarChart(request, response, queryId, newSet, width, height, format, PlotOrientation.HORIZONTAL);
}
else
{
throw new Exception(typeChart + " is not a legal value");
}
// change number format
NumberFormat df = new DecimalFormat("##0.0");
NumberFormat pdf = new DecimalFormat("##0.0%");
Plot plot = chart.getPlot();
if (plot instanceof CategoryPlot) {
Axis axis = ((CategoryPlot) plot).getRangeAxis();
if (axis instanceof NumberAxis) {
((NumberAxis) axis).setNumberFormatOverride(df);
}
axis = ((CategoryPlot) plot).getDomainAxis();
if (axis instanceof NumberAxis) {
((NumberAxis) axis).setNumberFormatOverride(df);
}
CategoryItemRenderer renderer = ((CategoryPlot) plot).getRenderer();
StandardCategoryToolTipGenerator generator =
new StandardCategoryToolTipGenerator(StandardCategoryToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT_STRING, df);
renderer.setBaseToolTipGenerator(generator);
} else {
PiePlot pplot = (PiePlot)((MultiplePiePlot) plot).getPieChart().getPlot();
StandardPieToolTipGenerator generator =
new StandardPieToolTipGenerator(StandardPieToolTipGenerator.DEFAULT_TOOLTIP_FORMAT, df, pdf);
pplot.setToolTipGenerator(generator);
}
if (imagemap)
{
// create RenderingInfo object
StandardEntityCollection col = new StandardEntityCollection();
ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());
BufferedImage chartImage = chart.createBufferedImage(width, height, info);
BufferedImage image = new BufferedImage(width,
height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
PrintWriter pw = new PrintWriter(response.getOutputStream());
chart.draw(graphics, new Rectangle(0,0,width,height), info);
ChartUtilities.writeImageMap(pw, imagemapId, info, new StandardToolTipTagFragmentGenerator(),
new StandardURLTagFragmentGenerator());
pw.close();
}
else
{
try
{
if (format == null)
{
response.setContentType("image/png");
}
else
{
response.setContentType(format);
}
ChartUtilities.writeChartAsPNG(response.getOutputStream(), chart, width,height);
} catch (Exception e)
{
System.out.println("Problem occurred while creating chart.");
e.printStackTrace();
}finally {
response.getOutputStream().close();
}
}
}
}