comparison artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper2.java @ 7116:3c7471b929d1

Merge branch generator-refectoring into default.
author Andre Heinecke <aheinecke@intevation.de>
date Tue, 24 Sep 2013 12:29:44 +0200
parents 74ebe5e7fa69
children e4606eae8ea5
comparison
equal deleted inserted replaced
7095:bdadffad35b1 7116:3c7471b929d1
1 /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
2 * Software engineering by Intevation GmbH
3 *
4 * This file is Free Software under the GNU AGPL (>=v3)
5 * and comes with ABSOLUTELY NO WARRANTY! Check out the
6 * documentation coming with Dive4Elements River for details.
7 */
8
9 package org.dive4elements.river.exports;
10
11 import java.awt.geom.AffineTransform;
12 import java.awt.geom.NoninvertibleTransformException;
13 import java.awt.geom.Rectangle2D;
14
15 import java.util.Date;
16
17 import org.w3c.dom.Document;
18 import org.w3c.dom.Element;
19
20 import org.apache.log4j.Logger;
21
22 import org.jfree.chart.ChartRenderingInfo;
23 import org.jfree.chart.JFreeChart;
24 import org.jfree.chart.axis.DateAxis;
25 import org.jfree.chart.axis.NumberAxis;
26 import org.jfree.chart.axis.ValueAxis;
27 import org.jfree.chart.plot.XYPlot;
28 import org.jfree.data.Range;
29 import org.jfree.data.xy.XYDataset;
30
31 import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
32 import org.dive4elements.artifacts.common.utils.XMLUtils;
33 import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
34
35 import org.dive4elements.river.jfree.Bounds;
36
37
38 /**
39 * This class helps generating chart info documents.
40 */
41 public class InfoGeneratorHelper2 {
42
43 /** Private logging instance. */
44 private static final Logger logger =
45 Logger.getLogger(InfoGeneratorHelper2.class);
46
47 protected ChartGenerator2 generator;
48
49 public InfoGeneratorHelper2(ChartGenerator2 generator) {
50 this.generator = generator;
51 }
52
53 /**
54 * Triggers the creation of the chart info document.
55 *
56 * @param chart The JFreeChart chart.
57 * @param info An info object that has been created while chart creation.
58 *
59 * @return the info document.
60 */
61 public Document createInfoDocument(
62 JFreeChart chart,
63 ChartRenderingInfo info)
64 {
65 logger.debug("InfoGeneratorHelper.createInfoDocument");
66
67 Document doc = XMLUtils.newDocument();
68
69 ElementCreator cr = new ElementCreator(
70 doc,
71 ArtifactNamespaceContext.NAMESPACE_URI,
72 ArtifactNamespaceContext.NAMESPACE_PREFIX);
73
74 Element chartinfo = cr.create("chartinfo");
75
76 chartinfo.appendChild(createAxesElements(cr, chart));
77 chartinfo.appendChild(createTransformationElements(cr, chart, info));
78
79 doc.appendChild(chartinfo);
80
81 return doc;
82 }
83
84
85 /**
86 * This method create a axes element that contains all domain and range
87 * axes of the given chart.
88 *
89 * @param cr The ElementCreator.
90 * @param chart The chart that provides range information of its axes.
91 *
92 * @return an element with axes information.
93 */
94 protected Element createAxesElements(
95 ElementCreator cr,
96 JFreeChart chart)
97 {
98 logger.debug("InfoGeneratorHelper.createRangeElements");
99
100 Element axes = cr.create("axes");
101
102 XYPlot plot = (XYPlot) chart.getPlot();
103
104 int dAxisCount = plot.getDomainAxisCount();
105 for (int i = 0; i < dAxisCount; i++) {
106 ValueAxis axis = plot.getDomainAxis(i);
107 XYDataset data = plot.getDataset(i);
108
109 if (axis != null) {
110 Element e = createAxisElement(cr, axis, data, "domain", i);
111 axes.appendChild(e);
112 }
113 }
114
115 int rAxisCount = plot.getRangeAxisCount();
116 for (int i = 0; i < rAxisCount; i++) {
117 ValueAxis axis = plot.getRangeAxis(i);
118 XYDataset data = plot.getDataset(i);
119
120 if (axis == null || data == null) {
121 logger.warn("Axis or dataset is empty at pos: " + i);
122 continue;
123 }
124
125 Element e = createAxisElement(cr, axis, data, "range", i);
126 axes.appendChild(e);
127 }
128
129 return axes;
130 }
131
132
133 /**
134 * This method create a axis element for a given <i>axis</i> and
135 * <i>type</i>. Type can be one of 'domain' or 'range'.
136 *
137 * @param cr The ElementCreator
138 * @param axis The axis that provides range information.
139 * @param dataset The dataset for min/max determination.
140 * @param type The axis type ('domain' or 'range').
141 * @param pos The position in the chart.
142 *
143 * @return An element that contains range information of a given axis.
144 */
145 protected Element createAxisElement(
146 ElementCreator cr,
147 ValueAxis axis,
148 XYDataset dataset,
149 String type,
150 int pos)
151 {
152 logger.debug("createAxisElement " + pos);
153 logger.debug("Axis is from type: " + axis.getClass());
154
155 Element e = cr.create(type);
156 cr.addAttr(e, "pos", String.valueOf(pos), true);
157
158 if (axis instanceof DateAxis) {
159 prepareDateAxisElement(
160 e, cr, (DateAxis) axis, dataset, type, pos);
161 }
162 else {
163 prepareNumberAxisElement(
164 e, cr, (NumberAxis) axis, dataset, type, pos);
165 }
166
167 return e;
168 }
169
170
171 protected Element prepareNumberAxisElement(
172 Element e,
173 ElementCreator cr,
174 NumberAxis axis,
175 XYDataset dataset,
176 String type,
177 int pos
178 ) {
179 Range range = axis.getRange();
180
181 cr.addAttr(e, "from", String.valueOf(range.getLowerBound()), true);
182 cr.addAttr(e, "to", String.valueOf(range.getUpperBound()), true);
183 cr.addAttr(e, "axistype", "number", true);
184
185 Range[] rs = generator.getRangesForAxis(pos);
186 Range r = null;
187
188 if (type.equals("range")) {
189 r = rs[1];
190 }
191 else {
192 r = rs[0];
193 }
194
195 cr.addAttr(e, "min", String.valueOf(r.getLowerBound()), true);
196 cr.addAttr(e, "max", String.valueOf(r.getUpperBound()), true);
197
198 return e;
199 }
200
201
202 protected Element prepareDateAxisElement(
203 Element e,
204 ElementCreator cr,
205 DateAxis axis,
206 XYDataset dataset,
207 String type,
208 int pos
209 ) {
210 Date from = axis.getMinimumDate();
211 Date to = axis.getMaximumDate();
212
213 Bounds bounds = null;
214 if (type.equals("range")) {
215 bounds = generator.getYBounds(pos);
216 }
217 else {
218 bounds = generator.getXBounds(pos);
219 }
220
221 cr.addAttr(e, "axistype", "date", true);
222 cr.addAttr(e, "from", String.valueOf(from.getTime()), true);
223 cr.addAttr(e, "to", String.valueOf(to.getTime()), true);
224
225 cr.addAttr(e, "min", bounds.getLower().toString(), true);
226 cr.addAttr(e, "max", bounds.getUpper().toString(), true);
227
228 return e;
229 }
230
231
232 /**
233 * This method appends the values of a transformation matrix to transform
234 * image pixel coordinates into chart coordinates.
235 *
236 * @param cr The ElementCreator.
237 * @param chart The chart object.
238 * @param info The ChartRenderingInfo that is filled while chart creation.
239 *
240 * @return an element that contains one or more transformation matrix.
241 */
242 protected Element createTransformationElements(
243 ElementCreator cr,
244 JFreeChart chart,
245 ChartRenderingInfo info)
246 {
247 logger.debug("InfoGeneratorHelper.createTransformationElements");
248
249 Element tf = cr.create("transformation-matrix");
250
251 Rectangle2D dataArea = info.getPlotInfo().getDataArea();
252
253 XYPlot plot = (XYPlot) chart.getPlot();
254 ValueAxis xAxis = plot.getDomainAxis();
255
256 if (xAxis == null) {
257 logger.error("There is no x axis in the chart!");
258 return null;
259 }
260
261 for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
262 ValueAxis yAxis = plot.getRangeAxis(i);
263
264 if (yAxis == null) {
265 logger.warn("No y axis at pos " + i + " existing.");
266 continue;
267 }
268
269 Element matrix = createTransformationElement(
270 cr, xAxis, yAxis, dataArea, i);
271
272 tf.appendChild(matrix);
273 }
274
275 return tf;
276 }
277
278
279 /**
280 * Creates an element that contains values used to transform coordinates
281 * of a coordinate system A into a coordinate system B.
282 *
283 * @param cr The ElementCreator.
284 * @param xAxis The x axis of the target coordinate system.
285 * @param yAxis The y axis of the target coordinate system.
286 * @param dataArea The pixel coordinates of the chart image.
287 * @param pos The dataset position.
288 *
289 * @return an element that contains transformation matrix values.
290 */
291 protected Element createTransformationElement(
292 ElementCreator cr,
293 ValueAxis xAxis,
294 ValueAxis yAxis,
295 Rectangle2D dataArea,
296 int pos)
297 {
298 double[] tm = createTransformationMatrix(dataArea, xAxis, yAxis);
299
300 Element matrix = cr.create("matrix");
301
302 cr.addAttr(matrix, "pos", String.valueOf(pos), true);
303 cr.addAttr(matrix, "sx", String.valueOf(tm[0]), true);
304 cr.addAttr(matrix, "sy", String.valueOf(tm[1]), true);
305 cr.addAttr(matrix, "tx", String.valueOf(tm[2]), true);
306 cr.addAttr(matrix, "ty", String.valueOf(tm[3]), true);
307
308 if (xAxis instanceof DateAxis) {
309 cr.addAttr(matrix, "xtype", "date", true);
310 }
311 else {
312 cr.addAttr(matrix, "xtype", "number", true);
313 }
314
315 if (yAxis instanceof DateAxis) {
316 cr.addAttr(matrix, "ytype", "date", true);
317 }
318 else {
319 cr.addAttr(matrix, "ytype", "number", true);
320 }
321
322 return matrix;
323 }
324
325
326 /**
327 * This method determines a transformation matrix to transform pixel
328 * coordinates of the chart image into chart coordinates.
329 *
330 * @param dataArea The rectangle that contains the data points of the chart.
331 * @param xAxis The x axis.
332 * @param yAxis The y axis.
333 *
334 * @return a double array as follows: [sx, sy, tx, ty].
335 */
336 protected static double[] createTransformationMatrix(
337 Rectangle2D dataArea,
338 ValueAxis xAxis,
339 ValueAxis yAxis)
340 {
341 logger.debug("InfoGeneratorHelper.createTransformationMatrix");
342
343 double offsetX = dataArea.getX();
344 double width = dataArea.getWidth();
345 double offsetY = dataArea.getY();
346 double height = dataArea.getHeight();
347
348 Range xRange = getRangeFromAxis(xAxis);
349 Range yRange = getRangeFromAxis(yAxis);
350
351 double lowerX = xRange.getLowerBound();
352 double upperX = xRange.getUpperBound();
353 double lowerY = yRange.getLowerBound();
354 double upperY = yRange.getUpperBound();
355
356 if (xAxis.isInverted()) {
357 logger.info("X-Axis is inverted!");
358
359 double tmp = upperX;
360 upperX = lowerX;
361 lowerX = tmp;
362 }
363
364 double dMoveX = upperX - lowerX;
365 double fMoveX = width * lowerX;
366 double dMoveY = lowerY - upperY;
367 double fMoveY = height * upperY;
368
369 AffineTransform t1 = AffineTransform.getTranslateInstance(
370 offsetX - ( fMoveX / dMoveX ),
371 offsetY - ( fMoveY / dMoveY ) );
372
373 AffineTransform t2 = AffineTransform.getScaleInstance(
374 width / (upperX - lowerX),
375 height / (lowerY - upperY));
376
377 t1.concatenate(t2);
378
379 try {
380 t1.invert();
381
382 double[] c = new double[6];
383 t1.getMatrix(c);
384
385 return new double[] { c[0], c[3], c[4], c[5] };
386 }
387 catch (NoninvertibleTransformException e) {
388 // do nothing
389 logger.warn("Matrix is not invertible.");
390 }
391
392 return new double[] { 1d, 1d, 0d, 0d };
393 }
394
395
396 protected static Range getRangeFromAxis(ValueAxis axis) {
397 if (axis instanceof DateAxis) {
398 DateAxis dAxis = (DateAxis) axis;
399 Date min = dAxis.getMinimumDate();
400 Date max = dAxis.getMaximumDate();
401
402 return new Range(min.getTime(), max.getTime());
403 }
404 else {
405 return axis.getRange();
406 }
407 }
408 }
409 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org