comparison 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
comparison
equal deleted inserted replaced
1027:fca4b5eb8d2f 1119:7c4f81f74c47
1 /*
2 * Copyright (c) 2010 by Intevation GmbH
3 *
4 * This program is free software under the LGPL (>=v2.1)
5 * Read the file LGPL.txt coming with the software for details
6 * or visit http://www.gnu.org/licenses/ if it does not exist.
7 */
8
9 package de.intevation.gnv.chart;
10
11 import de.intevation.gnv.geobackend.base.Result;
12
13 import de.intevation.gnv.state.describedata.KeyValueDescibeData;
14
15 import java.awt.Color;
16
17 import java.awt.geom.Ellipse2D;
18
19 import java.text.NumberFormat;
20
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.Locale;
24 import java.util.Map;
25
26 import org.apache.log4j.Logger;
27
28 import org.jfree.chart.ChartFactory;
29 import org.jfree.chart.JFreeChart;
30
31 import org.jfree.chart.axis.Axis;
32 import org.jfree.chart.axis.AxisLocation;
33 import org.jfree.chart.axis.NumberAxis;
34 import org.jfree.chart.axis.NumberTickUnit;
35
36 import org.jfree.chart.plot.PlotOrientation;
37 import org.jfree.chart.plot.XYPlot;
38
39 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
40
41 import org.jfree.chart.title.TextTitle;
42
43 import org.jfree.data.Range;
44
45 import org.jfree.data.general.Series;
46
47 import org.jfree.data.xy.XYDataset;
48
49 import org.jfree.ui.RectangleInsets;
50
51 /**
52 * This abstract class defines some methods to adjust chart settings after its
53 * creation.
54 *
55 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
56 */
57 public abstract class AbstractXYLineChart
58 extends AbstractChart
59 {
60 /**
61 * Constant field used to expand the area between data and chart border. Its
62 * value is {@value}.<br>
63 * A value of 0.05 equals 5 percent.
64 */
65 public static final double LOWER_MARGIN = 0.05D;
66
67 /**
68 * Constant field used to expand the area between data and chart border. Its
69 * value is {@value}.<br>
70 * A value of 0.05 equals 5 percent.
71 */
72 public static final double UPPER_MARGIN = 0.05D;
73
74 /**
75 * Logger used to log with log4j.
76 */
77 private static Logger log = Logger.getLogger(AbstractXYLineChart.class);
78
79 /**
80 * Field of supported colors used for lines and data points in charts.
81 */
82 protected static Color[] COLOR = {
83 Color.black, Color.red, Color.green, Color.blue, Color.yellow,
84 Color.gray, Color.orange, Color.pink, Color.cyan
85 };
86
87 /**
88 * Static field to remember the index of the previously used color.
89 */
90 protected static int nextColor = 0;
91
92 /**
93 * Default <code>PlotOrientation</code>.
94 */
95 protected PlotOrientation PLOT_ORIENTATION = PlotOrientation.VERTICAL;
96
97 /**
98 * Map to store datasets for each parameter.
99 */
100 protected Map datasets;
101
102 /**
103 * Map to store max ranges of each parameter (axis.setAutoRange(true)
104 * doesn't seem to work */
105 protected Map ranges;
106
107 /**
108 * This method is called by <code>Chart</code> to bring the data into the
109 * right form fitting to JFreeChart objects.
110 */
111 protected abstract void initData();
112
113 /**
114 * Add a value of <code>row</code> to <code>series</code>.
115 *
116 * @param row <code>Result</code> Object returned from database. Contains
117 * a value used to add to <code>series</code>
118 * @param series A JFreeChart Series object.
119 */
120 protected abstract void addValue(Result row, Series series);
121
122 /**
123 * Add <code>series</code> to JFreeChart's Dataset object currently which is
124 * processing.
125 *
126 * @param series Series to add.
127 * @param label Label used show in legend.
128 * @param idx Currently not used.
129 */
130 protected abstract void addSeries(Series series, String label, int idx);
131
132 /**
133 * Abstract method which is called by <code>Chart</code> interface after
134 * chart creation. It turns an axis' label into a locale specific format.
135 *
136 * @param axis Axis to adjust.
137 * @param locale java.util.Locale object used specify the format.
138 */
139 protected abstract void localizeDomainAxis(Axis axis, Locale locale);
140
141 /**
142 * Abstract method to create a label for a series of parameters.
143 *
144 * @param breakPoint1 Identifier returned from database. These identifier
145 * are used to identify the results from database which are all stored in
146 * one big java.util.Collection.
147 * @param breakPoint2 Identifier returned from database.
148 * @param breakPoint3 Identifier returned from database.
149 *
150 * @return Concatinated string of parameter name and measurement.
151 */
152 protected abstract String createSeriesName(
153 String breakPoint1,
154 String breakPoint2,
155 String breakPoint3
156 );
157
158
159 /**
160 * @see de.intevation.gnv.chart.Chart#generateChart()
161 */
162 public JFreeChart generateChart() {
163 log.debug("generate XYLineChart");
164 nextColor = 0;
165
166 if (chart != null)
167 return chart;
168
169 initChart();
170
171 chart.addSubtitle(new TextTitle(labels.getSubtitle()));
172
173 theme.apply(chart);
174 initData();
175
176 adjustPlot((XYPlot)chart.getPlot());
177
178 return chart;
179 }
180
181
182 protected void initChart() {
183 chart = ChartFactory.createXYLineChart(
184 labels.getTitle(),
185 labels.getDomainAxisLabel(),
186 null,
187 null,
188 PLOT_ORIENTATION,
189 true,
190 false,
191 false
192 );
193 }
194
195
196 /**
197 * Method used to adjust the axes after chart generation. Methods for i18n
198 * support ({@link #localizeDomainAxis} and {@link #localizeRangeAxis}) are
199 * called and axes of this series are expanded.
200 *
201 * @param seriesKey Identifier of an axis which have to be adjusted.
202 * @param idx Set the axis identified by <code>seriesKey</code> to position
203 * <code>idx</code>.
204 */
205 protected void prepareAxis(String seriesKey, int idx) {
206 log.debug("prepare axis of xychart");
207
208 XYPlot plot = chart.getXYPlot();
209 Axis xAxis = plot.getDomainAxis();
210 NumberAxis yAxis = new NumberAxis(seriesKey);
211
212 localizeDomainAxis(xAxis, locale);
213 localizeRangeAxis(yAxis, locale);
214
215 // litte workarround to adjust the max range of axes.
216 // NumberAxis.setAutoRange(true) doesn't seem to work properly.
217 Range yRange = (Range) ranges.get(seriesKey);
218 double lo = yRange.getLowerBound();
219 double hi = yRange.getUpperBound();
220
221 if (lo == hi) {
222 yRange = new Range(
223 lo - (lo / 100 * LOWER_MARGIN),
224 hi + (hi / 100 * UPPER_MARGIN));
225 }
226 else {
227 yRange = Range.expand(yRange, LOWER_MARGIN, UPPER_MARGIN);
228 }
229 yAxis.setRange(yRange);
230 log.debug("Max Range of dataset is: " + yRange.toString());
231
232 if (seriesKey.contains("richtung")) {
233 yAxis.setTickUnit(new NumberTickUnit(30.0));
234 yAxis.setUpperBound(360.0);
235 yAxis.setLowerBound(0.0);
236 }
237 else {
238 yAxis.setFixedDimension(10.0);
239 yAxis.setAutoRangeIncludesZero(false);
240 }
241
242 plot.setRangeAxis(idx, yAxis);
243 yAxis.configure();
244
245 if (idx % 2 != 0)
246 plot.setRangeAxisLocation(idx, AxisLocation.BOTTOM_OR_RIGHT);
247 else
248 plot.setRangeAxisLocation(idx, AxisLocation.BOTTOM_OR_LEFT);
249
250 plot.mapDatasetToRangeAxis(idx, idx);
251 }
252
253
254 /**
255 * Method to adjust the rendering of a series in a chart. Line color and
256 * symbols of vertices are configured here.
257 *
258 * @param idx Position of the renderer.
259 * @param seriesCount Maximum number of series in this chart.
260 * @param renderLines Lines are displayed if true, otherwise they are not.
261 * @param renderShapes Vertices are displayed if true, otherwise they are
262 * not.
263 */
264 protected void adjustRenderer(
265 int idx,
266 int seriesCount,
267 boolean renderLines,
268 boolean renderShapes
269 ) {
270 log.debug("Adjust render of series");
271 XYLineAndShapeRenderer renderer = null;
272 XYPlot plot = chart.getXYPlot();
273
274 try {
275 renderer = (XYLineAndShapeRenderer)((XYLineAndShapeRenderer)
276 (plot.getRenderer())).clone();
277 }
278 catch (CloneNotSupportedException cnse) {
279 log.warn("Error while cloning renderer.", cnse);
280 renderer = new XYLineAndShapeRenderer(renderLines, renderShapes);
281 renderer.setBaseShape(new Ellipse2D.Double(-2,-2,4,4));
282 }
283
284 for (int i = 0; i < seriesCount; i++) {
285 renderer.setSeriesShape(i, renderer.getSeriesShape(0));
286 renderer.setSeriesPaint(i, COLOR[nextColor() % COLOR.length]);
287 renderer.setSeriesShapesVisible(i, renderShapes);
288 renderer.setSeriesLinesVisible(i, renderLines);
289 }
290 plot.setRenderer(idx, renderer);
291 }
292
293
294 /**
295 * @return Index of the next color
296 */
297 protected static synchronized int nextColor() {
298 return nextColor++;
299 }
300
301
302 /**
303 * Method to adjust the plot rendering. Disable horizontal grid lines if
304 * <code>plot</code> contains only a single y-axis.
305 *
306 * @param plot JFreeChart Plot object to be adjusted.
307 */
308 protected void adjustPlot(XYPlot plot) {
309 if (plot.getRangeAxisCount() > 1)
310 plot.setRangeGridlinesVisible(false);
311
312 plot.setAxisOffset(new RectangleInsets(0, 0, 0, 15));
313 }
314
315
316 /**
317 * Abstract method which is called after chart creation. It turns an
318 * axis' label into a locale specific format.
319 *
320 * @param axis Axis to adjust.
321 * @param locale java.util.Locale object used specify the format.
322 *
323 */
324 protected void localizeRangeAxis(Axis axis, Locale locale) {
325 if (locale == null)
326 return;
327
328 log.debug(
329 "Set language of axis [" + axis.getLabel() + "] " +
330 "to " + locale.toString()
331 );
332
333 NumberFormat format = NumberFormat.getInstance(locale);
334 ((NumberAxis) axis).setNumberFormatOverride(format);
335 }
336
337
338 /**
339 * Return the maximum y-range of <code>dataset</code>.
340 *
341 * @param dataset Dataset to be scaned.
342 *
343 * @return JFreeChart Range object containing min and max y-value.
344 */
345 public Range getMaxRangeOfDataset(XYDataset dataset) {
346 int seriesCount = dataset.getSeriesCount();
347 double upper = Double.NEGATIVE_INFINITY;
348 double lower = Double.POSITIVE_INFINITY;
349
350 for (int i = 0; i < seriesCount; i++) {
351 int itemCount = dataset.getItemCount(i);
352
353 for (int j = 0; j < itemCount; j++) {
354 Number num = dataset.getY(i, j);
355
356 if (num != null) {
357 double y = num.doubleValue();
358 lower = y < lower ? y : lower;
359 upper = y > upper ? y : upper;
360 }
361 }
362 }
363
364 return new Range(lower, upper);
365 }
366
367
368 /**
369 * Return the maximum y-range of <code>dataset</code> with a margin of
370 * <code>percent</code> percent.
371 *
372 * @param dataset Dataset to be scaned.
373 * @param percent Percent used to expand the range.
374 * @return JFreeChart Range object containing min and max y-value with a
375 * margin.
376 */
377 public Range getMaxRangeOfDatasetWithMargin(
378 XYDataset dataset,
379 double percent
380 ) {
381 Range range = getMaxRangeOfDataset(dataset);
382 double length = range.getLength();
383 double upper = range.getUpperBound() + length /100 * percent;
384 double lower = range.getLowerBound() - length /100 * percent;
385
386 return new Range(lower, upper);
387 }
388
389
390 /**
391 * Method to find a parameter specified by its value.
392 *
393 * @param label Search string.
394 *
395 * @return Value of a parameter with the given label.
396 */
397 protected String findParameter(String label) {
398 Iterator iter = parameters.iterator();
399
400 while (iter.hasNext()) {
401 KeyValueDescibeData data = (KeyValueDescibeData) iter.next();
402 String key = data.getValue();
403
404 if (label.indexOf(key) > -1)
405 return key;
406 }
407
408 return label;
409 }
410
411
412 /**
413 * Method to find a description of a given collection of values.
414 *
415 * @param values Collection to be scaned.
416 * @param id Identifier and search string of the searched value.
417 *
418 * @return title
419 */
420 protected String findValueTitle(Collection values, String id) {
421 log.debug("find description of dataset");
422
423 if (values != null){
424 Iterator it = values.iterator();
425 while (it.hasNext()) {
426 KeyValueDescibeData data = (KeyValueDescibeData) it.next();
427
428 if (id.equals(data.getKey()))
429 return data.getValue();
430 }
431 }
432 return "";
433 }
434
435
436 /**
437 * Method to store the maximum range. Since we need to adjust the range of
438 * each range axis, we have to memorize its range - each parameter uses an
439 * own range axis.
440 *
441 * @param ranges Map where ranges of each axis will be stored in.
442 * @param value A given value on an axis.
443 * @param parameter Parameter name which belongs to <code>value</code>.
444 */
445 protected void storeMaxRange(Map ranges, double value, String parameter) {
446 Range range = null;
447
448 range = ranges.containsKey(parameter)
449 ? (Range) ranges.get(parameter)
450 : new Range(value, value);
451
452 double lower = range.getLowerBound();
453 double upper = range.getUpperBound();
454
455 lower = value < lower ? value : lower;
456 upper = value > upper ? value : upper;
457
458 ranges.put(parameter, new Range(lower, upper));
459 }
460 }
461 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org