comparison gnv-artifacts/src/main/java/de/intevation/gnv/chart/AbstractXYLineChart.java @ 875:5e9efdda6894

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

http://dive4elements.wald.intevation.org