Mercurial > dive4elements > gnv-client
view gnv-artifacts/src/main/java/de/intevation/gnv/chart/AbstractXYLineChart.java @ 1119:7c4f81f74c47
merged gnv-artifacts
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:00 +0200 |
parents | f953c9a559d8 |
children |
line wrap: on
line source
/* * Copyright (c) 2010 by Intevation GmbH * * This program is free software under the LGPL (>=v2.1) * Read the file LGPL.txt coming with the software for details * or visit http://www.gnu.org/licenses/ if it does not exist. */ package de.intevation.gnv.chart; import de.intevation.gnv.geobackend.base.Result; import de.intevation.gnv.state.describedata.KeyValueDescibeData; import java.awt.Color; import java.awt.geom.Ellipse2D; import java.text.NumberFormat; import java.util.Collection; import java.util.Iterator; import java.util.Locale; import java.util.Map; import org.apache.log4j.Logger; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberTickUnit; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.title.TextTitle; import org.jfree.data.Range; import org.jfree.data.general.Series; import org.jfree.data.xy.XYDataset; import org.jfree.ui.RectangleInsets; /** * This abstract class defines some methods to adjust chart settings after its * creation. * * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> */ public abstract class AbstractXYLineChart extends AbstractChart { /** * Constant field used to expand the area between data and chart border. Its * value is {@value}.<br> * A value of 0.05 equals 5 percent. */ public static final double LOWER_MARGIN = 0.05D; /** * Constant field used to expand the area between data and chart border. Its * value is {@value}.<br> * A value of 0.05 equals 5 percent. */ public static final double UPPER_MARGIN = 0.05D; /** * Logger used to log with log4j. */ private static Logger log = Logger.getLogger(AbstractXYLineChart.class); /** * Field of supported colors used for lines and data points in charts. */ protected static Color[] COLOR = { Color.black, Color.red, Color.green, Color.blue, Color.yellow, Color.gray, Color.orange, Color.pink, Color.cyan }; /** * Static field to remember the index of the previously used color. */ protected static int nextColor = 0; /** * Default <code>PlotOrientation</code>. */ protected PlotOrientation PLOT_ORIENTATION = PlotOrientation.VERTICAL; /** * Map to store datasets for each parameter. */ protected Map datasets; /** * Map to store max ranges of each parameter (axis.setAutoRange(true) * doesn't seem to work */ protected Map ranges; /** * This method is called by <code>Chart</code> to bring the data into the * right form fitting to JFreeChart objects. */ protected abstract void initData(); /** * Add a value of <code>row</code> to <code>series</code>. * * @param row <code>Result</code> Object returned from database. Contains * a value used to add to <code>series</code> * @param series A JFreeChart Series object. */ protected abstract void addValue(Result row, Series series); /** * Add <code>series</code> to JFreeChart's Dataset object currently which is * processing. * * @param series Series to add. * @param label Label used show in legend. * @param idx Currently not used. */ protected abstract void addSeries(Series series, String label, int idx); /** * Abstract method which is called by <code>Chart</code> interface after * chart creation. It turns an axis' label into a locale specific format. * * @param axis Axis to adjust. * @param locale java.util.Locale object used specify the format. */ protected abstract void localizeDomainAxis(Axis axis, Locale locale); /** * Abstract method to create a label for a series of parameters. * * @param breakPoint1 Identifier returned from database. These identifier * are used to identify the results from database which are all stored in * one big java.util.Collection. * @param breakPoint2 Identifier returned from database. * @param breakPoint3 Identifier returned from database. * * @return Concatinated string of parameter name and measurement. */ protected abstract String createSeriesName( String breakPoint1, String breakPoint2, String breakPoint3 ); /** * @see de.intevation.gnv.chart.Chart#generateChart() */ public JFreeChart generateChart() { log.debug("generate XYLineChart"); nextColor = 0; if (chart != null) return chart; initChart(); chart.addSubtitle(new TextTitle(labels.getSubtitle())); theme.apply(chart); initData(); adjustPlot((XYPlot)chart.getPlot()); return chart; } protected void initChart() { chart = ChartFactory.createXYLineChart( labels.getTitle(), labels.getDomainAxisLabel(), null, null, PLOT_ORIENTATION, true, false, false ); } /** * Method used to adjust the axes after chart generation. Methods for i18n * support ({@link #localizeDomainAxis} and {@link #localizeRangeAxis}) are * called and axes of this series are expanded. * * @param seriesKey Identifier of an axis which have to be adjusted. * @param idx Set the axis identified by <code>seriesKey</code> to position * <code>idx</code>. */ protected void prepareAxis(String seriesKey, int idx) { log.debug("prepare axis of xychart"); XYPlot plot = chart.getXYPlot(); Axis xAxis = plot.getDomainAxis(); NumberAxis yAxis = new NumberAxis(seriesKey); localizeDomainAxis(xAxis, locale); localizeRangeAxis(yAxis, locale); // litte workarround to adjust the max range of axes. // NumberAxis.setAutoRange(true) doesn't seem to work properly. Range yRange = (Range) ranges.get(seriesKey); double lo = yRange.getLowerBound(); double hi = yRange.getUpperBound(); if (lo == hi) { yRange = new Range( lo - (lo / 100 * LOWER_MARGIN), hi + (hi / 100 * UPPER_MARGIN)); } else { yRange = Range.expand(yRange, LOWER_MARGIN, UPPER_MARGIN); } yAxis.setRange(yRange); log.debug("Max Range of dataset is: " + yRange.toString()); if (seriesKey.contains("richtung")) { yAxis.setTickUnit(new NumberTickUnit(30.0)); yAxis.setUpperBound(360.0); yAxis.setLowerBound(0.0); } else { yAxis.setFixedDimension(10.0); yAxis.setAutoRangeIncludesZero(false); } plot.setRangeAxis(idx, yAxis); yAxis.configure(); if (idx % 2 != 0) plot.setRangeAxisLocation(idx, AxisLocation.BOTTOM_OR_RIGHT); else plot.setRangeAxisLocation(idx, AxisLocation.BOTTOM_OR_LEFT); plot.mapDatasetToRangeAxis(idx, idx); } /** * Method to adjust the rendering of a series in a chart. Line color and * symbols of vertices are configured here. * * @param idx Position of the renderer. * @param seriesCount Maximum number of series in this chart. * @param renderLines Lines are displayed if true, otherwise they are not. * @param renderShapes Vertices are displayed if true, otherwise they are * not. */ protected void adjustRenderer( int idx, int seriesCount, boolean renderLines, boolean renderShapes ) { log.debug("Adjust render of series"); XYLineAndShapeRenderer renderer = null; XYPlot plot = chart.getXYPlot(); try { renderer = (XYLineAndShapeRenderer)((XYLineAndShapeRenderer) (plot.getRenderer())).clone(); } catch (CloneNotSupportedException cnse) { log.warn("Error while cloning renderer.", cnse); renderer = new XYLineAndShapeRenderer(renderLines, renderShapes); renderer.setBaseShape(new Ellipse2D.Double(-2,-2,4,4)); } for (int i = 0; i < seriesCount; i++) { renderer.setSeriesShape(i, renderer.getSeriesShape(0)); renderer.setSeriesPaint(i, COLOR[nextColor() % COLOR.length]); renderer.setSeriesShapesVisible(i, renderShapes); renderer.setSeriesLinesVisible(i, renderLines); } plot.setRenderer(idx, renderer); } /** * @return Index of the next color */ protected static synchronized int nextColor() { return nextColor++; } /** * Method to adjust the plot rendering. Disable horizontal grid lines if * <code>plot</code> contains only a single y-axis. * * @param plot JFreeChart Plot object to be adjusted. */ protected void adjustPlot(XYPlot plot) { if (plot.getRangeAxisCount() > 1) plot.setRangeGridlinesVisible(false); plot.setAxisOffset(new RectangleInsets(0, 0, 0, 15)); } /** * Abstract method which is called after chart creation. It turns an * axis' label into a locale specific format. * * @param axis Axis to adjust. * @param locale java.util.Locale object used specify the format. * */ protected void localizeRangeAxis(Axis axis, Locale locale) { if (locale == null) return; log.debug( "Set language of axis [" + axis.getLabel() + "] " + "to " + locale.toString() ); NumberFormat format = NumberFormat.getInstance(locale); ((NumberAxis) axis).setNumberFormatOverride(format); } /** * Return the maximum y-range of <code>dataset</code>. * * @param dataset Dataset to be scaned. * * @return JFreeChart Range object containing min and max y-value. */ public Range getMaxRangeOfDataset(XYDataset dataset) { int seriesCount = dataset.getSeriesCount(); double upper = Double.NEGATIVE_INFINITY; double lower = Double.POSITIVE_INFINITY; for (int i = 0; i < seriesCount; i++) { int itemCount = dataset.getItemCount(i); for (int j = 0; j < itemCount; j++) { Number num = dataset.getY(i, j); if (num != null) { double y = num.doubleValue(); lower = y < lower ? y : lower; upper = y > upper ? y : upper; } } } return new Range(lower, upper); } /** * Return the maximum y-range of <code>dataset</code> with a margin of * <code>percent</code> percent. * * @param dataset Dataset to be scaned. * @param percent Percent used to expand the range. * @return JFreeChart Range object containing min and max y-value with a * margin. */ public Range getMaxRangeOfDatasetWithMargin( XYDataset dataset, double percent ) { Range range = getMaxRangeOfDataset(dataset); double length = range.getLength(); double upper = range.getUpperBound() + length /100 * percent; double lower = range.getLowerBound() - length /100 * percent; return new Range(lower, upper); } /** * Method to find a parameter specified by its value. * * @param label Search string. * * @return Value of a parameter with the given label. */ protected String findParameter(String label) { Iterator iter = parameters.iterator(); while (iter.hasNext()) { KeyValueDescibeData data = (KeyValueDescibeData) iter.next(); String key = data.getValue(); if (label.indexOf(key) > -1) return key; } return label; } /** * Method to find a description of a given collection of values. * * @param values Collection to be scaned. * @param id Identifier and search string of the searched value. * * @return title */ protected String findValueTitle(Collection values, String id) { log.debug("find description of dataset"); if (values != null){ Iterator it = values.iterator(); while (it.hasNext()) { KeyValueDescibeData data = (KeyValueDescibeData) it.next(); if (id.equals(data.getKey())) return data.getValue(); } } return ""; } /** * Method to store the maximum range. Since we need to adjust the range of * each range axis, we have to memorize its range - each parameter uses an * own range axis. * * @param ranges Map where ranges of each axis will be stored in. * @param value A given value on an axis. * @param parameter Parameter name which belongs to <code>value</code>. */ protected void storeMaxRange(Map ranges, double value, String parameter) { Range range = null; range = ranges.containsKey(parameter) ? (Range) ranges.get(parameter) : new Range(value, value); double lower = range.getLowerBound(); double upper = range.getUpperBound(); lower = value < lower ? value : lower; upper = value > upper ? value : upper; ranges.put(parameter, new Range(lower, upper)); } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :