comparison artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java @ 5838:5aa05a7a34b7

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

http://dive4elements.wald.intevation.org