Mercurial > dive4elements > river
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 : |