comparison flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java @ 3242:1dca41dba135

Move annotation code to base class ChartGenerator flys-artifacts/trunk@4874 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Christian Lins <christian.lins@intevation.de>
date Wed, 04 Jul 2012 22:28:44 +0000
parents ed07dd55f487
children dd3ddc8ecb14
comparison
equal deleted inserted replaced
3241:da3e58694cae 3242:1dca41dba135
27 import org.w3c.dom.Element; 27 import org.w3c.dom.Element;
28 28
29 import org.jfree.chart.JFreeChart; 29 import org.jfree.chart.JFreeChart;
30 import org.jfree.chart.LegendItem; 30 import org.jfree.chart.LegendItem;
31 import org.jfree.chart.LegendItemCollection; 31 import org.jfree.chart.LegendItemCollection;
32 import org.jfree.chart.annotations.XYBoxAnnotation;
33 import org.jfree.chart.annotations.XYLineAnnotation;
34 import org.jfree.chart.annotations.XYTextAnnotation;
32 import org.jfree.chart.axis.NumberAxis; 35 import org.jfree.chart.axis.NumberAxis;
33 import org.jfree.chart.plot.XYPlot; 36 import org.jfree.chart.plot.XYPlot;
34 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 37 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
35 import org.jfree.data.Range; 38 import org.jfree.data.Range;
36 import org.jfree.data.general.Series; 39 import org.jfree.data.general.Series;
37 import org.jfree.data.xy.XYDataset; 40 import org.jfree.data.xy.XYDataset;
38 import org.jfree.ui.RectangleInsets; 41 import org.jfree.ui.RectangleInsets;
42 import org.jfree.ui.TextAnchor;
39 43
40 import de.intevation.artifacts.Artifact; 44 import de.intevation.artifacts.Artifact;
45 import de.intevation.artifacts.ArtifactNamespaceContext;
41 import de.intevation.artifacts.CallContext; 46 import de.intevation.artifacts.CallContext;
42 import de.intevation.artifacts.CallMeta; 47 import de.intevation.artifacts.CallMeta;
43 import de.intevation.artifacts.PreferredLocale; 48 import de.intevation.artifacts.PreferredLocale;
44 49
45 import de.intevation.artifacts.ArtifactNamespaceContext;
46 import de.intevation.artifacts.common.utils.XMLUtils; 50 import de.intevation.artifacts.common.utils.XMLUtils;
47 51
48 import de.intevation.artifactdatabase.state.ArtifactAndFacet; 52 import de.intevation.artifactdatabase.state.ArtifactAndFacet;
53 import de.intevation.artifactdatabase.state.Facet;
49 import de.intevation.artifactdatabase.state.Settings; 54 import de.intevation.artifactdatabase.state.Settings;
50 55
51 import de.intevation.flys.model.River; 56 import de.intevation.flys.model.River;
52 57
53 import de.intevation.flys.artifacts.FLYSArtifact; 58 import de.intevation.flys.artifacts.FLYSArtifact;
59
54 import de.intevation.flys.artifacts.resources.Resources; 60 import de.intevation.flys.artifacts.resources.Resources;
61
62 import de.intevation.flys.artifacts.model.HYKFactory;
63
55 import de.intevation.flys.jfree.Bounds; 64 import de.intevation.flys.jfree.Bounds;
65 import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation;
56 import de.intevation.flys.jfree.DoubleBounds; 66 import de.intevation.flys.jfree.DoubleBounds;
57 import de.intevation.flys.jfree.EnhancedLineAndShapeRenderer; 67 import de.intevation.flys.jfree.EnhancedLineAndShapeRenderer;
58 import de.intevation.flys.jfree.FLYSAnnotation; 68 import de.intevation.flys.jfree.FLYSAnnotation;
59 import de.intevation.flys.jfree.StableXYDifferenceRenderer; 69 import de.intevation.flys.jfree.StableXYDifferenceRenderer;
70 import de.intevation.flys.jfree.StickyAxisAnnotation;
60 import de.intevation.flys.jfree.StyledAreaSeriesCollection; 71 import de.intevation.flys.jfree.StyledAreaSeriesCollection;
61 import de.intevation.flys.jfree.Style; 72 import de.intevation.flys.jfree.Style;
62 import de.intevation.flys.jfree.StyledSeries; 73 import de.intevation.flys.jfree.StyledSeries;
74
63 import de.intevation.flys.themes.ThemeAccess; 75 import de.intevation.flys.themes.ThemeAccess;
76 import de.intevation.flys.utils.ThemeUtil;
77
64 import de.intevation.flys.utils.FLYSUtils; 78 import de.intevation.flys.utils.FLYSUtils;
65 79
66 80
67 /** 81 /**
68 * The base class for chart creation. It should provide some basic things that 82 * The base class for chart creation. It should provide some basic things that
80 public static final Color DEFAULT_GRID_COLOR = Color.GRAY; 94 public static final Color DEFAULT_GRID_COLOR = Color.GRAY;
81 public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; 95 public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f;
82 public static final int DEFAULT_FONT_SIZE = 12; 96 public static final int DEFAULT_FONT_SIZE = 12;
83 public static final String DEFAULT_FONT_NAME = "Tahoma"; 97 public static final String DEFAULT_FONT_NAME = "Tahoma";
84 98
99 protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f;
85 100
86 public static final String XPATH_CHART_SIZE = 101 public static final String XPATH_CHART_SIZE =
87 "/art:action/art:attributes/art:size"; 102 "/art:action/art:attributes/art:size";
88 103
89 public static final String XPATH_CHART_FORMAT = 104 public static final String XPATH_CHART_FORMAT =
168 annotations = new ArrayList<FLYSAnnotation>(); 183 annotations = new ArrayList<FLYSAnnotation>();
169 } 184 }
170 185
171 annotations.add(annotation); 186 annotations.add(annotation);
172 } 187 }
173 188
189 /**
190 * Add a text and a line annotation.
191 * @param area convenience to determine positions in plot.
192 * @param theme (optional) theme document
193 */
194 protected void addStickyAnnotation(
195 StickyAxisAnnotation annotation,
196 XYPlot plot,
197 ChartArea area,
198 ThemeAccess.LineStyle lineStyle,
199 ThemeAccess.TextStyle textStyle,
200 Document theme
201 ) {
202 // OPTIMIZE pre-calculate area-related values
203 final float TEXT_OFF = 0.03f;
204
205 XYLineAnnotation lineAnnotation = null;
206 XYTextAnnotation textAnnotation = null;
207
208 int rendererIndex = 0;
209
210 if (annotation.atX()) {
211 textAnnotation = new CollisionFreeXYTextAnnotation(
212 annotation.getText(), annotation.getPos(), area.ofGround(TEXT_OFF));
213 // OPTIMIZE externalize the calculation involving PI.
214 //textAnnotation.setRotationAngle(270f*Math.PI/180f);
215 lineAnnotation = createGroundStickAnnotation(
216 area, annotation.getPos(), lineStyle);
217 textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT);
218 textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT);
219 }
220 else {
221 // Do the more complicated case where we stick to the Y-Axis.
222 // There is one nasty case (duration curves, where annotations
223 // might stick to the second y-axis).
224 // FIXME: Remove dependency to XYChartGenerator here
225 AxisDataset dataset = (XYChartGenerator.XYAxisDataset) getAxisDataset(
226 new Integer(annotation.getAxisSymbol()));
227 if (dataset == null) {
228 logger.warn("Annotation should stick to unfindable y-axis: "
229 + annotation.getAxisSymbol());
230 rendererIndex = 0;
231 }
232 else {
233 rendererIndex = dataset.getPlotAxisIndex();
234 }
235
236 // Stick to the "right" (opposed to left) Y-Axis.
237 if (rendererIndex != 0) {
238 // OPTIMIZE: Pass a different area to this function,
239 // do the adding to renderer outside (let this
240 // function return the annotations).
241 // Note that this path is travelled rarely.
242 ChartArea area2 = new ChartArea(plot.getDomainAxis(), plot.getRangeAxis(rendererIndex));
243 textAnnotation = new CollisionFreeXYTextAnnotation(
244 annotation.getText(), area2.ofRight(TEXT_OFF), annotation.getPos());
245 textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT);
246 textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT);
247 lineAnnotation = createRightStickAnnotation(
248 area2, annotation.getPos(), lineStyle);
249 if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
250 // New line annotation to hit curve.
251 if (ThemeUtil.parseShowVerticalLine(theme)) {
252 XYLineAnnotation hitLineAnnotation =
253 createStickyLineAnnotation(
254 StickyAxisAnnotation.SimpleAxis.X_AXIS,
255 annotation.getHitPoint(), annotation.getPos(),// annotation.getHitPoint(),
256 area2, lineStyle);
257 plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation,
258 org.jfree.ui.Layer.BACKGROUND);
259 }
260 if (ThemeUtil.parseShowHorizontalLine(theme)) {
261 XYLineAnnotation lineBackAnnotation =
262 createStickyLineAnnotation(
263 StickyAxisAnnotation.SimpleAxis.Y_AXIS2,
264 annotation.getPos(), annotation.getHitPoint(),
265 area2, lineStyle);
266 plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation,
267 org.jfree.ui.Layer.BACKGROUND);
268 }
269 }
270 }
271 else { // Stick to the left y-axis.
272 textAnnotation = new CollisionFreeXYTextAnnotation(
273 annotation.getText(), area.ofLeft(TEXT_OFF), annotation.getPos());
274 textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT);
275 textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT);
276 lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle);
277 if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
278 // New line annotation to hit curve.
279 if (ThemeUtil.parseShowHorizontalLine(theme)) {
280 XYLineAnnotation hitLineAnnotation =
281 createStickyLineAnnotation(
282 StickyAxisAnnotation.SimpleAxis.Y_AXIS,
283 annotation.getPos(), annotation.getHitPoint(),
284 area, lineStyle);
285 plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation,
286 org.jfree.ui.Layer.BACKGROUND);
287 }
288 if (ThemeUtil.parseShowVerticalLine(theme)) {
289 XYLineAnnotation lineBackAnnotation =
290 createStickyLineAnnotation(
291 StickyAxisAnnotation.SimpleAxis.X_AXIS,
292 annotation.getHitPoint(), annotation.getPos(),
293 area, lineStyle);
294 plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation,
295 org.jfree.ui.Layer.BACKGROUND);
296 }
297 }
298 }
299 }
300
301 // Style the text.
302 if (textStyle != null) {
303 textStyle.apply(textAnnotation);
304 }
305
306 // Add the Annotations to renderer.
307 plot.getRenderer(rendererIndex).addAnnotation(textAnnotation,
308 org.jfree.ui.Layer.FOREGROUND);
309 plot.getRenderer(rendererIndex).addAnnotation(lineAnnotation,
310 org.jfree.ui.Layer.FOREGROUND);
311 }
312
313 /**
314 * Create annotation that sticks to "ground" (X) axis.
315 * @param area helper to calculate coordinates
316 * @param pos one-dimensional position (distance from axis)
317 * @param lineStyle the line style to use for the line.
318 */
319 protected static XYLineAnnotation createGroundStickAnnotation(
320 ChartArea area, float pos, ThemeAccess.LineStyle lineStyle
321 ) {
322 // Style the line.
323 if (lineStyle != null) {
324 return new XYLineAnnotation(
325 pos, area.atGround(),
326 pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET),
327 new BasicStroke(lineStyle.getWidth()),lineStyle.getColor());
328 }
329 else {
330 return new XYLineAnnotation(
331 pos, area.atGround(),
332 pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET));
333 }
334 }
335
336
337 /**
338 * Create annotation that sticks to the second Y axis ("right").
339 * @param area helper to calculate coordinates
340 * @param pos one-dimensional position (distance from axis)
341 * @param lineStyle the line style to use for the line.
342 */
343 protected static XYLineAnnotation createRightStickAnnotation(
344 ChartArea area, float pos, ThemeAccess.LineStyle lineStyle
345 ) {
346 // Style the line.
347 if (lineStyle != null) {
348 return new XYLineAnnotation(
349 area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos,
350 area.atRight(), pos,
351 new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
352 }
353 else {
354 return new XYLineAnnotation(
355 area.atRight(), pos,
356 area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos);
357 }
358 }
359
360
361 /**
362 * Create annotation that sticks to the first Y axis ("left").
363 * @param area helper to calculate coordinates
364 * @param pos one-dimensional position (distance from axis)
365 * @param lineStyle the line style to use for the line.
366 */
367 protected static XYLineAnnotation createLeftStickAnnotation(
368 ChartArea area, float pos, ThemeAccess.LineStyle lineStyle
369 ) {
370 // Style the line.
371 if (lineStyle != null) {
372 return new XYLineAnnotation(
373 area.atLeft(), pos,
374 area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos,
375 new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
376 }
377 else {
378 return new XYLineAnnotation(
379 area.atLeft(), pos,
380 area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos);
381 }
382 }
383
384
385 /**
386 * Create a line from a axis to a given point.
387 * @param axis The "simple" axis.
388 * @param fromD1 from-location in first dimension.
389 * @param toD2 to-location in second dimension.
390 * @param area helper to calculate offsets.
391 * @param lineStyle optional line style.
392 */
393 protected static XYLineAnnotation createStickyLineAnnotation(
394 StickyAxisAnnotation.SimpleAxis axis, float fromD1, float toD2,
395 ChartArea area, ThemeAccess.LineStyle lineStyle
396 ) {
397 double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d;
398 switch(axis) {
399 case X_AXIS:
400 anchorX1 = fromD1;
401 anchorX2 = fromD1;
402 anchorY1 = area.atGround();
403 anchorY2 = toD2;
404 break;
405 case Y_AXIS:
406 anchorX1 = area.atLeft();
407 anchorX2 = toD2;
408 anchorY1 = fromD1;
409 anchorY2 = fromD1;
410 break;
411 case Y_AXIS2:
412 anchorX1 = area.atRight();
413 anchorX2 = toD2;
414 anchorY1 = fromD1;
415 anchorY2 = fromD1;
416 break;
417 }
418 // Style the line.
419 if (lineStyle != null) {
420 return new XYLineAnnotation(
421 anchorX1, anchorY1,
422 anchorX2, anchorY2,
423 new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
424 }
425 else {
426 return new XYLineAnnotation(
427 anchorX1, anchorY1,
428 anchorX2, anchorY2);
429 }
430 }
431
432 /**
433 * Add the annotations (Sticky, Text and hyk zones) stored
434 * in the annotations field.
435 */
436 protected void addAnnotationsToRenderer(XYPlot plot) {
437 logger.debug("addAnnotationsToRenderer");
438
439 if (annotations == null || annotations.size() == 0) {
440 logger.debug("addAnnotationsToRenderer: no annotations.");
441 return;
442 }
443
444 // Paints for the boxes/lines.
445 Stroke basicStroke = new BasicStroke(1.0f);
446
447 Paint linePaint = new Color(255, 0,0,60);
448 Paint fillPaint = new Color(0, 255,0,60);
449 Paint tranPaint = new Color(0, 0,0, 0);
450
451 // OPTMIMIZE: Pre-calculate positions
452 ChartArea area = new ChartArea(
453 plot.getDomainAxis(0).getRange(),
454 plot.getRangeAxis().getRange());
455
456 // Walk over all Annotation sets.
457 for (FLYSAnnotation fa: annotations) {
458
459 // Access text styling, if any.
460 Document theme = fa.getTheme();
461 ThemeAccess.TextStyle textStyle = null;
462 ThemeAccess.LineStyle lineStyle = null;
463
464 // Get Themeing information and add legend item.
465 if (theme != null) {
466 ThemeAccess themeAccess = new ThemeAccess(theme);
467 textStyle = themeAccess.parseTextStyle();
468 lineStyle = themeAccess.parseLineStyle();
469 if (fa.getLabel() != null) {
470 LegendItemCollection lic = new LegendItemCollection();
471 LegendItemCollection old = plot.getFixedLegendItems();
472 lic.add(createLegendItem(theme, fa.getLabel()));
473 // (Re-)Add prior legend entries.
474 if (old != null) {
475 old.addAll(lic);
476 }
477 else {
478 old = lic;
479 }
480 plot.setFixedLegendItems(old);
481 }
482 }
483
484 // The 'Sticky' Annotations (at axis, with line and text).
485 for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) {
486 addStickyAnnotation(
487 sta, plot, area, lineStyle, textStyle, theme);
488 }
489
490 // Other Text Annotations (e.g. labels of (manual) points).
491 for (XYTextAnnotation ta: fa.getTextAnnotations()) {
492 // Style the text.
493 if (textStyle != null) {
494 textStyle.apply(ta);
495 }
496 ta.setY(area.above(0.05d, ta.getY()));
497 plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND);
498 }
499
500 // Hyks.
501 for (HYKFactory.Zone zone: fa.getBoxes()) {
502 // For each zone, create a box to fill with color, a box to draw
503 // the lines and a text to display the type.
504 fillPaint = colorForHYKZone(zone.getName());
505
506 XYBoxAnnotation boxA = new XYBoxAnnotation(zone.getFrom(), area.atGround(),
507 zone.getTo(), area.ofGround(0.03f), basicStroke, tranPaint, fillPaint);
508 XYBoxAnnotation boxB = new XYBoxAnnotation(zone.getFrom(), area.atGround(),
509 zone.getTo(), area.atTop(), basicStroke, fillPaint, tranPaint);
510
511 XYTextAnnotation tex = new XYTextAnnotation(zone.getName(),
512 zone.getFrom() + (zone.getTo() - zone.getFrom()) / 1.0d,
513 area.ofGround(0.015f));
514 if (textStyle != null) {
515 textStyle.apply(tex);
516 }
517
518 plot.getRenderer().addAnnotation(boxA, org.jfree.ui.Layer.BACKGROUND);
519 plot.getRenderer().addAnnotation(boxB, org.jfree.ui.Layer.BACKGROUND);
520 plot.getRenderer().addAnnotation(tex, org.jfree.ui.Layer.BACKGROUND);
521 }
522 }
523 }
524
174 525
175 /** 526 /**
176 * This method needs to be implemented by concrete subclasses to create new 527 * This method needs to be implemented by concrete subclasses to create new
177 * instances of JFreeChart. 528 * instances of JFreeChart.
178 * 529 *
273 * @param chart The JFreeChart chart object. 624 * @param chart The JFreeChart chart object.
274 */ 625 */
275 protected void addSubtitles(JFreeChart chart) { 626 protected void addSubtitles(JFreeChart chart) {
276 // do nothing 627 // do nothing
277 } 628 }
278 629
630
631 /** Get color for hyk zones by their type (which is the name). */
632 protected Paint colorForHYKZone(String zoneName) {
633 if (zoneName.startsWith("R")) {
634 // Brownish.
635 return new Color(153, 60, 0);
636 }
637 else if (zoneName.startsWith("V")) {
638 // Greenish.
639 return new Color(0, 255, 0);
640 }
641 else if (zoneName.startsWith("B")) {
642 // Grayish.
643 return new Color(128, 128, 128);
644 }
645 else if (zoneName.startsWith("H")) {
646 // Blueish.
647 return new Color(0, 0, 255);
648 }
649 else {
650 // Default.
651 logger.debug("Unknown zone type found.");
652 return new Color(255, 0, 0);
653 }
654 }
655
656
657 /**
658 * Register annotations like MainValues for later plotting
659 *
660 * @param annotations list of annotations (data of facet).
661 * @param aandf Artifact and the facet.
662 * @param theme Theme document for given annotations.
663 * @param visible The visibility of the annotations.
664 */
665 protected void doAnnotations(
666 FLYSAnnotation annotations,
667 ArtifactAndFacet aandf,
668 Document theme,
669 boolean visible
670 ){
671 // Running into trouble here.
672 logger.debug("doAnnotations");
673
674 // Add all annotations to our annotation pool.
675 annotations.setTheme(theme);
676 if (aandf != null) {
677 Facet facet = aandf.getFacet();
678 annotations.setLabel(aandf.getFacetDescription());
679 }
680 else {
681 logger.debug(
682 "Art/Facet for Annotations is null. " +
683 "This should never happen!");
684 }
685
686 if (visible) {
687 addAnnotations(annotations);
688 }
689 }
690
279 691
280 /** 692 /**
281 * Generate chart. 693 * Generate chart.
282 */ 694 */
283 @Override 695 @Override

http://dive4elements.wald.intevation.org