comparison flys-artifacts/src/main/java/de/intevation/flys/jfree/EnhancedLineAndShapeRenderer.java @ 2793:6310b1582f2d

merged flys-artifacts/2.7
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:30 +0200
parents bdc86e61428c
children 9cea02b51241
comparison
equal deleted inserted replaced
2548:ada02bbd3b7f 2793:6310b1582f2d
1 package de.intevation.flys.jfree;
2
3 import java.awt.Color;
4 import java.awt.Graphics2D;
5 import java.awt.Font;
6 import java.awt.Paint;
7 import java.awt.Shape;
8 import java.awt.geom.Rectangle2D;
9 import java.util.HashMap;
10 import java.util.Map;
11
12 import org.apache.log4j.Logger;
13
14 import org.jfree.chart.axis.ValueAxis;
15 import org.jfree.chart.entity.EntityCollection;
16 import org.jfree.chart.plot.CrosshairState;
17 import org.jfree.chart.plot.PlotOrientation;
18 import org.jfree.chart.plot.XYPlot;
19 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
20 import org.jfree.data.xy.XYDataset;
21 import org.jfree.data.xy.XYSeries;
22 import org.jfree.data.xy.XYSeriesCollection;
23 import org.jfree.ui.RectangleEdge;
24 import org.jfree.util.BooleanList;
25 import org.jfree.util.ShapeUtilities;
26
27 import de.intevation.flys.jfree.HasLabel;
28
29
30 /**
31 * Renderer with additional the additional functionality of renderering minima
32 * and/or maxima of dataseries contained in datasets.
33 */
34 public class EnhancedLineAndShapeRenderer extends XYLineAndShapeRenderer {
35
36 /** Own logger. */
37 private static final Logger logger =
38 Logger.getLogger(EnhancedLineAndShapeRenderer.class);
39
40 protected BooleanList isMinimumShapeVisible;
41 protected BooleanList isMaximumShapeVisible;
42 protected BooleanList showLineLabel;
43
44 protected Map<Integer, Double> seriesMinimum;
45 protected Map<Integer, Double> seriesMinimumX;
46 protected Map<Integer, Double> seriesMaximum;
47
48 protected Map<Integer, Font> lineLabelFonts;
49 protected Map<Integer, Color> lineLabelTextColors;
50 protected BooleanList showLineLabelBG;
51 protected Map<Integer, Color> lineLabelBGColors;
52
53
54 public EnhancedLineAndShapeRenderer(boolean lines, boolean shapes) {
55 super(lines, shapes);
56 this.isMinimumShapeVisible = new BooleanList();
57 this.isMaximumShapeVisible = new BooleanList();
58 this.showLineLabel = new BooleanList();
59 this.showLineLabelBG = new BooleanList();
60 this.seriesMinimum = new HashMap<Integer, Double>();
61 this.seriesMaximum = new HashMap<Integer, Double>();
62 this.seriesMinimumX = new HashMap<Integer, Double>();
63 this.lineLabelFonts = new HashMap<Integer, Font>();
64 this.lineLabelTextColors = new HashMap<Integer, Color>();
65 this.lineLabelBGColors = new HashMap<Integer, Color>();
66 }
67
68
69 /** Draw a background-box of a text to render. */
70 public static void drawTextBox(Graphics2D g2, String text, float textX, float textY, Color bgColor) {
71 Rectangle2D hotspotBox = g2.getFontMetrics().getStringBounds(text, g2);
72 float w = (float) hotspotBox.getWidth(), h = (float) hotspotBox.getHeight();
73 hotspotBox.setRect(textX, textY-h, w, h);
74 Color oldColor = g2.getColor();
75 g2.setColor(bgColor);
76 g2.fill(hotspotBox);
77 g2.setColor(oldColor);
78 }
79
80
81 public boolean getItemShapeVisible(XYDataset dataset, int series, int item){
82 if (super.getItemShapeVisible(series, item)) {
83 return true;
84 }
85
86 if (isMinimumShapeVisible(series) && isMinimum(dataset, series, item)) {
87 return true;
88 }
89
90 if (isMaximumShapeVisible(series) && isMaximum(dataset, series, item)) {
91 return true;
92 }
93
94 return false;
95 }
96
97
98 public Shape getMaximumShape(int series, int column) {
99 return new Rectangle2D.Double(-5d, -5d, 10d, 10d);
100 }
101
102
103 public Shape getMinimumShape(int series, int column) {
104 return new Rectangle2D.Double(-5d, -5d, 10d, 10d);
105 }
106
107
108 public Paint getMaximumFillPaint(int series, int column) {
109 Paint p = getItemPaint(series, column);
110
111 if (p instanceof Color) {
112 Color c = (Color) p;
113 Color b = c;
114
115 for (int i = 0; i < 2; i++) {
116 b = b.darker();
117 }
118
119 return b;
120 }
121
122 logger.warn("Item paint is no instance of Color!");
123 return p;
124 }
125
126
127 public Paint getMinimumFillPaint(int series, int column) {
128 Paint p = getItemPaint(series, column);
129
130 if (p instanceof Color) {
131 Color c = (Color) p;
132 Color b = c;
133
134 for (int i = 0; i < 2; i++) {
135 b = b.darker();
136 }
137
138 return b;
139 }
140
141 logger.warn("Item paint is no instance of Color!");
142 return p;
143 }
144
145
146 /**
147 * Overrides XYLineAndShapeRenderer.drawSecondaryPass() to call an adapted
148 * method getItemShapeVisible() which now takes an XYDataset. So, 99% of
149 * code equal the code in XYLineAndShapeRenderer.
150 */
151 @Override
152 protected void drawSecondaryPass(
153 Graphics2D g2,
154 XYPlot plot,
155 XYDataset dataset,
156 int pass,
157 int series,
158 int item,
159 ValueAxis domainAxis,
160 Rectangle2D dataArea,
161 ValueAxis rangeAxis,
162 CrosshairState crosshairState,
163 EntityCollection entities
164 ) {
165 Shape entityArea = null;
166
167 // get the data point...
168 double x1 = dataset.getXValue(series, item);
169 double y1 = dataset.getYValue(series, item);
170 if (Double.isNaN(y1) || Double.isNaN(x1)) {
171 return;
172 }
173
174 PlotOrientation orientation = plot.getOrientation();
175 RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
176 RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
177 double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
178 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
179
180 if (getItemShapeVisible(dataset, series, item)) {
181 Shape shape = null;
182
183 // OPTIMIZE: instead of calculating minimum and maximum for every
184 // point, calculate it just once (assume that dataset
185 // content does not change during rendering).
186 // NOTE: Above OPTIMIZE might already be fulfilled to most extend.
187 boolean isMinimum = isMinimumShapeVisible(series)
188 && isMinimum(dataset, series, item);
189
190 boolean isMaximum = isMaximumShapeVisible(series)
191 && isMaximum(dataset, series, item);
192
193 if (isMinimum) {
194 logger.debug("Create a Minimum shape.");
195 shape = getMinimumShape(series, item);
196 }
197 else if (isMaximum) {
198 logger.debug("Create a Maximum shape.");
199 shape = getMaximumShape(series, item);
200 }
201 else {
202 shape = getItemShape(series, item);
203 }
204
205 if (orientation == PlotOrientation.HORIZONTAL) {
206 shape = ShapeUtilities.createTranslatedShape(shape, transY1,
207 transX1);
208 }
209 else if (orientation == PlotOrientation.VERTICAL) {
210 shape = ShapeUtilities.createTranslatedShape(shape, transX1,
211 transY1);
212 }
213 entityArea = shape;
214 if (shape.intersects(dataArea)) {
215 if (getItemShapeFilled(series, item)) {
216 if (getUseFillPaint()) {
217 g2.setPaint(getItemFillPaint(series, item));
218 }
219 else {
220 g2.setPaint(getItemPaint(series, item));
221 }
222 g2.fill(shape);
223 }
224 if (getDrawOutlines()) {
225 if (getUseOutlinePaint()) {
226 g2.setPaint(getItemOutlinePaint(series, item));
227 }
228 else {
229 g2.setPaint(getItemPaint(series, item));
230 }
231 g2.setStroke(getItemOutlineStroke(series, item));
232 g2.draw(shape);
233 }
234
235 if (isMinimum) {
236 g2.setPaint(getMinimumFillPaint(series, item));
237 g2.fill(shape);
238 g2.setPaint(getItemOutlinePaint(series, item));
239 g2.setStroke(getItemOutlineStroke(series, item));
240 g2.draw(shape);
241 }
242 else if (isMaximum) {
243 g2.setPaint(getMaximumFillPaint(series, item));
244 g2.fill(shape);
245 g2.setPaint(getItemOutlinePaint(series, item));
246 g2.setStroke(getItemOutlineStroke(series, item));
247 g2.draw(shape);
248 }
249 }
250 } // if (getItemShapeVisible(dataset, series, item))
251
252 double xx = transX1;
253 double yy = transY1;
254 if (orientation == PlotOrientation.HORIZONTAL) {
255 xx = transY1;
256 yy = transX1;
257 }
258
259 // draw the item label if there is one...
260 if (isItemLabelVisible(series, item)) {
261 drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
262 (y1 < 0.0));
263 }
264
265 // Draw label of line.
266 if (isShowLineLabel(series) && isMinimumX (dataset, series, item)) {
267 XYSeries xYSeries = ((XYSeriesCollection) dataset).getSeries(series);
268 String waterlevelLabel = (xYSeries instanceof HasLabel)
269 ? ((HasLabel)xYSeries).getLabel()
270 : xYSeries.getKey().toString();
271 // TODO Force water of some German rivers to flow direction mountains.
272 Font oldFont = g2.getFont();
273
274 Color oldColor = g2.getColor();
275 g2.setFont(this.getLineLabelFont(series));
276 g2.setColor(this.getLineLabelTextColor(series));
277 g2.setBackground(Color.black);
278
279 // Fill background.
280 if (isShowLineLabelBG(series)) {
281 drawTextBox(g2, waterlevelLabel, (float)xx, (float)yy-3f,
282 getLineLabelBGColor(series));
283 }
284
285 g2.drawString(waterlevelLabel, (float)xx, (float)yy-3f);
286
287 g2.setFont(oldFont);
288 g2.setColor(oldColor);
289 }
290
291 int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
292 int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
293 updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
294 rangeAxisIndex, transX1, transY1, orientation);
295
296 // add an entity for the item, but only if it falls within the data
297 // area...
298 if (entities != null && isPointInRect(dataArea, xx, yy)) {
299 addEntity(entities, entityArea, dataset, series, item, xx, yy);
300 }
301 }
302
303
304 public void setIsMinimumShapeVisisble(int series, boolean isVisible) {
305 this.isMinimumShapeVisible.setBoolean(series, isVisible);
306 }
307
308
309 public boolean isMinimumShapeVisible(int series) {
310 if (this.isMinimumShapeVisible.size() <= series) {
311 return false;
312 }
313
314 return isMinimumShapeVisible.getBoolean(series);
315 }
316
317
318 public void setIsMaximumShapeVisible(int series, boolean isVisible) {
319 this.isMaximumShapeVisible.setBoolean(series, isVisible);
320 }
321
322
323 public boolean isMaximumShapeVisible(int series) {
324 if (this.isMaximumShapeVisible.size() <= series) {
325 return false;
326 }
327
328 return isMaximumShapeVisible.getBoolean(series);
329 }
330
331 /** Whether or not a label should be shown for series. */
332 public boolean isShowLineLabel(int series) {
333 if (this.showLineLabel.size() <= series) {
334 return false;
335 }
336
337 return showLineLabel.getBoolean(series);
338 }
339
340
341 /** Sets whether or not a label should be shown for series. */
342 public void setShowLineLabel(boolean showLineLabel, int series) {
343 this.showLineLabel.setBoolean(series, showLineLabel);
344 }
345
346 /** Whether or not a label should be shown for series. */
347 public boolean isShowLineLabelBG(int series) {
348 if (this.showLineLabelBG.size() <= series) {
349 return false;
350 }
351
352 return showLineLabelBG.getBoolean(series);
353 }
354
355 public void setShowLineLabelBG(int series, boolean doShow) {
356 this.showLineLabelBG.setBoolean(series, doShow);
357 }
358
359 public Color getLineLabelBGColor(int series) {
360 if (this.lineLabelBGColors.size() <= series) {
361 return null;
362 }
363
364 return this.lineLabelBGColors.get(series);
365 }
366
367 public void setLineLabelBGColor(int series, Color color) {
368 this.lineLabelBGColors.put(series, color);
369 }
370
371 public Color getLineLabelTextColor(int series) {
372 if (this.lineLabelTextColors.size() <= series) {
373 return null;
374 }
375
376 return this.lineLabelTextColors.get(series);
377 }
378
379 public void setLineLabelTextColor(int series, Color color) {
380 this.lineLabelTextColors.put(series, color);
381 }
382
383 public void setLineLabelFont(Font font, int series) {
384 this.lineLabelFonts.put(series, font);
385 }
386
387 public Font getLineLabelFont(int series) {
388 return this.lineLabelFonts.get(series);
389 }
390
391
392 /**
393 * True if the given item of given dataset has the smallest
394 * X value within this set.
395 */
396 public boolean isMinimumX(XYDataset dataset, int series, int item) {
397 return dataset.getXValue(series, item) == getMinimumX(dataset, series);
398 }
399
400
401 /**
402 * Get Minimum X Value of a given series in a dataset.
403 * The value is stored for later use if queried the first time.
404 */
405 public double getMinimumX(XYDataset dataset, int series) {
406 Integer key = Integer.valueOf(series);
407 Double old = seriesMinimumX.get(key);
408
409 if (old != null) {
410 return old.doubleValue();
411 }
412
413 logger.debug("Compute minimum of Series: " + series);
414
415 double min = Double.MAX_VALUE;
416
417 for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
418 double tmpValue = dataset.getXValue(series, i);
419
420 if (tmpValue < min) {
421 min = tmpValue;
422 }
423 }
424
425 seriesMinimumX.put(key, Double.valueOf(min));
426
427 return min;
428 }
429
430
431 /**
432 * True if the given item of given dataset has the smallest
433 * Y value within this set.
434 */
435 public boolean isMinimum(XYDataset dataset, int series, int item) {
436 return dataset.getYValue(series, item) == getMinimum(dataset, series);
437 }
438
439
440 /**
441 * Get Minimum Y Value of a given series in a dataset.
442 * The value is stored for later use if queried the first time.
443 */
444 public double getMinimum(XYDataset dataset, int series) {
445 Integer key = Integer.valueOf(series);
446 Double old = seriesMinimum.get(key);
447
448 if (old != null) {
449 return old.doubleValue();
450 }
451
452 logger.debug("Compute minimum of Series: " + series);
453
454 double min = Double.MAX_VALUE;
455
456 for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
457 double tmpValue = dataset.getYValue(series, i);
458
459 if (tmpValue < min) {
460 min = tmpValue;
461 }
462 }
463
464 seriesMinimum.put(key, Double.valueOf(min));
465
466 return min;
467 }
468
469
470 /**
471 * True if the given item of given dataset has the biggest
472 * Y value within this set.
473 */
474 public boolean isMaximum(XYDataset dataset, int series, int item) {
475 return dataset.getYValue(series, item) == getMaximum(dataset, series);
476 }
477
478
479 /**
480 * Get maximum Y Value of a given series in a dataset.
481 * The value is stored for later use if queried the first time.
482 */
483 public double getMaximum(XYDataset dataset, int series) {
484 Integer key = Integer.valueOf(series);
485 Double old = seriesMaximum.get(key);
486
487 if (old != null) {
488 return old.doubleValue();
489 }
490
491 logger.debug("Compute maximum of Series: " + series);
492
493 double max = -Double.MAX_VALUE;
494
495 for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
496 double tmpValue = dataset.getYValue(series, i);
497
498 if (tmpValue > max) {
499 max = tmpValue;
500 }
501 }
502
503 seriesMaximum.put(key, Double.valueOf(max));
504
505 return max;
506 }
507 }
508 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org