Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java @ 1190:f514894ec2fd
merged flys-artifacts/2.5
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:17 +0200 |
parents | f1c9bfb07ba7 |
children | f708120cb7bc |
comparison
equal
deleted
inserted
replaced
917:b48c36076e17 | 1190:f514894ec2fd |
---|---|
1 package de.intevation.flys.jfree; | |
2 | |
3 import org.apache.log4j.Logger; | |
4 | |
5 import java.util.Iterator; | |
6 | |
7 import java.awt.Shape; | |
8 import java.awt.geom.Rectangle2D; | |
9 import java.awt.geom.Line2D; | |
10 | |
11 import org.jfree.chart.annotations.XYTextAnnotation; | |
12 import org.jfree.chart.axis.ValueAxis; | |
13 import org.jfree.chart.util.LineUtilities; | |
14 import org.jfree.chart.plot.PlotOrientation; | |
15 import org.jfree.chart.plot.XYPlot; | |
16 import org.jfree.chart.entity.XYAnnotationEntity; | |
17 import org.jfree.chart.plot.PlotRenderingInfo; | |
18 import org.jfree.chart.ChartRenderingInfo; | |
19 import org.jfree.chart.plot.Plot; | |
20 | |
21 import org.jfree.data.Range; | |
22 | |
23 import org.jfree.text.TextUtilities; | |
24 | |
25 import org.jfree.ui.RectangleEdge; | |
26 import org.jfree.ui.TextAnchor; | |
27 | |
28 | |
29 /** | |
30 * Custom Annotations class that is drawn only if no collisions with other | |
31 * already drawn CustomAnnotations in current plot are found. | |
32 * Draws a given text and a line to it from either axis. | |
33 */ | |
34 public class StickyAxisAnnotation extends XYTextAnnotation { | |
35 | |
36 /** Logger for this class. */ | |
37 private static Logger logger = | |
38 Logger.getLogger(StickyAxisAnnotation.class); | |
39 | |
40 /** Simplified view on axes. */ | |
41 public static enum SimpleAxis { | |
42 X_AXIS, /** Usually "horizontal". */ | |
43 Y_AXIS /** Usually "vertical". */ | |
44 } | |
45 | |
46 /** Which axis to stick to. */ | |
47 protected SimpleAxis stickyAxis = SimpleAxis.X_AXIS; | |
48 | |
49 /** The 1-dimensional position of this annotation. */ | |
50 protected float pos; | |
51 | |
52 | |
53 /** | |
54 * Trivial constructor. | |
55 * | |
56 * @param text Text to display. | |
57 * @param x X-position in dataspace (typical horizontal, in km). | |
58 * @param y Y-position in dataspace (typical vertical, in m). | |
59 * @deprecated | |
60 */ | |
61 public StickyAxisAnnotation(String text, float x, float y) { | |
62 super(text, x, y); | |
63 setStickyAxis(SimpleAxis.X_AXIS); | |
64 } | |
65 | |
66 | |
67 /** | |
68 * Constructor with implicit sticky x-axis. | |
69 * @param text the text to display. | |
70 * @param pos the position at which to draw the text and mark. | |
71 */ | |
72 public StickyAxisAnnotation(String text, float pos) { | |
73 this(text, pos, pos, SimpleAxis.X_AXIS); | |
74 } | |
75 | |
76 | |
77 /** | |
78 * Constructor with given explicit axis. | |
79 * @param text the text to display. | |
80 * @param pos the position at which to draw the text and mark. | |
81 * @param stickyAxis the axis at which to stick (and to which 'pos' is | |
82 * relative). | |
83 */ | |
84 public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis) { | |
85 super(text, pos, pos); | |
86 setStickyAxis(stickAxis); | |
87 this.pos = pos; | |
88 } | |
89 | |
90 /** | |
91 * Legacy-Constructor. | |
92 * @deprecated | |
93 */ | |
94 public StickyAxisAnnotation(String text, float x, float y, | |
95 SimpleAxis stickAxis) { | |
96 super(text, x, y); | |
97 setStickyAxis(stickAxis); | |
98 this.pos = x; | |
99 } | |
100 | |
101 | |
102 /** | |
103 * Sets the "sticky axis" (whether to draw annotations at the | |
104 * X- or the Y-Axis. | |
105 * | |
106 * @param stickyAxis axis to stick to. | |
107 */ | |
108 public void setStickyAxis(SimpleAxis stickyAxis) { | |
109 this.stickyAxis = stickyAxis; | |
110 if (stickyAxis == SimpleAxis.X_AXIS) { | |
111 this.setRotationAngle(270f * (Math.PI / 180f)); | |
112 this.setRotationAnchor(TextAnchor.CENTER_LEFT); | |
113 this.setTextAnchor(TextAnchor.CENTER_LEFT); | |
114 } else { | |
115 this.setRotationAngle(0f * (Math.PI / 180f)); | |
116 this.setRotationAnchor(TextAnchor.CENTER_LEFT); | |
117 this.setTextAnchor(TextAnchor.CENTER_LEFT); | |
118 } | |
119 } | |
120 | |
121 | |
122 /** | |
123 * Draws a small line at axis where this annotation resides. | |
124 * | |
125 * @param g2 the graphics device. | |
126 * @param dataArea the data area. | |
127 * @param domainAxis the domain axis. | |
128 * @param rangeAxis the range axis. | |
129 * @param domainEdge the domain edge. | |
130 * @param rangeEdge the range edge. | |
131 * @param orientation the plot orientation. | |
132 */ | |
133 protected void drawAxisMark( | |
134 java.awt.Graphics2D g2, | |
135 java.awt.geom.Rectangle2D dataArea, | |
136 ValueAxis domainAxis, | |
137 ValueAxis rangeAxis, | |
138 RectangleEdge domainEdge, | |
139 RectangleEdge rangeEdge, | |
140 PlotOrientation orientation) { | |
141 float j2DX1 = 0.0f; | |
142 float j2DX2 = 0.0f; | |
143 float j2DY1 = 0.0f; | |
144 float j2DY2 = 0.0f; | |
145 float x = (float) getX(); | |
146 float y = (float) getY(); | |
147 /* When dependent on X/Y-Axis and orientation, following | |
148 can be used as a base: | |
149 if (orientation == PlotOrientation.VERTICAL) { | |
150 j2DX1 = (float) domainAxis.valueToJava2D(x, dataArea, | |
151 domainEdge); | |
152 j2DY1 = (float) rangeAxis.valueToJava2D(y, dataArea, | |
153 rangeEdge); | |
154 j2DX2 = (float) domainAxis.valueToJava2D(x, dataArea, | |
155 domainEdge); | |
156 j2DY2 = (float) rangeAxis.valueToJava2D(y, dataArea, | |
157 rangeEdge); | |
158 } | |
159 else if (orientation == PlotOrientation.HORIZONTAL) { | |
160 j2DY1 = (float) domainAxis.valueToJava2D(x, dataArea, | |
161 domainEdge); | |
162 j2DX1 = (float) rangeAxis.valueToJava2D(y, dataArea, | |
163 rangeEdge); | |
164 j2DY2 = (float) domainAxis.valueToJava2D(x, dataArea, | |
165 domainEdge); | |
166 j2DX2 = (float) rangeAxis.valueToJava2D(y, dataArea, | |
167 rangeEdge); | |
168 } | |
169 | |
170 g2.setPaint(this.paint); | |
171 g2.setStroke(this.stroke); | |
172 */ | |
173 if (this.stickyAxis == SimpleAxis.X_AXIS) { | |
174 j2DY1 = (float) RectangleEdge.coordinate(dataArea, domainEdge); | |
175 double rangeLow = rangeAxis.getRange().getLowerBound(); | |
176 // Line ends at 1.5% of full distance. | |
177 j2DY2 = (float) rangeAxis.valueToJava2D( | |
178 (1f - 0.015f) * rangeLow + 0.015f * | |
179 rangeAxis.getRange().getUpperBound(), | |
180 dataArea, rangeEdge); | |
181 j2DX1 = (float) domainAxis.valueToJava2D(x, dataArea, domainEdge); | |
182 j2DX2 = j2DX1; | |
183 } else { | |
184 j2DX1 = (float) RectangleEdge.coordinate(dataArea, rangeEdge); | |
185 Range domainRange = domainAxis.getRange(); | |
186 double rangeLow = domainRange.getLowerBound(); | |
187 // Line ends at 1.5% of full distance. | |
188 j2DX2 = (float) domainAxis.valueToJava2D( | |
189 (1f - 0.015f) * rangeLow + 0.015f * | |
190 domainRange.getUpperBound(), | |
191 dataArea, domainEdge); | |
192 j2DY1 = (float) rangeAxis.valueToJava2D(pos, dataArea, rangeEdge); | |
193 j2DY2 = j2DY1; | |
194 } | |
195 | |
196 Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2); | |
197 | |
198 // line is clipped to avoid JRE bug 6574155, for more info | |
199 // see JFreeChart bug 2221495 | |
200 boolean visible = LineUtilities.clipLine(line, dataArea); | |
201 if (visible) { | |
202 g2.draw(line); | |
203 } | |
204 } | |
205 | |
206 | |
207 /** | |
208 * Draw the Annotiation if it does not collide with other already drawn | |
209 * Annotations. | |
210 * | |
211 * @param g2 the graphics device. | |
212 * @param plot the plot. | |
213 * @param dataArea the data area. | |
214 * @param domainAxis the domain axis. | |
215 * @param rangeAxis the range axis. | |
216 * @param rendererIndex the render index. | |
217 * @param info state information, escpecially collects info about | |
218 * already drawn shapes (and thus annotations), used | |
219 * for collision detection. | |
220 */ | |
221 @Override | |
222 public void draw( | |
223 java.awt.Graphics2D g2, | |
224 XYPlot plot, | |
225 java.awt.geom.Rectangle2D dataArea, | |
226 ValueAxis domainAxis, | |
227 ValueAxis rangeAxis, | |
228 int rendererIndex, | |
229 PlotRenderingInfo info) { | |
230 | |
231 if (info == null) | |
232 return; | |
233 | |
234 if (domainAxis == null || rangeAxis == null | |
235 || domainAxis.getRange() == null | |
236 || rangeAxis.getRange() == null | |
237 ) { | |
238 logger.error("Annotation cannot be drawn (missing axis)."); | |
239 return; | |
240 } | |
241 | |
242 // Calculate the bounding box. | |
243 ChartRenderingInfo chartInfo = info.getOwner(); | |
244 | |
245 PlotOrientation orientation = plot.getOrientation(); | |
246 RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( | |
247 plot.getDomainAxisLocation(), orientation); | |
248 RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( | |
249 plot.getRangeAxisLocation(), orientation); | |
250 float anchorX = 0f; | |
251 float anchorY = 0.0f; | |
252 if (this.stickyAxis == SimpleAxis.X_AXIS) { | |
253 // Text starts at 1.5% of full distance. | |
254 float rangeLow = (float) rangeAxis.getRange().getLowerBound(); | |
255 float y = rangeLow + 0.02f * ((float) | |
256 rangeAxis.getRange().getUpperBound() - rangeLow); | |
257 setY(y); | |
258 | |
259 anchorX = (float) domainAxis.valueToJava2D( | |
260 getX(), dataArea, domainEdge); | |
261 anchorY = (float) rangeAxis.valueToJava2D( | |
262 getY(), dataArea, rangeEdge); | |
263 } else { | |
264 float rangeLow = (float) domainAxis.getRange().getLowerBound(); | |
265 float x = rangeLow + 0.02f * ((float) | |
266 domainAxis.getRange().getUpperBound() - rangeLow); | |
267 setX(x); | |
268 anchorX = (float) domainAxis.valueToJava2D( | |
269 getX(), dataArea, domainEdge); | |
270 anchorY = (float) rangeAxis.valueToJava2D( | |
271 getY(), dataArea, rangeEdge); | |
272 } | |
273 if (orientation == PlotOrientation.HORIZONTAL) { | |
274 float tempAnchor = anchorX; | |
275 anchorX = anchorY; | |
276 anchorY = tempAnchor; | |
277 } | |
278 | |
279 // Always draw the small line at axis. | |
280 drawAxisMark(g2, dataArea, domainAxis, rangeAxis, domainEdge, | |
281 rangeEdge, orientation); | |
282 | |
283 g2.setFont(getFont()); | |
284 Shape hotspot = TextUtilities.calculateRotatedStringBounds( | |
285 getText(), g2, anchorX, anchorY, getTextAnchor(), | |
286 getRotationAngle(), getRotationAnchor()); | |
287 Rectangle2D hotspotBox = hotspot.getBounds2D(); | |
288 // Check for collisions with other XYAnnotations. | |
289 for (Iterator i = chartInfo.getEntityCollection().iterator(); | |
290 i.hasNext(); ) { | |
291 Object next = i.next(); | |
292 // Collision with other stuff than XYAnnotations are okay. | |
293 if (next instanceof XYAnnotationEntity) { | |
294 XYAnnotationEntity drawnShape = (XYAnnotationEntity) next; | |
295 if (drawnShape.getArea().intersects(hotspotBox)) { | |
296 // Found collision, early stop. | |
297 return; | |
298 } | |
299 } | |
300 } | |
301 | |
302 // Actuall drawing. | |
303 if (getBackgroundPaint() != null) { | |
304 g2.setPaint(getBackgroundPaint()); | |
305 g2.fill(hotspot); | |
306 } | |
307 g2.setPaint(getPaint()); | |
308 TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY, | |
309 getTextAnchor(), getRotationAngle(), getRotationAnchor()); | |
310 // Draw outline. | |
311 if (false) { | |
312 g2.setStroke(getOutlineStroke()); | |
313 g2.setPaint(getOutlinePaint()); | |
314 g2.draw(hotspot); | |
315 } | |
316 | |
317 // Add info that we have drawn this Annotation. | |
318 addEntity(info, hotspot, rendererIndex, getToolTipText(), getURL()); | |
319 } | |
320 } | |
321 | |
322 |