Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java @ 7043:06a9a241faac generator-refactoring
Factor out annotation handling code
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Wed, 18 Sep 2013 17:12:13 +0200 |
parents | |
children | 35aabc86566d |
comparison
equal
deleted
inserted
replaced
7042:599d3c48474c | 7043:06a9a241faac |
---|---|
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 package org.dive4elements.river.jfree; | |
9 | |
10 import org.dive4elements.river.artifacts.model.HYKFactory; | |
11 import org.dive4elements.river.themes.ThemeDocument; | |
12 | |
13 import java.util.Collections; | |
14 import java.util.List; | |
15 import java.util.Map; | |
16 | |
17 import java.awt.BasicStroke; | |
18 import java.awt.Color; | |
19 import java.awt.Font; | |
20 | |
21 import org.jfree.ui.TextAnchor; | |
22 import org.jfree.chart.plot.XYPlot; | |
23 import org.jfree.chart.LegendItem; | |
24 import org.jfree.chart.LegendItemCollection; | |
25 import org.jfree.chart.annotations.XYTextAnnotation; | |
26 import org.jfree.chart.annotations.XYLineAnnotation; | |
27 | |
28 import org.dive4elements.river.themes.LineStyle; | |
29 import org.dive4elements.river.themes.TextStyle; | |
30 import org.dive4elements.river.exports.ChartSettings; | |
31 import org.dive4elements.river.exports.LegendSection; | |
32 import org.dive4elements.river.exports.ChartArea; | |
33 | |
34 import org.apache.log4j.Logger; | |
35 | |
36 /** Annotation helper class, handles plotting of annotations. */ | |
37 public class AnnotationHelper { | |
38 private static final Logger logger = Logger.getLogger(AnnotationHelper.class); | |
39 | |
40 protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f; | |
41 | |
42 /* arr this would be better in chartsettings */ | |
43 public static final int DEFAULT_FONT_SIZE = 12; | |
44 public static final String DEFAULT_FONT_NAME = "Tahoma"; | |
45 | |
46 /** | |
47 * Add annotations (Sticky, Text and hyk zones) to a plot. | |
48 * @param annotations Annotations to add | |
49 * @param plot Plot to add annotations to. | |
50 * @param settings ChartSettings object for settings. | |
51 */ | |
52 public static void addAnnotationsToRenderer(List<RiverAnnotation> annotations, | |
53 XYPlot plot, ChartSettings settings, Map<Integer, AxisDataset> datasets) { | |
54 logger.debug("addAnnotationsToRenderer"); | |
55 | |
56 if (annotations == null || annotations.isEmpty()) { | |
57 logger.debug("addAnnotationsToRenderer: no annotations."); | |
58 return; | |
59 } | |
60 | |
61 // OPTMIMIZE: Pre-calculate positions | |
62 ChartArea area = new ChartArea( | |
63 plot.getDomainAxis(0).getRange(), | |
64 plot.getRangeAxis().getRange()); | |
65 | |
66 // Walk over all Annotation sets. | |
67 for (RiverAnnotation fa: annotations) { | |
68 | |
69 // Access text styling, if any. | |
70 ThemeDocument theme = fa.getTheme(); | |
71 TextStyle textStyle = null; | |
72 LineStyle lineStyle = null; | |
73 | |
74 // Get Themeing information and add legend item. | |
75 if (theme != null) { | |
76 textStyle = theme.parseComplexTextStyle(); | |
77 lineStyle = theme.parseComplexLineStyle(); | |
78 if (fa.getLabel() != null) { | |
79 // Legend handling, maybe misplaced? | |
80 LegendItemCollection lic = new LegendItemCollection(); | |
81 LegendItemCollection old = plot.getFixedLegendItems(); | |
82 | |
83 Color color = theme.parseLineColorField(); | |
84 if (color == null) { | |
85 color = Color.BLACK; | |
86 } | |
87 | |
88 LegendItem newItem = new LegendItem(fa.getLabel(), color); | |
89 LegendSection ls = settings.getLegendSection(); | |
90 newItem.setLabelFont (new Font( | |
91 DEFAULT_FONT_NAME, | |
92 Font.PLAIN, | |
93 ls != null ? ls.getFontSize() : null) | |
94 ); | |
95 | |
96 lic.add(newItem); | |
97 // (Re-)Add prior legend entries. | |
98 if (old != null) { | |
99 old.addAll(lic); | |
100 } | |
101 else { | |
102 old = lic; | |
103 } | |
104 plot.setFixedLegendItems(old); | |
105 } | |
106 } | |
107 | |
108 // The 'Sticky' Annotations (at axis, with line and text). | |
109 for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) { | |
110 addStickyAnnotation( | |
111 sta, plot, area, lineStyle, textStyle, theme, | |
112 datasets.get(new Integer(sta.getAxisSymbol()))); | |
113 } | |
114 | |
115 // Other Text Annotations (e.g. labels of (manual) points). | |
116 for (XYTextAnnotation ta: fa.getTextAnnotations()) { | |
117 // Style the text. | |
118 if (textStyle != null) { | |
119 textStyle.apply(ta); | |
120 } | |
121 ta.setY(area.above(0.05d, ta.getY())); | |
122 plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND); | |
123 } | |
124 } | |
125 } | |
126 | |
127 /** | |
128 * Add a text and a line annotation. | |
129 * @param area convenience to determine positions in plot. | |
130 * @param theme (optional) theme document | |
131 */ | |
132 public static void addStickyAnnotation( | |
133 StickyAxisAnnotation annotation, | |
134 XYPlot plot, | |
135 ChartArea area, | |
136 LineStyle lineStyle, | |
137 TextStyle textStyle, | |
138 ThemeDocument theme, | |
139 AxisDataset dataset | |
140 ) { | |
141 // OPTIMIZE pre-calculate area-related values | |
142 final float TEXT_OFF = 0.03f; | |
143 | |
144 XYLineAnnotation lineAnnotation = null; | |
145 XYTextAnnotation textAnnotation = null; | |
146 | |
147 int rendererIndex = 0; | |
148 | |
149 if (annotation.atX()) { | |
150 textAnnotation = new CollisionFreeXYTextAnnotation( | |
151 annotation.getText(), annotation.getPos(), area.ofGround(TEXT_OFF)); | |
152 // OPTIMIZE externalize the calculation involving PI. | |
153 //textAnnotation.setRotationAngle(270f*Math.PI/180f); | |
154 lineAnnotation = createGroundStickAnnotation( | |
155 area, annotation.getPos(), lineStyle); | |
156 textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); | |
157 textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); | |
158 } | |
159 else { | |
160 // Do the more complicated case where we stick to the Y-Axis. | |
161 // There is one nasty case (duration curves, where annotations | |
162 // might stick to the second y-axis). | |
163 if (dataset == null) { | |
164 logger.warn("Annotation should stick to unfindable y-axis: " | |
165 + annotation.getAxisSymbol()); | |
166 rendererIndex = 0; | |
167 } | |
168 else { | |
169 rendererIndex = dataset.getPlotAxisIndex(); | |
170 } | |
171 | |
172 // Stick to the "right" (opposed to left) Y-Axis. | |
173 if (rendererIndex != 0) { | |
174 // OPTIMIZE: Pass a different area to this function, | |
175 // do the adding to renderer outside (let this | |
176 // function return the annotations). | |
177 // Note that this path is travelled rarely. | |
178 ChartArea area2 = new ChartArea(plot.getDomainAxis(), plot.getRangeAxis(rendererIndex)); | |
179 textAnnotation = new CollisionFreeXYTextAnnotation( | |
180 annotation.getText(), area2.ofRight(TEXT_OFF), annotation.getPos()); | |
181 textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT); | |
182 textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT); | |
183 lineAnnotation = createRightStickAnnotation( | |
184 area2, annotation.getPos(), lineStyle); | |
185 if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { | |
186 // New line annotation to hit curve. | |
187 if (theme.parseShowVerticalLine()) { | |
188 XYLineAnnotation hitLineAnnotation = | |
189 createStickyLineAnnotation( | |
190 StickyAxisAnnotation.SimpleAxis.X_AXIS, | |
191 annotation.getHitPoint(), annotation.getPos(),// annotation.getHitPoint(), | |
192 area2, lineStyle); | |
193 plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, | |
194 org.jfree.ui.Layer.BACKGROUND); | |
195 } | |
196 if (theme.parseShowHorizontalLine()) { | |
197 XYLineAnnotation lineBackAnnotation = | |
198 createStickyLineAnnotation( | |
199 StickyAxisAnnotation.SimpleAxis.Y_AXIS2, | |
200 annotation.getPos(), annotation.getHitPoint(), | |
201 area2, lineStyle); | |
202 plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, | |
203 org.jfree.ui.Layer.BACKGROUND); | |
204 } | |
205 } | |
206 } | |
207 else { // Stick to the left y-axis. | |
208 textAnnotation = new CollisionFreeXYTextAnnotation( | |
209 annotation.getText(), area.ofLeft(TEXT_OFF), annotation.getPos()); | |
210 textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); | |
211 textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); | |
212 lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle); | |
213 if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { | |
214 // New line annotation to hit curve. | |
215 if (theme.parseShowHorizontalLine()) { | |
216 XYLineAnnotation hitLineAnnotation = | |
217 createStickyLineAnnotation( | |
218 StickyAxisAnnotation.SimpleAxis.Y_AXIS, | |
219 annotation.getPos(), annotation.getHitPoint(), | |
220 area, lineStyle); | |
221 plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, | |
222 org.jfree.ui.Layer.BACKGROUND); | |
223 } | |
224 if (theme.parseShowVerticalLine()) { | |
225 XYLineAnnotation lineBackAnnotation = | |
226 createStickyLineAnnotation( | |
227 StickyAxisAnnotation.SimpleAxis.X_AXIS, | |
228 annotation.getHitPoint(), annotation.getPos(), | |
229 area, lineStyle); | |
230 plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, | |
231 org.jfree.ui.Layer.BACKGROUND); | |
232 } | |
233 } | |
234 } | |
235 } | |
236 | |
237 // Style the text. | |
238 if (textStyle != null) { | |
239 textStyle.apply(textAnnotation); | |
240 } | |
241 | |
242 // Add the Annotations to renderer. | |
243 plot.getRenderer(rendererIndex).addAnnotation(textAnnotation, | |
244 org.jfree.ui.Layer.FOREGROUND); | |
245 plot.getRenderer(rendererIndex).addAnnotation(lineAnnotation, | |
246 org.jfree.ui.Layer.FOREGROUND); | |
247 } | |
248 | |
249 /** | |
250 * Create annotation that sticks to "ground" (X) axis. | |
251 * @param area helper to calculate coordinates | |
252 * @param pos one-dimensional position (distance from axis) | |
253 * @param lineStyle the line style to use for the line. | |
254 */ | |
255 public static XYLineAnnotation createGroundStickAnnotation( | |
256 ChartArea area, float pos, LineStyle lineStyle | |
257 ) { | |
258 // Style the line. | |
259 if (lineStyle != null) { | |
260 return new XYLineAnnotation( | |
261 pos, area.atGround(), | |
262 pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET), | |
263 new BasicStroke(lineStyle.getWidth()),lineStyle.getColor()); | |
264 } | |
265 else { | |
266 return new XYLineAnnotation( | |
267 pos, area.atGround(), | |
268 pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET)); | |
269 } | |
270 } | |
271 | |
272 | |
273 /** | |
274 * Create annotation that sticks to the second Y axis ("right"). | |
275 * @param area helper to calculate coordinates | |
276 * @param pos one-dimensional position (distance from axis) | |
277 * @param lineStyle the line style to use for the line. | |
278 */ | |
279 public static XYLineAnnotation createRightStickAnnotation( | |
280 ChartArea area, float pos, LineStyle lineStyle | |
281 ) { | |
282 // Style the line. | |
283 if (lineStyle != null) { | |
284 return new XYLineAnnotation( | |
285 area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos, | |
286 area.atRight(), pos, | |
287 new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); | |
288 } | |
289 else { | |
290 return new XYLineAnnotation( | |
291 area.atRight(), pos, | |
292 area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos); | |
293 } | |
294 } | |
295 /** | |
296 * Create annotation that sticks to the first Y axis ("left"). | |
297 * @param area helper to calculate coordinates | |
298 * @param pos one-dimensional position (distance from axis) | |
299 * @param lineStyle the line style to use for the line. | |
300 */ | |
301 public static XYLineAnnotation createLeftStickAnnotation( | |
302 ChartArea area, float pos, LineStyle lineStyle | |
303 ) { | |
304 // Style the line. | |
305 if (lineStyle != null) { | |
306 return new XYLineAnnotation( | |
307 area.atLeft(), pos, | |
308 area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos, | |
309 new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); | |
310 } | |
311 else { | |
312 return new XYLineAnnotation( | |
313 area.atLeft(), pos, | |
314 area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos); | |
315 } | |
316 } | |
317 | |
318 | |
319 /** | |
320 * Create a line from a axis to a given point. | |
321 * @param axis The "simple" axis. | |
322 * @param fromD1 from-location in first dimension. | |
323 * @param toD2 to-location in second dimension. | |
324 * @param area helper to calculate offsets. | |
325 * @param lineStyle optional line style. | |
326 */ | |
327 public static XYLineAnnotation createStickyLineAnnotation( | |
328 StickyAxisAnnotation.SimpleAxis axis, float fromD1, float toD2, | |
329 ChartArea area, LineStyle lineStyle | |
330 ) { | |
331 double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d; | |
332 switch(axis) { | |
333 case X_AXIS: | |
334 anchorX1 = fromD1; | |
335 anchorX2 = fromD1; | |
336 anchorY1 = area.atGround(); | |
337 anchorY2 = toD2; | |
338 break; | |
339 case Y_AXIS: | |
340 anchorX1 = area.atLeft(); | |
341 anchorX2 = toD2; | |
342 anchorY1 = fromD1; | |
343 anchorY2 = fromD1; | |
344 break; | |
345 case Y_AXIS2: | |
346 anchorX1 = area.atRight(); | |
347 anchorX2 = toD2; | |
348 anchorY1 = fromD1; | |
349 anchorY2 = fromD1; | |
350 break; | |
351 } | |
352 // Style the line. | |
353 if (lineStyle != null) { | |
354 return new XYLineAnnotation( | |
355 anchorX1, anchorY1, | |
356 anchorX2, anchorY2, | |
357 new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); | |
358 } | |
359 else { | |
360 return new XYLineAnnotation( | |
361 anchorX1, anchorY1, | |
362 anchorX2, anchorY2); | |
363 } | |
364 } | |
365 | |
366 }; |