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 };

http://dive4elements.wald.intevation.org