Inserción de gráficas JFreeChart en un documento Excel mediante la librería POI de Apache

2
18329

Inserción de gráficas JFreeChart en un documento Excel mediante la librería POI de Apache.

0. Índice de contenidos.


1. Introducción

Un caso común en nuestras aplicaciones es la generación de documentos Excel con gran cantidad de información, la cual muchas veces queremos plasmar como una gráfica para que todo sea más claro.

Existen diversas librerías en Java para poder realizar gráficas a partir de datos de nuestras aplicaciones, o para crear y manejar documentos Excel.

En nuestro caso utilizamos la librería POI de apache para realizar la hoja Excel. El proyecto POI de Apache consiste en el mantenimiento de diversas librerías que nos permiten crear y manipular ficheros Excel. A continuación pensemos como incluir una gráfica en documentos de este tipo generados mediante POI.

Una posible solución es que la librería POI en sus últimas versiones incluye la posibilidad de generar gráficas dentro del documento Excel.

Puesto que dicha funcionalidad es de reciente creación descartamos esta opción por no estar aún muy probada y no tener una funcionalidad muy extensa a la hora de crear la gráfica. Aún así se pueden ver ejemplos muy interesantes en la página de Apache.

La segunda opción, es que los datos a representar vayan dentro de nuestro propio Excel y al abrir el mismo, mediante una macro, representemos la gráfica que queremos. Esto implica un tratamiento mediante macros complejo, aunque siempre es otra opción.

Nuestra última opción y la escogida, es crear nuestra gráfica mediante la librería JFreeChart y luego embeber el objeto dentro de nuestro documento Excel mediante POI. Escogemos esta opción ya que JFreeChart es una librería que lleva mucho tiempo entre nosotros, esta muy probada, es muy usable y además está dotada de una infinidad de opciones para crear nuestra gráfica. Por otro lado es sencillo inserta el objeto JFreeChart en nuestro documento Excel mediante POI.


2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2.2 GHz Intel Core i7 Duo, 8GB DDR3 SDRAM).
  • Sistema Operativo: Mac OS X Lion 10.7.3 (11D50d)
  • Apache POI 3.8
  • JFreeChart 1.0.13


3. Inserción de gráfica JFreeChart en Excel mediante POI.

Lo primero de todo decir que la parte del código que genera la gráfica lo basamos en el tutorial de un compañero publicado con anterioridad ‘Crear gráficas con series con JFreeChart’.

Iremos viendo por pasos la formación de nuestra clase que dará lugar al documento Excel con la gráfica.

En primer lugar nuestra clase se llamará por ejemplo ‘JFreeChartPOIEmbebed‘. Definimos las siguientes constantes:

	private static final Color COLOR_SERIE_1 = new Color(255, 128, 64);
 
	private static final Color COLOR_SERIE_2 = new Color(28, 84, 140);
 
	private static final Color COLOR_CHART_CELLS = new Color(31, 87, 4);
 
	private static final Color BACKGROUND_COLOR_CHART = Color.white;
    
	private static final String TITLE = "Relación de unidades";
    
	private static final String LABEL_X = "Semanas";
    
	private static final String LABEL_Y = "Unidades";
    
	private static final String SERIE_NAME_2 = "Inventario2";
    
	private static final String SERIE_NAME_1 = "Inventario1";
    
	private static final String SHEET_NAME = "hoja";
    
	private static final int POSITION_X = 1;
    
	private static final int POSITION_Y = 1;
	
	private static final int CHART_WIDTH = 10 + POSITION_X; 
 
	private static final int CHART_HIGH = 23 + POSITION_Y; 
	
	private static final int WIDTH = 400;
	
	private static final int HIGH = 300;

A continuación tenemos los métodos que generán la gráfica mediante la libreria JFreeChart:


	// creación de la gráfica mediante JFreeChart
	public static JFreeChart drawChart(XYSeriesCollection dataset) {
 
        final JFreeChart chart = ChartFactory.createXYLineChart(TITLE, LABEL_X, LABEL_Y,
                dataset,
                PlotOrientation.VERTICAL,
                true, // uso de leyenda
                false, // uso de tooltips 
                false // uso de urls
                );

        chart.setBackgroundPaint(BACKGROUND_COLOR_CHART);
 
        final XYPlot plot = (XYPlot) chart.getPlot();
        configurePlot(plot);
 
        final NumberAxis domainAxis = (NumberAxis)plot.getDomainAxis();
        configureDomainAxis(domainAxis);
         
        final NumberAxis rangeAxis = (NumberAxis)plot.getRangeAxis();
        configureRangeAxis(rangeAxis);
 
        final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer)plot.getRenderer();
        configureRendered(renderer);
 
        return chart;
    }
     
    // configuramos el contenido del gráfico (damos un color a las líneas que sirven de guía)
    private static void configurePlot (XYPlot plot) {
        plot.setDomainGridlinePaint(COLOR_CHART_CELLS);
        plot.setRangeGridlinePaint(COLOR_CHART_CELLS);
    }
     
    // configuramos el eje X de la gráfica (se muestran números enteros y de uno en uno)
    private static void configureDomainAxis (NumberAxis domainAxis) {
        domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        domainAxis.setTickUnit(new NumberTickUnit(1));
    }
     
    // configuramos el eje y de la gráfica (números enteros de dos en dos y rango entre 120 y 135)
    private static void configureRangeAxis (NumberAxis rangeAxis) {
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        rangeAxis.setTickUnit(new NumberTickUnit(2));
        rangeAxis.setRange(120, 135);
    }
     
    // configuramos las líneas de las series (añadimos un círculo en los puntos y asignamos el color de cada serie)
    private static void configureRendered (XYLineAndShapeRenderer renderer) {
        renderer.setSeriesShapesVisible(0, true);
        renderer.setSeriesShapesVisible(1, true);
        renderer.setSeriesPaint(0, COLOR_SERIE_1);
        renderer.setSeriesPaint(1, COLOR_SERIE_2);
    }

Básicamente tenemos un método ‘drawChart’ que recibe como parámetro una colección de series con datos, y nos devuelve un objeto del tipo JFreeChart que representa nuestra gráfica.

Dicho método se apoya en otros métodos para configurar de manera adecuada la gráfica. Para obtener más detalle de esta parte visitar el contenido del tutorial ‘Crear gráficas con series con JFreeChart’.

A continuación tenemos el siguiente método:

public static void addChartToExcel(JFreeChart chart) throws Exception{
    	
    	final BufferedImage buffer = chart.createBufferedImage(WIDTH, HIGH);
    	final FileOutputStream file = new FileOutputStream("ExcelPOIGrafica.xls");

    	ByteArrayOutputStream img_bytes = new ByteArrayOutputStream();
    	ImageIO.write( buffer, "png",img_bytes );
    	img_bytes.flush();

    	HSSFWorkbook wb = new HSSFWorkbook();
    	wb.createSheet(SHEET_NAME);
    	
    	
    	HSSFClientAnchor anchor = new HSSFClientAnchor(0,0,0,0,(short)POSITION_X,POSITION_Y,(short)CHART_WIDTH,CHART_HIGH);
    	
    	int index = wb.addPicture(img_bytes.toByteArray(),HSSFWorkbook.PICTURE_TYPE_PNG);
    	HSSFSheet sheet = wb.getSheet(SHEET_NAME);
    	HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
    	patriarch.createPicture(anchor,index);
    	wb.write(file);
    	file.close();
    }

Lo primero que hace es obtener a partir del objeto JFreeChart que recibimos como parametro, un array de bytes, que necesitamos posteriormente en el API que utilizamos de la libreria POI.

A continuación creamos nuestro ‘libro excel’, representado por el objeto HSSFWorkbook.

Una de las tareas habituales y posiblemente imprescindibles sobre este tipo de objetos es crear una hoja que representa una hoja de nuestro documento Excel. Para ello invocamos al método ‘createSheet(String name)‘, pasando como parámetro el nombre de la hoja que vamos a crear.

Lo siguiente es crear un area de trabajo dentro de la hoja Excel, que nos permita situar el contedor que crearemos más adelante, dentro de la hoja Excel. Dicho objeto es del tipo ‘HSSFClientAnchor’. Veamos que representa cada parámetro del contructor ‘public HSSFClientAnchor(int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2)’:

  • int dx1: Coordenada X de la esquina superior izquierda dentro de la primera celda
  • int dy1: Coordenada Y de la esquina superior izquierda dentro de la primera celda
  • int dx2: Coordenada X de la esquina superior izquierda dentro de la última celda
  • int dy2: Coordenada Y de la esquina superior izquierda dentro de la última celda
  • short col1: La columna donde se situa la primera celda del anchor
  • int row1: La fila donde se situa la primera celda del anchor
  • short col2: La columna donde se situa la última celda del anchor
  • int row2: La fila donde se situa la última celda del anchor

El siguiente paso es añadir a nuestro libro la imagen. para ello utilizamos el método ‘public int addPicture(byte[] pictureData, int format)’, que recibe como parámetro el array de bytes con la imagen que creamos con anterioridad, y el formato de nuestra imagen, que en este caso elegimos del tipo PNG. Dicho método nos devuelve un
indice para poder referenciar la imagen dentro del libro en cualquier momento.

Luego recuperamos la hoja (sheet) que creamos dentro de nuestro libro, para crear en su interior un contenedor donde se aloje la imagen. Dicho contenedor es del tipo ‘HSSFPatriarch’. Se trata de un contenedor que pueda albergar grupos de datos, objetos o incluso otro tipo de contenedores. A continuación creamos la imagen dentro del contenedor mediante

el método ‘createPicture(HSSFClientAnchor anchor, int pictureIndex)’, que recibe como parámetro el ‘anchor’ creado con anterioridad, y el indice de la imagen que queremos añadir.

Solo queda escribir nuestro libro en el fichero Excel y cerrar dicho fichero.

Veamos un pequeño main que nos permita probar la funcionalidad:

 public static void main(String[] args) throws Exception {

	    final XYSeries serie1 = new XYSeries(SERIE_NAME_1);
	    serie1.add(1, 131.78);
	    serie1.add(2, 129.95);
	    serie1.add(3, 128.16);
	    serie1.add(4, 125.91);
	    serie1.add(5, 130.44);
	     
	    final XYSeries serie2 = new XYSeries(SERIE_NAME_2);
	    serie2.add(1, 133.16);
	    serie2.add(2, 132.32);
	    serie2.add(3, 129.86);
	    serie2.add(4, 128.02);
	    serie2.add(5, 132.45);
	     
	    final XYSeriesCollection collection = new XYSeriesCollection();
	    collection.addSeries(serie1);
	    collection.addSeries(serie2);
	     
	    try {
	    	
	        final JFreeChart chart = JFreeChartPOIEmbebed.drawChart(collection);
	        addChartToExcel(chart);
	        
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
    	 
    }

En él, básicamente creamos una colección con los datos que queremos representar, creamos la la gráfica del tipo JFreeChart, y la añadimos a nuestro Excel.

Trás ejecutar el ejemplo observamos como se ha creado nuestro documento Excel con la gráfica que hemos asignado.


4. Referencias.


5. Conclusiones.

Como podemos ver el API de la librería POI nos permite de manera sencilla crear un documento Excel y embeber objetos dentro del mismo. Efectivamente este es un ejemplo muy sencillo, pero POI ofrece una amplio abanico de funcionalidad para el manejo de ficheros Excel, además se complementa con la cantidad de opciones que JFreeChart nos ofrece
para crear gráficas.

Un saludo.

Daniel Casanova

dcasanova@autentia.com

2 COMENTARIOS

  1. Saludos cordiales, muy bueno el tutorial, tengo un problema al momento que se invoca el método marca un:

    Exception in thread \\\»main\\\» java.lang.NoClassDefFoundError: org/apache/commons/codec/digest/DigestUtils
    at org.apache.poi.hssf.usermodel.HSSFWorkbook.addPicture(HSSFWorkbook.java:1610)
    at Graficos.JFreeChartPOIEmbebed.addChartToExcel(JFreeChartPOIEmbebed.java:131)
    at Graficos.JFreeChartPOIEmbebed.main(JFreeChartPOIEmbebed.java:165)
    Caused by: java.lang.ClassNotFoundException: org.apache.commons.codec.digest.DigestUtils
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)

    La linea del problema es:
    index = wb.addPicture(img_bytes.toByteArray(),HSSFWorkbook.PICTURE_TYPE_PNG);

    Me podrías ayudar?, saludos!!

  2. Excelente información, hay alguna posibilidad que podemos tener la gráfica de forma dinámica en el excel? no necesariamente insertar la grafica en formato imagen.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad