ingo@152: /*
ingo@152: * Copyright (c) 2011 by Intevation GmbH
ingo@152: *
ingo@152: * This program is free software under the LGPL (>=v2.1)
ingo@152: * Read the file LGPL.txt coming with the software for details
ingo@152: * or visit http://www.gnu.org/licenses/ if it does not exist.
ingo@152: */
ingo@152: package de.intevation.flys.exports;
ingo@152:
ingo@152: import com.lowagie.text.Document;
ingo@152: import com.lowagie.text.DocumentException;
ingo@152: import com.lowagie.text.PageSize;
ingo@152: import com.lowagie.text.Rectangle;
ingo@152:
ingo@152: import com.lowagie.text.pdf.PdfContentByte;
ingo@152: import com.lowagie.text.pdf.PdfTemplate;
ingo@152: import com.lowagie.text.pdf.PdfWriter;
ingo@152:
ingo@152: import java.awt.Graphics2D;
ingo@152: import java.awt.Transparency;
ingo@152:
ingo@152: import java.awt.geom.Rectangle2D.Double;
ingo@152:
ingo@152: import java.awt.geom.Rectangle2D;
ingo@152:
ingo@152: import java.io.IOException;
ingo@152: import java.io.OutputStream;
ingo@152: import java.io.OutputStreamWriter;
ingo@152: import java.io.UnsupportedEncodingException;
felix@1036: import org.jfree.chart.ChartRenderingInfo;
ingo@152:
ingo@152: import javax.imageio.ImageIO;
ingo@152:
raimund@2231: import au.com.bytecode.opencsv.CSVWriter;
raimund@2231:
ingo@152: import org.apache.batik.svggen.SVGGraphics2D;
ingo@152: import org.apache.batik.svggen.SVGGraphics2DIOException;
ingo@152:
ingo@152: import org.apache.log4j.Logger;
ingo@152:
ingo@152: import org.jfree.chart.JFreeChart;
raimund@2231: import org.jfree.chart.plot.XYPlot;
raimund@2231: import org.jfree.data.xy.XYDataset;
ingo@152:
ingo@152: import de.intevation.artifacts.CallContext;
ingo@152:
ingo@152: import de.intevation.artifacts.common.utils.XMLUtils;
ingo@152:
ingo@152:
ingo@152: /**
ingo@152: * This class is a helper class which supports some methods to export charts
ingo@152: * into specific formats.
ingo@152: *
ingo@152: * @author Ingo Weinzierl
ingo@152: */
ingo@152: public class ChartExportHelper {
ingo@152:
ingo@1735: public static final String FORMAT_PNG = "png";
ingo@1735:
ingo@1735: public static final String FORMAT_PDF = "pdf";
ingo@1735:
ingo@1735: public static final String FORMAT_SVG = "svg";
ingo@1735:
raimund@2231: public static final String FORMAT_CSV = "csv";
ingo@1735:
ingo@152: /**
ingo@152: * Constant field to define A4 as default page size.
ingo@152: */
ingo@1735: public static final String DEFAULT_PAGE_SIZE = "A4";
ingo@152:
ingo@152: /**
ingo@152: * Constant field to define UTF-8 as default encoding.
ingo@152: */
ingo@1735: public static final String DEFAULT_ENCODING = "UTF-8";
ingo@152:
raimund@2231: /** The default separator for the CSV export. */
raimund@2231: public static final char DEFAULT_CSV_SEPARATOR = ',';
raimund@2231:
raimund@2231:
ingo@152: /**
ingo@152: * Logger used for logging with log4j.
ingo@152: */
ingo@152: private static Logger log = Logger.getLogger(ChartExportHelper.class);
ingo@152:
ingo@152:
ingo@152: /**
ingo@152: * A method to export a JFreeChart
as image to an
ingo@152: * OutputStream
with a given format, width and height.
ingo@152: *
ingo@152: * @param out OutputStream
ingo@152: * @param chart JFreeChart object to be exported.
ingo@152: * @param format Format (e.g. png, gif, jpg)
ingo@152: * @param width Width, the image used to be
ingo@152: * @param height Height, the image used to be
ingo@152: *
ingo@152: * @throws IOException if writing image to OutputStream failed.
ingo@152: */
ingo@152: public static void exportImage(
ingo@152: OutputStream out,
ingo@1735: JFreeChart chart,
ingo@1735: CallContext cc
ingo@152: )
ingo@152: throws IOException
ingo@152: {
ingo@152: log.info("export chart as png");
ingo@152:
felix@1036: ChartRenderingInfo info = new ChartRenderingInfo();
felix@1036:
ingo@1735: String format = (String) cc.getContextValue("chart.image.format");
ingo@1735:
ingo@1735: int[] size = getSize(cc);
ingo@1735:
ingo@152: ImageIO.write(
ingo@152: chart.createBufferedImage(
ingo@1735: size[0], size[1], Transparency.BITMASK, info
ingo@152: ),
ingo@152: format,
ingo@152: out
ingo@152: );
ingo@152: }
ingo@152:
ingo@152:
ingo@152: /**
ingo@152: * A method to export a JFreeChart
as SVG to an
ingo@152: * OutputStream
.
ingo@152: *
ingo@152: * @param out OutputStream
ingo@152: * @param chart JFreeChart to be exported
ingo@1735: * @param context The CallContext object that contains extra chart
ingo@1735: * parameters.
ingo@152: */
ingo@152: public static void exportSVG(
ingo@152: OutputStream out,
ingo@152: JFreeChart chart,
ingo@1735: CallContext context
ingo@152: ) {
ingo@1735: String encoding = (String) context.getContextValue("chart.encoding");
ingo@1735:
ingo@152: log.info("export chart as svg");
ingo@152:
ingo@152: if (encoding == null)
ingo@152: encoding = DEFAULT_ENCODING;
ingo@152:
ingo@152: org.w3c.dom.Document document = XMLUtils.newDocument();
ingo@152: SVGGraphics2D graphics = new SVGGraphics2D(document);
ingo@152:
ingo@1735: int[] size = getSize(context);
ingo@1735:
felix@2160: ChartRenderingInfo info = new ChartRenderingInfo();
felix@2160:
felix@2160: chart.draw(graphics, new Rectangle2D.Double(0.0D, 0.0D,size[0],size[1]), info);
ingo@152:
ingo@152: try {
ingo@152: graphics.stream(new OutputStreamWriter(out, encoding));
ingo@152: }
ingo@152: catch (SVGGraphics2DIOException svge) {
ingo@152: log.error("Error while writing svg export to output stream.", svge);
ingo@152: }
ingo@152: catch (UnsupportedEncodingException uee) {
ingo@152: log.error("Unsupported encoding: " + encoding, uee);
ingo@152: }
ingo@152: }
ingo@152:
ingo@152:
ingo@152: /**
ingo@152: * A method to export a JFreeChart
as PDF to an
ingo@152: * OutputStream
.
ingo@152: *
ingo@152: * @param out OutputStream
ingo@152: * @param chart JFreeChart
ingo@152: * @param pageFormat String to specify a page format, {@link
ingo@152: * #DEFAULT_PAGE_SIZE} is used if no pageFormat is given
ingo@152: * @param landscape If this is true, the pdf is delivered in landscape
ingo@152: * format
ingo@152: * @param marginLeft Space to left border
ingo@152: * @param marginRight Space to right border
ingo@152: * @param marginTop Space to upper border
ingo@152: * @param marginBottom Space to lower border
ingo@152: */
ingo@152: public static void exportPDF(
ingo@152: OutputStream out,
ingo@152: JFreeChart chart,
ingo@1735: CallContext cc
ingo@152: ) {
ingo@152: log.info("export chart as pdf.");
ingo@152:
ingo@1735: String pageFormat = (String) cc.getContextValue("chart.page.format");
ingo@1735:
ingo@152: if (pageFormat == null)
ingo@152: pageFormat = DEFAULT_PAGE_SIZE;
ingo@152:
ingo@152: // max size of the chart
ingo@152: Rectangle page = PageSize.getRectangle(pageFormat);
ingo@152: float pageWidth = page.getWidth();
ingo@152: float pageHeight = page.getHeight();
ingo@152:
ingo@152: // the chart width
ingo@1735: int[] size = getSize(cc);
ingo@152:
ingo@1735: boolean landscape = size[0] > size[1];
ingo@152:
ingo@152: float width = 0;
ingo@152: float height = 0;
ingo@152: if (landscape) {
ingo@152: width = pageHeight;
ingo@152: height = pageWidth;
ingo@152: }
ingo@152: else {
ingo@152: width = pageWidth;
ingo@152: height = pageHeight;
ingo@152: }
ingo@152:
ingo@1735: float marginLeft = (Float) cc.getContextValue(
ingo@1735: "chart.marginLeft");
ingo@1735:
ingo@1735: float marginRight = (Float) cc.getContextValue(
ingo@1735: "chart.marginRight");
ingo@1735:
ingo@1735: float marginTop = (Float) cc.getContextValue(
ingo@1735: "chart.marginTop");
ingo@1735:
ingo@1735: float marginBottom = (Float) cc.getContextValue(
ingo@1735: "chart.marginBottom");
ingo@1735:
ingo@152: float spaceX = width - marginLeft - marginRight;
ingo@1735: if (size[0] > spaceX) {
ingo@152: log.warn("Width of the chart is too big for pdf -> resize it now.");
ingo@1735: double ratio = ((double)spaceX) / size[0];
ingo@1735: size[0] *= ratio;
ingo@1735: size[1] *= ratio;
ingo@1735: log.debug("Resized chart to " + size[0] + "x" + size[1]);
ingo@152: }
ingo@152:
ingo@152: float spaceY = height - marginTop - marginBottom;
ingo@1735: if (size[1] > spaceY) {
ingo@152: log.warn("Height of the chart is too big for pdf -> resize it now.");
ingo@1735: double ratio = ((double)spaceY) / size[1];
ingo@1735: size[0] *= ratio;
ingo@1735: size[1] *= ratio;
ingo@1735: log.debug("Resized chart to " + size[0] + "x" + size[1]);
ingo@152: }
ingo@152:
ingo@152: Document document = null;
ingo@152: if (landscape) {
ingo@152: document = new Document(page.rotate());
ingo@152: log.debug("Create landscape pdf.");
ingo@152: }
ingo@152: else
ingo@152: document = new Document(page);
ingo@152:
ingo@152: try {
ingo@152: PdfWriter writer = PdfWriter.getInstance(document, out);
ingo@152:
ingo@152: document.addSubject(chart.getTitle().getText());
ingo@152: document.addCreationDate();
ingo@152: document.open();
ingo@152:
ingo@152: PdfContentByte content = writer.getDirectContent();
ingo@152:
ingo@152: PdfTemplate template = content.createTemplate(width, height);
ingo@152: Graphics2D graphics = template.createGraphics(width, height);
ingo@152:
ingo@152: double[] origin = getCenteredAnchor(
ingo@152: marginLeft, marginRight, marginBottom, marginTop,
ingo@152: width, height,
ingo@1735: size[0], size[1]);
ingo@152:
ingo@152: Rectangle2D area = new Rectangle2D.Double(
ingo@1735: origin[0], origin[1], size[0], size[1]);
ingo@152:
felix@2160: ChartRenderingInfo info = new ChartRenderingInfo();
felix@2160:
felix@2160: chart.draw(graphics, area, info);
ingo@152: graphics.dispose();
ingo@152: content.addTemplate(template, 0f, 0f);
ingo@152: }
ingo@152: catch (DocumentException de) {
ingo@152: log.error("Error while exporting chart to pdf.", de);
ingo@152: }
ingo@152: finally {
ingo@152: document.close();
ingo@152: }
ingo@152: }
ingo@152:
ingo@152:
raimund@2231: /**
raimund@2231: * A method to export a CSV file to an
raimund@2231: * OutputStream
.
raimund@2231: *
raimund@2231: * @param out OutputStream
raimund@2231: * @param chart JFreeChart containing the data.
raimund@2231: * @param context The CallContext object that contains extra parameters.
raimund@2231: */
raimund@2231: public static void exportCSV(
raimund@2231: OutputStream out,
raimund@2231: JFreeChart chart,
raimund@2231: CallContext context)
raimund@2231: {
raimund@2231: log.debug("export chart as CSV");
raimund@2231: CSVWriter writer = null;
raimund@2231: try {
raimund@2231: writer = new CSVWriter(
raimund@2231: new OutputStreamWriter(
raimund@2231: out,
raimund@2231: DEFAULT_ENCODING),
raimund@2231: DEFAULT_CSV_SEPARATOR);
raimund@2231: }
raimund@2231: catch(UnsupportedEncodingException uee) {
raimund@2231: log.warn("Wrong encoding for CSV export.");
raimund@2231: return;
raimund@2231: }
raimund@2231: XYPlot plot = chart.getXYPlot();
raimund@2231: int count = plot.getDatasetCount();
raimund@2231: for (int i = 0; i < count; i++) {
raimund@2231: XYDataset data = plot.getDataset(i);
raimund@2231: int scount = data.getSeriesCount();
raimund@2231: for (int j = 0; j < scount; j++) {
raimund@2231: Comparable seriesKey = data.getSeriesKey(j);
raimund@2231: log.debug("series key: " + seriesKey.toString());
raimund@2231: writeCSVHeader(writer, seriesKey.toString());
raimund@2231: writeCSVData(writer, data);
raimund@2231: }
raimund@2231: }
raimund@2231: try {
raimund@2231: writer.close();
raimund@2231: }
raimund@2231: catch(IOException ioe) {
raimund@2231: log.error("Writing CSV export failed!");
raimund@2231: }
raimund@2231: }
raimund@2231:
raimund@2231:
raimund@2231: protected static void writeCSVHeader(CSVWriter writer, String key) {
raimund@2231: writer.writeNext(new String[] {"#"});
raimund@2231: writer.writeNext(new String[] {"# " + key});
raimund@2231: writer.writeNext(new String[] {"#"});
raimund@2231: writer.writeNext(new String[] {"X", "Y"});
raimund@2231: }
raimund@2231:
raimund@2231:
raimund@2231: protected static void writeCSVData(CSVWriter writer, XYDataset data) {
raimund@2231: int series = data.getSeriesCount();
raimund@2231: for (int i = 0; i < series; i++) {
raimund@2231: int items = data.getItemCount(i);
raimund@2231: for (int j = 0; j < items; j++) {
raimund@2231: log.debug("write data: " + data.getX(i, j) + ", " + data.getY(i, j));
raimund@2231: writer.writeNext(new String[] {
raimund@2231: data.getX(i, j).toString(),
raimund@2231: data.getY(i, j).toString()});
raimund@2231: }
raimund@2231: }
raimund@2231: }
raimund@2231:
raimund@2231:
ingo@1735: public static int[] getSize(CallContext cc) {
ingo@1735: int[] size = new int[2];
ingo@1735:
ingo@1735: size[0] = (Integer) cc.getContextValue("chart.width");
ingo@1735: size[1] = (Integer) cc.getContextValue("chart.height");
ingo@1735:
ingo@1735: return size;
ingo@1735: }
ingo@1735:
ingo@1735:
ingo@152: /**
ingo@152: * This method returns the anchor of the chart so that the chart is centered
ingo@152: * according to the given parameters.
ingo@152: *
ingo@152: * @param mLeft Left margin
ingo@152: * @param mRight Right margin
ingo@152: * @param mBottom Bottom margin
ingo@152: * @param mTop Top margin
ingo@152: * @param width The complete width of the drawing area.
ingo@152: * @param height The complete height of the drawing area.
ingo@152: * @param chartWidth The width of the chart.
ingo@152: * @param chartHeight The height of the chart.
ingo@152: *
ingo@152: * @return an array that contains the anchor for a chart with the given
ingo@152: * parameters. The first value is the x point, the second value is the y
ingo@152: * point.
ingo@152: */
ingo@152: public static double[] getCenteredAnchor(
ingo@152: double mLeft, double mRight, double mBottom, double mTop,
ingo@152: double width, double height,
ingo@152: double chartWidth, double chartHeight
ingo@152: ) {
ingo@152: if (log.isDebugEnabled()) {
ingo@152: log.debug("Calculate centered origin...");
ingo@152: log.debug("-> PDF width : " + width);
ingo@152: log.debug("-> PDF height : " + height);
ingo@152: log.debug("-> Chart width : " + chartWidth);
ingo@152: log.debug("-> Chart height : " + chartHeight);
ingo@152: log.debug("-> margin left : " + mLeft);
ingo@152: log.debug("-> margin right : " + mRight);
ingo@152: log.debug("-> margin bottom: " + mBottom);
ingo@152: log.debug("-> margin top : " + mTop);
ingo@152: }
ingo@152:
ingo@152: double[] origin = new double[2];
ingo@152:
ingo@152: double centerX = width / 2;
ingo@152: double centerY = height / 2;
ingo@152:
ingo@152: origin[0] = centerX - chartWidth / 2;
ingo@152: origin[1] = centerY - chartHeight / 2;
ingo@152:
ingo@152: origin[0] = origin[0] >= mLeft ? origin[0] : mLeft;
ingo@152: origin[1] = origin[1] >= mTop ? origin[1] : mTop;
ingo@152:
ingo@152: if (log.isDebugEnabled()) {
ingo@152: log.debug("==> centered left origin: " + origin[0]);
ingo@152: log.debug("==> centered top origin: " + origin[1]);
ingo@152: }
ingo@152:
ingo@152: return origin;
ingo@152: }
ingo@152: }
ingo@152: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :