Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java @ 7033:0d91a6598a89 generator-refactoring
Add a copy of the ChartGenerator class.
This class will have very reduced functionality and mostly
provide the interface defined in OutGenerator and work as
a bridge for the Charts that are not in a Diagram (think svg export)
This will later replace the ChartGenerator class again after the
refactoring is done.
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Tue, 17 Sep 2013 16:49:37 +0200 |
parents | artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java@819481cc9195 |
children | 557cb3a3d772 |
comparison
equal
deleted
inserted
replaced
7032:069acf3cf45e | 7033:0d91a6598a89 |
---|---|
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 | |
9 package org.dive4elements.river.exports; | |
10 | |
11 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; | |
12 import org.dive4elements.artifactdatabase.state.Settings; | |
13 import org.dive4elements.artifacts.Artifact; | |
14 import org.dive4elements.artifacts.ArtifactNamespaceContext; | |
15 import org.dive4elements.artifacts.CallContext; | |
16 import org.dive4elements.artifacts.CallMeta; | |
17 import org.dive4elements.artifacts.PreferredLocale; | |
18 import org.dive4elements.artifacts.common.utils.XMLUtils; | |
19 import org.dive4elements.river.artifacts.access.RangeAccess; | |
20 import org.dive4elements.river.artifacts.D4EArtifact; | |
21 import org.dive4elements.river.artifacts.resources.Resources; | |
22 import org.dive4elements.river.collections.D4EArtifactCollection; | |
23 import org.dive4elements.river.jfree.Bounds; | |
24 import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; | |
25 import org.dive4elements.river.jfree.DoubleBounds; | |
26 import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer; | |
27 import org.dive4elements.river.jfree.RiverAnnotation; | |
28 import org.dive4elements.river.jfree.StableXYDifferenceRenderer; | |
29 import org.dive4elements.river.jfree.StickyAxisAnnotation; | |
30 import org.dive4elements.river.jfree.Style; | |
31 import org.dive4elements.river.jfree.StyledAreaSeriesCollection; | |
32 import org.dive4elements.river.jfree.StyledSeries; | |
33 import org.dive4elements.river.model.River; | |
34 import org.dive4elements.river.themes.LineStyle; | |
35 import org.dive4elements.river.themes.TextStyle; | |
36 import org.dive4elements.river.themes.ThemeDocument; | |
37 import org.dive4elements.river.utils.RiverUtils; | |
38 | |
39 import java.awt.BasicStroke; | |
40 import java.awt.Color; | |
41 import java.awt.Font; | |
42 import java.awt.Paint; | |
43 import java.awt.Stroke; | |
44 import java.awt.TexturePaint; | |
45 import java.awt.geom.Rectangle2D; | |
46 import java.awt.image.BufferedImage; | |
47 import java.io.IOException; | |
48 import java.io.OutputStream; | |
49 import java.util.ArrayList; | |
50 import java.util.List; | |
51 import java.util.Locale; | |
52 import java.util.Map; | |
53 import java.util.SortedMap; | |
54 import java.util.TreeMap; | |
55 | |
56 import javax.xml.xpath.XPathConstants; | |
57 | |
58 import org.apache.log4j.Logger; | |
59 import org.jfree.chart.JFreeChart; | |
60 import org.jfree.chart.LegendItem; | |
61 import org.jfree.chart.LegendItemCollection; | |
62 import org.jfree.chart.annotations.XYLineAnnotation; | |
63 import org.jfree.chart.annotations.XYTextAnnotation; | |
64 import org.jfree.chart.axis.NumberAxis; | |
65 import org.jfree.chart.plot.XYPlot; | |
66 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; | |
67 import org.jfree.chart.title.TextTitle; | |
68 import org.jfree.data.Range; | |
69 import org.jfree.data.general.Series; | |
70 import org.jfree.data.xy.XYDataset; | |
71 import org.jfree.ui.RectangleInsets; | |
72 import org.jfree.ui.TextAnchor; | |
73 import org.w3c.dom.Document; | |
74 import org.w3c.dom.Element; | |
75 | |
76 import org.dive4elements.river.utils.Formatter; | |
77 | |
78 /** | |
79 * The base class for chart creation. It should provide some basic things that | |
80 * equal in all chart types. | |
81 * | |
82 * Annotations are added as RiverAnnotations and come in mutliple basic forms: | |
83 * TextAnnotations are labels somewhere in data space, StickyAnnotations are | |
84 * labels of a slice or line in one data dimension (i.e. visualized as label | |
85 * on a single axis). | |
86 * | |
87 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
88 */ | |
89 public abstract class ChartGenerator2 implements OutGenerator { | |
90 | |
91 private static Logger logger = Logger.getLogger(ChartGenerator2.class); | |
92 | |
93 public static final int DEFAULT_CHART_WIDTH = 600; | |
94 public static final int DEFAULT_CHART_HEIGHT = 400; | |
95 public static final String DEFAULT_CHART_FORMAT = "png"; | |
96 public static final Color DEFAULT_GRID_COLOR = Color.GRAY; | |
97 public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; | |
98 public static final int DEFAULT_FONT_SIZE = 12; | |
99 public static final String DEFAULT_FONT_NAME = "Tahoma"; | |
100 | |
101 protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f; | |
102 | |
103 public static final String XPATH_CHART_SIZE = | |
104 "/art:action/art:attributes/art:size"; | |
105 | |
106 public static final String XPATH_CHART_FORMAT = | |
107 "/art:action/art:attributes/art:format/@art:value"; | |
108 | |
109 public static final String XPATH_CHART_X_RANGE = | |
110 "/art:action/art:attributes/art:xrange"; | |
111 | |
112 public static final String XPATH_CHART_Y_RANGE = | |
113 "/art:action/art:attributes/art:yrange"; | |
114 | |
115 | |
116 /** The document of the incoming out() request.*/ | |
117 protected Document request; | |
118 | |
119 /** The output stream where the data should be written to.*/ | |
120 protected OutputStream out; | |
121 | |
122 /** The CallContext object.*/ | |
123 protected CallContext context; | |
124 | |
125 protected D4EArtifactCollection collection; | |
126 | |
127 /** The artifact that is used to decorate the chart with meta information.*/ | |
128 protected Artifact master; | |
129 | |
130 /** The settings that should be used during output creation.*/ | |
131 protected Settings settings; | |
132 | |
133 /** Map of datasets ("index"). */ | |
134 protected SortedMap<Integer, AxisDataset> datasets; | |
135 | |
136 /** List of annotations to insert in plot. */ | |
137 protected List<RiverAnnotation> annotations = new ArrayList<RiverAnnotation>(); | |
138 | |
139 /** | |
140 * A mini interface that allows to walk over the YAXIS enums defined in | |
141 * subclasses. | |
142 */ | |
143 public interface YAxisWalker { | |
144 | |
145 int length(); | |
146 | |
147 String getId(int idx); | |
148 } // end of YAxisWalker interface | |
149 | |
150 | |
151 | |
152 public interface AxisDataset { | |
153 | |
154 void addDataset(XYDataset dataset); | |
155 | |
156 XYDataset[] getDatasets(); | |
157 | |
158 boolean isEmpty(); | |
159 | |
160 void setRange(Range range); | |
161 | |
162 Range getRange(); | |
163 | |
164 boolean isArea(XYDataset dataset); | |
165 | |
166 void setPlotAxisIndex(int idx); | |
167 | |
168 int getPlotAxisIndex(); | |
169 | |
170 } // end of AxisDataset interface | |
171 | |
172 | |
173 | |
174 /** | |
175 * Default constructor that initializes internal data structures. | |
176 */ | |
177 public ChartGenerator2() { | |
178 datasets = new TreeMap<Integer, AxisDataset>(); | |
179 } | |
180 | |
181 | |
182 /** | |
183 * Adds annotations to list. The given annotation will be visible. | |
184 */ | |
185 public void addAnnotations(RiverAnnotation annotation) { | |
186 annotations.add(annotation); | |
187 } | |
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 LineStyle lineStyle, | |
199 TextStyle textStyle, | |
200 ThemeDocument 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 XYChartGenerator2 here | |
225 AxisDataset dataset = 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 (theme.parseShowVerticalLine()) { | |
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 (theme.parseShowHorizontalLine()) { | |
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 (theme.parseShowHorizontalLine()) { | |
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 (theme.parseShowVerticalLine()) { | |
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, 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, 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, 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, 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 * @param plot Plot to add annotations to. | |
436 */ | |
437 protected void addAnnotationsToRenderer(XYPlot plot) { | |
438 logger.debug("addAnnotationsToRenderer"); | |
439 | |
440 if (annotations == null || annotations.isEmpty()) { | |
441 logger.debug("addAnnotationsToRenderer: no annotations."); | |
442 return; | |
443 } | |
444 | |
445 // OPTMIMIZE: Pre-calculate positions | |
446 ChartArea area = new ChartArea( | |
447 plot.getDomainAxis(0).getRange(), | |
448 plot.getRangeAxis().getRange()); | |
449 | |
450 // Walk over all Annotation sets. | |
451 for (RiverAnnotation fa: annotations) { | |
452 | |
453 // Access text styling, if any. | |
454 ThemeDocument theme = fa.getTheme(); | |
455 TextStyle textStyle = null; | |
456 LineStyle lineStyle = null; | |
457 | |
458 // Get Themeing information and add legend item. | |
459 if (theme != null) { | |
460 textStyle = theme.parseComplexTextStyle(); | |
461 lineStyle = theme.parseComplexLineStyle(); | |
462 if (fa.getLabel() != null) { | |
463 LegendItemCollection lic = new LegendItemCollection(); | |
464 LegendItemCollection old = plot.getFixedLegendItems(); | |
465 lic.add(createLegendItem(theme, fa.getLabel())); | |
466 // (Re-)Add prior legend entries. | |
467 if (old != null) { | |
468 old.addAll(lic); | |
469 } | |
470 else { | |
471 old = lic; | |
472 } | |
473 plot.setFixedLegendItems(old); | |
474 } | |
475 } | |
476 | |
477 // The 'Sticky' Annotations (at axis, with line and text). | |
478 for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) { | |
479 addStickyAnnotation( | |
480 sta, plot, area, lineStyle, textStyle, theme); | |
481 } | |
482 | |
483 // Other Text Annotations (e.g. labels of (manual) points). | |
484 for (XYTextAnnotation ta: fa.getTextAnnotations()) { | |
485 // Style the text. | |
486 if (textStyle != null) { | |
487 textStyle.apply(ta); | |
488 } | |
489 ta.setY(area.above(0.05d, ta.getY())); | |
490 plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND); | |
491 } | |
492 } | |
493 } | |
494 | |
495 | |
496 /** | |
497 * This method needs to be implemented by concrete subclasses to create new | |
498 * instances of JFreeChart. | |
499 * | |
500 * @return a new instance of a JFreeChart. | |
501 */ | |
502 public abstract JFreeChart generateChart(); | |
503 | |
504 | |
505 /** For every outable (i.e. facets), this function is | |
506 * called and handles the data accordingly. */ | |
507 @Override | |
508 public abstract void doOut( | |
509 ArtifactAndFacet bundle, | |
510 ThemeDocument attr, | |
511 boolean visible); | |
512 | |
513 | |
514 protected abstract YAxisWalker getYAxisWalker(); | |
515 | |
516 | |
517 protected abstract Series getSeriesOf(XYDataset dataset, int idx); | |
518 | |
519 /** | |
520 * Returns the default title of a chart. | |
521 * | |
522 * @return the default title of a chart. | |
523 */ | |
524 protected abstract String getDefaultChartTitle(); | |
525 | |
526 | |
527 /** | |
528 * Returns the default X-Axis label of a chart. | |
529 * | |
530 * @return the default X-Axis label of a chart. | |
531 */ | |
532 protected abstract String getDefaultXAxisLabel(); | |
533 | |
534 | |
535 /** | |
536 * This method is called to retrieve the default label for an Y axis at | |
537 * position <i>pos</i>. | |
538 * | |
539 * @param pos The position of an Y axis. | |
540 * | |
541 * @return the default Y axis label at position <i>pos</i>. | |
542 */ | |
543 protected abstract String getDefaultYAxisLabel(int pos); | |
544 | |
545 | |
546 /** | |
547 * This method is used to create new AxisDataset instances which may differ | |
548 * in concrete subclasses. | |
549 * | |
550 * @param idx The index of an axis. | |
551 */ | |
552 protected abstract AxisDataset createAxisDataset(int idx); | |
553 | |
554 | |
555 /** | |
556 * Combines the ranges of the X axis at index <i>idx</i>. | |
557 * | |
558 * @param bounds A new Bounds. | |
559 * @param idx The index of the X axis that should be comined with | |
560 * <i>range</i>. | |
561 */ | |
562 protected abstract void combineXBounds(Bounds bounds, int idx); | |
563 | |
564 | |
565 /** | |
566 * Combines the ranges of the Y axis at index <i>idx</i>. | |
567 * | |
568 * @param bounds A new Bounds. | |
569 * @param index The index of the Y axis that should be comined with. | |
570 * <i>range</i>. | |
571 */ | |
572 protected abstract void combineYBounds(Bounds bounds, int index); | |
573 | |
574 | |
575 /** | |
576 * This method is used to determine the ranges for axes at a given index. | |
577 * | |
578 * @param index The index of the axes at the plot. | |
579 * | |
580 * @return a Range[] with [xrange, yrange]; | |
581 */ | |
582 public abstract Range[] getRangesForAxis(int index); | |
583 | |
584 public abstract Bounds getXBounds(int axis); | |
585 | |
586 protected abstract void setXBounds(int axis, Bounds bounds); | |
587 | |
588 public abstract Bounds getYBounds(int axis); | |
589 | |
590 protected abstract void setYBounds(int axis, Bounds bounds); | |
591 | |
592 | |
593 /** | |
594 * This method retrieves the chart subtitle by calling getChartSubtitle() | |
595 * and adds it as TextTitle to the chart. | |
596 * The default implementation of getChartSubtitle() returns the same | |
597 * as getDefaultChartSubtitle() which must be implemented by derived | |
598 * classes. If you want to add multiple subtitles to the chart override | |
599 * this method and add your subtitles manually. | |
600 * | |
601 * @param chart The JFreeChart chart object. | |
602 */ | |
603 protected void addSubtitles(JFreeChart chart) { | |
604 String subtitle = getChartSubtitle(); | |
605 | |
606 if (subtitle != null && subtitle.length() > 0) { | |
607 chart.addSubtitle(new TextTitle(subtitle)); | |
608 } | |
609 } | |
610 | |
611 | |
612 /** | |
613 * Register annotations like MainValues for later plotting | |
614 * | |
615 * @param annotations list of annotations (data of facet). | |
616 * @param aandf Artifact and the facet. | |
617 * @param theme Theme document for given annotations. | |
618 * @param visible The visibility of the annotations. | |
619 */ | |
620 public void doAnnotations( | |
621 RiverAnnotation annotations, | |
622 ArtifactAndFacet aandf, | |
623 ThemeDocument theme, | |
624 boolean visible | |
625 ){ | |
626 logger.debug("doAnnotations"); | |
627 | |
628 // Add all annotations to our annotation pool. | |
629 annotations.setTheme(theme); | |
630 if (aandf != null) { | |
631 annotations.setLabel(aandf.getFacetDescription()); | |
632 } | |
633 else { | |
634 logger.error( | |
635 "Art/Facet for Annotations is null. " + | |
636 "This should never happen!"); | |
637 } | |
638 | |
639 if (visible) { | |
640 addAnnotations(annotations); | |
641 } | |
642 } | |
643 | |
644 | |
645 /** | |
646 * Generate chart. | |
647 */ | |
648 @Override | |
649 public void generate() | |
650 throws IOException | |
651 { | |
652 logger.debug("ChartGenerator2.generate"); | |
653 | |
654 JFreeChart chart = generateChart(); | |
655 | |
656 String format = getFormat(); | |
657 int[] size = getSize(); | |
658 | |
659 if (size == null) { | |
660 size = getExportDimension(); | |
661 } | |
662 | |
663 context.putContextValue("chart.width", size[0]); | |
664 context.putContextValue("chart.height", size[1]); | |
665 | |
666 if (format.equals(ChartExportHelper.FORMAT_PNG)) { | |
667 context.putContextValue("chart.image.format", "png"); | |
668 | |
669 ChartExportHelper.exportImage( | |
670 out, | |
671 chart, | |
672 context); | |
673 } | |
674 else if (format.equals(ChartExportHelper.FORMAT_PDF)) { | |
675 preparePDFContext(context); | |
676 | |
677 ChartExportHelper.exportPDF( | |
678 out, | |
679 chart, | |
680 context); | |
681 } | |
682 else if (format.equals(ChartExportHelper.FORMAT_SVG)) { | |
683 prepareSVGContext(context); | |
684 | |
685 ChartExportHelper.exportSVG( | |
686 out, | |
687 chart, | |
688 context); | |
689 } | |
690 else if (format.equals(ChartExportHelper.FORMAT_CSV)) { | |
691 context.putContextValue("chart.image.format", "csv"); | |
692 | |
693 ChartExportHelper.exportCSV( | |
694 out, | |
695 chart, | |
696 context); | |
697 } | |
698 } | |
699 | |
700 | |
701 @Override | |
702 public void init(Document request, OutputStream out, CallContext context) { | |
703 logger.debug("ChartGenerator2.init"); | |
704 | |
705 this.request = request; | |
706 this.out = out; | |
707 this.context = context; | |
708 } | |
709 | |
710 | |
711 /** Sets the master artifact. */ | |
712 @Override | |
713 public void setMasterArtifact(Artifact master) { | |
714 this.master = master; | |
715 } | |
716 | |
717 | |
718 /** | |
719 * Gets the master artifact. | |
720 * @return the master artifact. | |
721 */ | |
722 public Artifact getMaster() { | |
723 return master; | |
724 } | |
725 | |
726 | |
727 /** Sets the collection. */ | |
728 @Override | |
729 public void setCollection(D4EArtifactCollection collection) { | |
730 this.collection = collection; | |
731 } | |
732 | |
733 | |
734 @Override | |
735 public void setSettings(Settings settings) { | |
736 this.settings = settings; | |
737 } | |
738 | |
739 | |
740 /** | |
741 * Returns an instance of <i>ChartSettings</i> with a chart specific section | |
742 * but with no axes settings. | |
743 * | |
744 * @return an instance of <i>ChartSettings</i>. | |
745 */ | |
746 @Override | |
747 public Settings getSettings() { | |
748 if (this.settings != null) { | |
749 return this.settings; | |
750 } | |
751 | |
752 ChartSettings settings = new ChartSettings(); | |
753 | |
754 ChartSection chartSection = buildChartSection(); | |
755 LegendSection legendSection = buildLegendSection(); | |
756 ExportSection exportSection = buildExportSection(); | |
757 | |
758 settings.setChartSection(chartSection); | |
759 settings.setLegendSection(legendSection); | |
760 settings.setExportSection(exportSection); | |
761 | |
762 List<AxisSection> axisSections = buildAxisSections(); | |
763 for (AxisSection axisSection: axisSections) { | |
764 settings.addAxisSection(axisSection); | |
765 } | |
766 | |
767 return settings; | |
768 } | |
769 | |
770 | |
771 /** | |
772 * Creates a new <i>ChartSection</i>. | |
773 * | |
774 * @return a new <i>ChartSection</i>. | |
775 */ | |
776 protected ChartSection buildChartSection() { | |
777 ChartSection chartSection = new ChartSection(); | |
778 chartSection.setTitle(getChartTitle()); | |
779 chartSection.setSubtitle(getChartSubtitle()); | |
780 chartSection.setDisplayGrid(isGridVisible()); | |
781 chartSection.setDisplayLogo(showLogo()); | |
782 chartSection.setLogoVPlacement(logoVPlace()); | |
783 chartSection.setLogoHPlacement(logoHPlace()); | |
784 return chartSection; | |
785 } | |
786 | |
787 | |
788 /** | |
789 * Creates a new <i>LegendSection</i>. | |
790 * | |
791 * @return a new <i>LegendSection</i>. | |
792 */ | |
793 protected LegendSection buildLegendSection() { | |
794 LegendSection legendSection = new LegendSection(); | |
795 legendSection.setVisibility(isLegendVisible()); | |
796 legendSection.setFontSize(getLegendFontSize()); | |
797 legendSection.setAggregationThreshold(10); | |
798 return legendSection; | |
799 } | |
800 | |
801 | |
802 /** | |
803 * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b> | |
804 * and <b>HEIGHT=400</b>. | |
805 * | |
806 * @return a new <i>ExportSection</i>. | |
807 */ | |
808 protected ExportSection buildExportSection() { | |
809 ExportSection exportSection = new ExportSection(); | |
810 exportSection.setWidth(600); | |
811 exportSection.setHeight(400); | |
812 return exportSection; | |
813 } | |
814 | |
815 | |
816 /** | |
817 * Creates a list of Sections that contains all axes of the chart (including | |
818 * X and Y axes). | |
819 * | |
820 * @return a list of Sections for each axis in this chart. | |
821 */ | |
822 protected List<AxisSection> buildAxisSections() { | |
823 List<AxisSection> axisSections = new ArrayList<AxisSection>(); | |
824 | |
825 axisSections.addAll(buildXAxisSections()); | |
826 axisSections.addAll(buildYAxisSections()); | |
827 | |
828 return axisSections; | |
829 } | |
830 | |
831 | |
832 /** | |
833 * Creates a new Section for chart's X axis. | |
834 * | |
835 * @return a List that contains a Section for the X axis. | |
836 */ | |
837 protected List<AxisSection> buildXAxisSections() { | |
838 List<AxisSection> axisSections = new ArrayList<AxisSection>(); | |
839 | |
840 String identifier = "X"; | |
841 | |
842 AxisSection axisSection = new AxisSection(); | |
843 axisSection.setIdentifier(identifier); | |
844 axisSection.setLabel(getXAxisLabel()); | |
845 axisSection.setFontSize(14); | |
846 axisSection.setFixed(false); | |
847 | |
848 // XXX We are able to find better default ranges that [0,0], but the Y | |
849 // axes currently have no better ranges set. | |
850 axisSection.setUpperRange(0d); | |
851 axisSection.setLowerRange(0d); | |
852 | |
853 axisSections.add(axisSection); | |
854 | |
855 return axisSections; | |
856 } | |
857 | |
858 | |
859 /** | |
860 * Creates a list of Section for the chart's Y axes. This method makes use | |
861 * of <i>getYAxisWalker</i> to be able to access all Y axes defined in | |
862 * subclasses. | |
863 * | |
864 * @return a list of Y axis sections. | |
865 */ | |
866 protected List<AxisSection> buildYAxisSections() { | |
867 List<AxisSection> axisSections = new ArrayList<AxisSection>(); | |
868 | |
869 YAxisWalker walker = getYAxisWalker(); | |
870 for (int i = 0, n = walker.length(); i < n; i++) { | |
871 AxisSection ySection = new AxisSection(); | |
872 ySection.setIdentifier(walker.getId(i)); | |
873 ySection.setLabel(getYAxisLabel(i)); | |
874 ySection.setFontSize(14); | |
875 ySection.setFixed(false); | |
876 | |
877 // XXX We are able to find better default ranges that [0,0], the | |
878 // only problem is, that we do NOT have a better range than [0,0] | |
879 // for each axis, because the initial chart will not have a dataset | |
880 // for each axis set! | |
881 ySection.setUpperRange(0d); | |
882 ySection.setLowerRange(0d); | |
883 | |
884 axisSections.add(ySection); | |
885 } | |
886 | |
887 return axisSections; | |
888 } | |
889 | |
890 | |
891 /** | |
892 * Returns the <i>settings</i> as <i>ChartSettings</i>. | |
893 * | |
894 * @return the <i>settings</i> as <i>ChartSettings</i> or null, if | |
895 * <i>settings</i> is not an instance of <i>ChartSettings</i>. | |
896 */ | |
897 public ChartSettings getChartSettings() { | |
898 if (settings instanceof ChartSettings) { | |
899 return (ChartSettings) settings; | |
900 } | |
901 | |
902 return null; | |
903 } | |
904 | |
905 | |
906 /** | |
907 * Returns the chart title provided by <i>settings</i>. | |
908 * | |
909 * @param settings A ChartSettings object. | |
910 * | |
911 * @return the title provided by <i>settings</i> or null if no | |
912 * <i>ChartSection</i> is provided by <i>settings</i>. | |
913 * | |
914 * @throws NullPointerException if <i>settings</i> is null. | |
915 */ | |
916 public String getChartTitle(ChartSettings settings) { | |
917 ChartSection cs = settings.getChartSection(); | |
918 return cs != null ? cs.getTitle() : null; | |
919 } | |
920 | |
921 | |
922 /** | |
923 * Returns the chart subtitle provided by <i>settings</i>. | |
924 * | |
925 * @param settings A ChartSettings object. | |
926 * | |
927 * @return the subtitle provided by <i>settings</i> or null if no | |
928 * <i>ChartSection</i> is provided by <i>settings</i>. | |
929 * | |
930 * @throws NullPointerException if <i>settings</i> is null. | |
931 */ | |
932 public String getChartSubtitle(ChartSettings settings) { | |
933 ChartSection cs = settings.getChartSection(); | |
934 return cs != null ? cs.getSubtitle() : null; | |
935 } | |
936 | |
937 | |
938 /** | |
939 * Returns a boolean object that determines if the chart grid should be | |
940 * visible or not. This information needs to be provided by <i>settings</i>, | |
941 * otherweise the default is true. | |
942 * | |
943 * @param settings A ChartSettings object. | |
944 * | |
945 * @return true, if the chart grid should be visible otherwise false. | |
946 * | |
947 * @throws NullPointerException if <i>settings</i> is null. | |
948 */ | |
949 public boolean isGridVisible(ChartSettings settings) { | |
950 ChartSection cs = settings.getChartSection(); | |
951 Boolean displayGrid = cs.getDisplayGrid(); | |
952 | |
953 return displayGrid != null ? displayGrid : true; | |
954 } | |
955 | |
956 | |
957 /** | |
958 * Returns a boolean object that determines if the chart legend should be | |
959 * visible or not. This information needs to be provided by <i>settings</i>, | |
960 * otherwise the default is true. | |
961 * | |
962 * @param settings A ChartSettings object. | |
963 * | |
964 * @return true, if the chart legend should be visible otherwise false. | |
965 * | |
966 * @throws NullPointerException if <i>settings</i> is null. | |
967 */ | |
968 public boolean isLegendVisible(ChartSettings settings) { | |
969 LegendSection ls = settings.getLegendSection(); | |
970 Boolean displayLegend = ls.getVisibility(); | |
971 | |
972 return displayLegend != null ? displayLegend : true; | |
973 } | |
974 | |
975 | |
976 /** | |
977 * Returns the legend font size specified in <i>settings</i> or null if no | |
978 * <i>LegendSection</i> is provided by <i>settings</i>. | |
979 * | |
980 * @param settings A ChartSettings object. | |
981 * | |
982 * @return the legend font size or null. | |
983 * | |
984 * @throws NullPointerException if <i>settings</i> is null. | |
985 */ | |
986 public Integer getLegendFontSize(ChartSettings settings) { | |
987 LegendSection ls = settings.getLegendSection(); | |
988 return ls != null ? ls.getFontSize() : null; | |
989 } | |
990 | |
991 | |
992 /** | |
993 * Returns the title of a chart. The return value depends on the existence | |
994 * of ChartSettings: if there are ChartSettings set, this method returns the | |
995 * chart title provided by those settings. Otherwise, this method returns | |
996 * getDefaultChartTitle(). | |
997 * | |
998 * @return the title of a chart. | |
999 */ | |
1000 protected String getChartTitle() { | |
1001 ChartSettings chartSettings = getChartSettings(); | |
1002 | |
1003 if (chartSettings != null) { | |
1004 return getChartTitle(chartSettings); | |
1005 } | |
1006 | |
1007 return getDefaultChartTitle(); | |
1008 } | |
1009 | |
1010 | |
1011 /** | |
1012 * Returns the subtitle of a chart. The return value depends on the | |
1013 * existence of ChartSettings: if there are ChartSettings set, this method | |
1014 * returns the chart title provided by those settings. Otherwise, this | |
1015 * method returns getDefaultChartSubtitle(). | |
1016 * | |
1017 * @return the subtitle of a chart. | |
1018 */ | |
1019 protected String getChartSubtitle() { | |
1020 ChartSettings chartSettings = getChartSettings(); | |
1021 | |
1022 if (chartSettings != null) { | |
1023 return getChartSubtitle(chartSettings); | |
1024 } | |
1025 | |
1026 return getDefaultChartSubtitle(); | |
1027 } | |
1028 | |
1029 | |
1030 /** | |
1031 * This method always returns null. Override it in subclasses that require | |
1032 * subtitles. | |
1033 * | |
1034 * @return null. | |
1035 */ | |
1036 protected String getDefaultChartSubtitle() { | |
1037 // Override this method in subclasses | |
1038 return null; | |
1039 } | |
1040 | |
1041 | |
1042 /** | |
1043 * This method is used to determine, if the chart's legend is visible or | |
1044 * not. If a <i>settings</i> instance is set, this instance determines the | |
1045 * visibility otherwise, this method returns true as default if no | |
1046 * <i>settings</i> is set. | |
1047 * | |
1048 * @return true, if the legend should be visible, otherwise false. | |
1049 */ | |
1050 protected boolean isLegendVisible() { | |
1051 ChartSettings chartSettings = getChartSettings(); | |
1052 if (chartSettings != null) { | |
1053 return isLegendVisible(chartSettings); | |
1054 } | |
1055 | |
1056 return true; | |
1057 } | |
1058 | |
1059 | |
1060 /** Where to place the logo. */ | |
1061 protected String logoHPlace() { | |
1062 ChartSettings chartSettings = getChartSettings(); | |
1063 if (chartSettings != null) { | |
1064 ChartSection cs = chartSettings.getChartSection(); | |
1065 String place = cs.getLogoHPlacement(); | |
1066 | |
1067 return place; | |
1068 } | |
1069 return "center"; | |
1070 } | |
1071 | |
1072 | |
1073 /** Where to place the logo. */ | |
1074 protected String logoVPlace() { | |
1075 ChartSettings chartSettings = getChartSettings(); | |
1076 if (chartSettings != null) { | |
1077 ChartSection cs = chartSettings.getChartSection(); | |
1078 String place = cs.getLogoVPlacement(); | |
1079 | |
1080 return place; | |
1081 } | |
1082 return "top"; | |
1083 } | |
1084 | |
1085 | |
1086 /** Return the logo id from settings. */ | |
1087 protected String showLogo(ChartSettings chartSettings) { | |
1088 if (chartSettings != null) { | |
1089 ChartSection cs = chartSettings.getChartSection(); | |
1090 String logo = cs.getDisplayLogo(); | |
1091 | |
1092 return logo; | |
1093 } | |
1094 return "none"; | |
1095 } | |
1096 | |
1097 | |
1098 /** | |
1099 * This method is used to determine if a logo should be added to the plot. | |
1100 * | |
1101 * @return logo name (null if none). | |
1102 */ | |
1103 protected String showLogo() { | |
1104 ChartSettings chartSettings = getChartSettings(); | |
1105 return showLogo(chartSettings); | |
1106 } | |
1107 | |
1108 | |
1109 /** | |
1110 * This method is used to determine the font size of the chart's legend. If | |
1111 * a <i>settings</i> instance is set, this instance determines the font | |
1112 * size, otherwise this method returns 12 as default if no <i>settings</i> | |
1113 * is set or if it doesn't provide a legend font size. | |
1114 * | |
1115 * @return a legend font size. | |
1116 */ | |
1117 protected int getLegendFontSize() { | |
1118 Integer fontSize = null; | |
1119 | |
1120 ChartSettings chartSettings = getChartSettings(); | |
1121 if (chartSettings != null) { | |
1122 fontSize = getLegendFontSize(chartSettings); | |
1123 } | |
1124 | |
1125 return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; | |
1126 } | |
1127 | |
1128 | |
1129 /** | |
1130 * This method is used to determine if the resulting chart should display | |
1131 * grid lines or not. <b>Note: this method always returns true!</b> | |
1132 * | |
1133 * @return true, if the chart should display grid lines, otherwise false. | |
1134 */ | |
1135 protected boolean isGridVisible() { | |
1136 return true; | |
1137 } | |
1138 | |
1139 | |
1140 /** | |
1141 * Returns the X-Axis label of a chart. | |
1142 * | |
1143 * @return the X-Axis label of a chart. | |
1144 */ | |
1145 protected String getXAxisLabel() { | |
1146 ChartSettings chartSettings = getChartSettings(); | |
1147 if (chartSettings == null) { | |
1148 return getDefaultXAxisLabel(); | |
1149 } | |
1150 | |
1151 AxisSection as = chartSettings.getAxisSection("X"); | |
1152 if (as != null) { | |
1153 String label = as.getLabel(); | |
1154 | |
1155 if (label != null) { | |
1156 return label; | |
1157 } | |
1158 } | |
1159 | |
1160 return getDefaultXAxisLabel(); | |
1161 } | |
1162 | |
1163 | |
1164 /** | |
1165 * This method returns the font size for the X axis. If the font size is | |
1166 * specified in ChartSettings (if <i>chartSettings</i> is set), this size is | |
1167 * returned. Otherwise the default font size 12 is returned. | |
1168 * | |
1169 * @return the font size for the x axis. | |
1170 */ | |
1171 protected int getXAxisLabelFontSize() { | |
1172 ChartSettings chartSettings = getChartSettings(); | |
1173 if (chartSettings == null) { | |
1174 return DEFAULT_FONT_SIZE; | |
1175 } | |
1176 | |
1177 AxisSection as = chartSettings.getAxisSection("X"); | |
1178 Integer fontSize = as.getFontSize(); | |
1179 | |
1180 return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; | |
1181 } | |
1182 | |
1183 | |
1184 /** | |
1185 * This method returns the font size for an Y axis. If the font size is | |
1186 * specified in ChartSettings (if <i>chartSettings</i> is set), this size is | |
1187 * returned. Otherwise the default font size 12 is returned. | |
1188 * | |
1189 * @return the font size for the x axis. | |
1190 */ | |
1191 protected int getYAxisFontSize(int pos) { | |
1192 ChartSettings chartSettings = getChartSettings(); | |
1193 if (chartSettings == null) { | |
1194 return DEFAULT_FONT_SIZE; | |
1195 } | |
1196 | |
1197 YAxisWalker walker = getYAxisWalker(); | |
1198 | |
1199 AxisSection as = chartSettings.getAxisSection(walker.getId(pos)); | |
1200 if (as == null) { | |
1201 return DEFAULT_FONT_SIZE; | |
1202 } | |
1203 Integer fontSize = as.getFontSize(); | |
1204 | |
1205 return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; | |
1206 } | |
1207 | |
1208 | |
1209 /** | |
1210 * This method returns the export dimension specified in ChartSettings as | |
1211 * int array [width,height]. | |
1212 * | |
1213 * @return an int array with [width,height]. | |
1214 */ | |
1215 protected int[] getExportDimension() { | |
1216 ChartSettings chartSettings = getChartSettings(); | |
1217 if (chartSettings == null) { | |
1218 return new int[] { 600, 400 }; | |
1219 } | |
1220 | |
1221 ExportSection export = chartSettings.getExportSection(); | |
1222 Integer width = export.getWidth(); | |
1223 Integer height = export.getHeight(); | |
1224 | |
1225 if (width != null && height != null) { | |
1226 return new int[] { width, height }; | |
1227 } | |
1228 | |
1229 return new int[] { 600, 400 }; | |
1230 } | |
1231 | |
1232 | |
1233 /** | |
1234 * Returns the Y-Axis label of a chart at position <i>pos</i>. | |
1235 * | |
1236 * @return the Y-Axis label of a chart at position <i>0</i>. | |
1237 */ | |
1238 protected String getYAxisLabel(int pos) { | |
1239 ChartSettings chartSettings = getChartSettings(); | |
1240 if (chartSettings == null) { | |
1241 return getDefaultYAxisLabel(pos); | |
1242 } | |
1243 | |
1244 YAxisWalker walker = getYAxisWalker(); | |
1245 AxisSection as = chartSettings.getAxisSection(walker.getId(pos)); | |
1246 if (as != null) { | |
1247 String label = as.getLabel(); | |
1248 | |
1249 if (label != null) { | |
1250 return label; | |
1251 } | |
1252 } | |
1253 | |
1254 return getDefaultYAxisLabel(pos); | |
1255 } | |
1256 | |
1257 | |
1258 /** | |
1259 * This method searches for a specific axis in the <i>settings</i> if | |
1260 * <i>settings</i> is set. If the axis was found, this method returns the | |
1261 * specified axis range if the axis range is fixed. Otherwise, this method | |
1262 * returns null. | |
1263 * | |
1264 * @param axisId The identifier of an axis. | |
1265 * | |
1266 * @return the specified axis range from <i>settings</i> if the axis is | |
1267 * fixed, otherwise null. | |
1268 */ | |
1269 public Range getRangeForAxisFromSettings(String axisId) { | |
1270 ChartSettings chartSettings = getChartSettings(); | |
1271 if (chartSettings == null) { | |
1272 return null; | |
1273 } | |
1274 | |
1275 AxisSection as = chartSettings.getAxisSection(axisId); | |
1276 | |
1277 if (as == null) { | |
1278 return null; | |
1279 } | |
1280 | |
1281 Boolean fixed = as.isFixed(); | |
1282 | |
1283 if (fixed != null && fixed) { | |
1284 Double upper = as.getUpperRange(); | |
1285 Double lower = as.getLowerRange(); | |
1286 | |
1287 if (upper != null && lower != null) { | |
1288 return lower < upper | |
1289 ? new Range(lower, upper) | |
1290 : new Range(upper, lower); | |
1291 } | |
1292 } | |
1293 | |
1294 return null; | |
1295 } | |
1296 | |
1297 | |
1298 /** | |
1299 * Adds a new AxisDataset which contains <i>dataset</i> at index <i>idx</i>. | |
1300 * | |
1301 * @param dataset An XYDataset. | |
1302 * @param idx The axis index. | |
1303 * @param visible Determines, if the dataset should be visible or not. | |
1304 */ | |
1305 public void addAxisDataset(XYDataset dataset, int idx, boolean visible) { | |
1306 if (dataset == null || idx < 0) { | |
1307 return; | |
1308 } | |
1309 | |
1310 AxisDataset axisDataset = getAxisDataset(idx); | |
1311 | |
1312 Bounds[] xyBounds = ChartHelper.getBounds(dataset); | |
1313 | |
1314 if (xyBounds == null) { | |
1315 logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx); | |
1316 return; | |
1317 } | |
1318 | |
1319 if (visible) { | |
1320 if (logger.isDebugEnabled()) { | |
1321 logger.debug("Add new AxisDataset at index: " + idx); | |
1322 logger.debug("X extent: " + xyBounds[0]); | |
1323 logger.debug("Y extent: " + xyBounds[1]); | |
1324 } | |
1325 | |
1326 axisDataset.addDataset(dataset); | |
1327 } | |
1328 | |
1329 combineXBounds(xyBounds[0], 0); | |
1330 combineYBounds(xyBounds[1], idx); | |
1331 } | |
1332 | |
1333 | |
1334 /** | |
1335 * This method grants access to the AxisDatasets stored in <i>datasets</i>. | |
1336 * If no AxisDataset exists for index <i>idx</i>, a new AxisDataset is | |
1337 * created using <i>createAxisDataset()</i>. | |
1338 * | |
1339 * @param idx The index of the desired AxisDataset. | |
1340 * | |
1341 * @return an existing or new AxisDataset. | |
1342 */ | |
1343 public AxisDataset getAxisDataset(int idx) { | |
1344 AxisDataset axisDataset = datasets.get(idx); | |
1345 | |
1346 if (axisDataset == null) { | |
1347 axisDataset = createAxisDataset(idx); | |
1348 datasets.put(idx, axisDataset); | |
1349 } | |
1350 | |
1351 return axisDataset; | |
1352 } | |
1353 | |
1354 | |
1355 /** | |
1356 * Adjust some Stroke/Grid parameters for <i>plot</i>. The chart | |
1357 * <i>Settings</i> are applied in this method. | |
1358 * | |
1359 * @param plot The XYPlot which is adapted. | |
1360 */ | |
1361 protected void adjustPlot(XYPlot plot) { | |
1362 Stroke gridStroke = new BasicStroke( | |
1363 DEFAULT_GRID_LINE_WIDTH, | |
1364 BasicStroke.CAP_BUTT, | |
1365 BasicStroke.JOIN_MITER, | |
1366 3.0f, | |
1367 new float[] { 3.0f }, | |
1368 0.0f); | |
1369 | |
1370 ChartSettings cs = getChartSettings(); | |
1371 boolean isGridVisible = cs != null ? isGridVisible(cs) : true; | |
1372 | |
1373 plot.setDomainGridlineStroke(gridStroke); | |
1374 plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR); | |
1375 plot.setDomainGridlinesVisible(isGridVisible); | |
1376 | |
1377 plot.setRangeGridlineStroke(gridStroke); | |
1378 plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); | |
1379 plot.setRangeGridlinesVisible(isGridVisible); | |
1380 | |
1381 plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d)); | |
1382 } | |
1383 | |
1384 | |
1385 /** | |
1386 * This helper mehtod is used to extract the current locale from instance | |
1387 * vairable <i>context</i>. | |
1388 * | |
1389 * @return the current locale. | |
1390 */ | |
1391 protected Locale getLocale() { | |
1392 CallMeta meta = context.getMeta(); | |
1393 PreferredLocale[] prefs = meta.getLanguages(); | |
1394 | |
1395 int len = prefs != null ? prefs.length : 0; | |
1396 | |
1397 Locale[] locales = new Locale[len]; | |
1398 | |
1399 for (int i = 0; i < len; i++) { | |
1400 locales[i] = prefs[i].getLocale(); | |
1401 } | |
1402 | |
1403 return meta.getPreferredLocale(locales); | |
1404 } | |
1405 | |
1406 | |
1407 /** | |
1408 * Look up \param key in i18n dictionary. | |
1409 * @param key key for which to find i18nd version. | |
1410 * @param def default, returned if lookup failed. | |
1411 * @return value found in i18n dictionary, \param def if no value found. | |
1412 */ | |
1413 protected String msg(String key, String def) { | |
1414 return Resources.getMsg(context.getMeta(), key, def); | |
1415 } | |
1416 | |
1417 /** | |
1418 * Look up \param key in i18n dictionary. | |
1419 * @param key key for which to find i18nd version. | |
1420 * @return value found in i18n dictionary, key itself if failed. | |
1421 */ | |
1422 protected String msg(String key) { | |
1423 return Resources.getMsg(context.getMeta(), key, key); | |
1424 } | |
1425 | |
1426 protected String msg(String key, String def, Object[] args) { | |
1427 return Resources.getMsg(context.getMeta(), key, def, args); | |
1428 } | |
1429 | |
1430 | |
1431 protected String getRiverName() { | |
1432 D4EArtifact flys = (D4EArtifact) master; | |
1433 | |
1434 River river = RiverUtils.getRiver(flys); | |
1435 return (river != null) ? river.getName() : ""; | |
1436 } | |
1437 | |
1438 | |
1439 protected double[] getRange() { | |
1440 D4EArtifact flys = (D4EArtifact) master; | |
1441 | |
1442 RangeAccess rangeAccess = new RangeAccess(flys); | |
1443 return rangeAccess.getKmRange(); | |
1444 } | |
1445 | |
1446 | |
1447 /** | |
1448 * Returns the size of a chart export as array which has been specified by | |
1449 * the incoming request document. | |
1450 * | |
1451 * @return the size of a chart as [width, height] or null if no width or | |
1452 * height are given in the request document. | |
1453 */ | |
1454 protected int[] getSize() { | |
1455 int[] size = new int[2]; | |
1456 | |
1457 Element sizeEl = (Element)XMLUtils.xpath( | |
1458 request, | |
1459 XPATH_CHART_SIZE, | |
1460 XPathConstants.NODE, | |
1461 ArtifactNamespaceContext.INSTANCE); | |
1462 | |
1463 if (sizeEl != null) { | |
1464 String uri = ArtifactNamespaceContext.NAMESPACE_URI; | |
1465 | |
1466 String w = sizeEl.getAttributeNS(uri, "width"); | |
1467 String h = sizeEl.getAttributeNS(uri, "height"); | |
1468 | |
1469 if (w.length() > 0 && h.length() > 0) { | |
1470 try { | |
1471 size[0] = Integer.parseInt(w); | |
1472 size[1] = Integer.parseInt(h); | |
1473 } | |
1474 catch (NumberFormatException nfe) { | |
1475 logger.warn("Wrong values for chart width/height."); | |
1476 } | |
1477 } | |
1478 } | |
1479 | |
1480 return size[0] > 0 && size[1] > 0 ? size : null; | |
1481 } | |
1482 | |
1483 | |
1484 /** | |
1485 * This method returns the format specified in the <i>request</i> document | |
1486 * or <i>DEFAULT_CHART_FORMAT</i> if no format is specified in | |
1487 * <i>request</i>. | |
1488 * | |
1489 * @return the format used to export this chart. | |
1490 */ | |
1491 protected String getFormat() { | |
1492 String format = (String) XMLUtils.xpath( | |
1493 request, | |
1494 XPATH_CHART_FORMAT, | |
1495 XPathConstants.STRING, | |
1496 ArtifactNamespaceContext.INSTANCE); | |
1497 | |
1498 return format == null || format.length() == 0 | |
1499 ? DEFAULT_CHART_FORMAT | |
1500 : format; | |
1501 } | |
1502 | |
1503 | |
1504 /** | |
1505 * Returns the X-Axis range as String array from request document. | |
1506 * If the (x|y)range elements are not found in request document, return | |
1507 * null (i.e. not zoomed). | |
1508 * | |
1509 * @return a String array with [lower, upper], null if not in document. | |
1510 */ | |
1511 protected String[] getDomainAxisRangeFromRequest() { | |
1512 Element xrange = (Element)XMLUtils.xpath( | |
1513 request, | |
1514 XPATH_CHART_X_RANGE, | |
1515 XPathConstants.NODE, | |
1516 ArtifactNamespaceContext.INSTANCE); | |
1517 | |
1518 if (xrange == null) { | |
1519 return null; | |
1520 } | |
1521 | |
1522 String uri = ArtifactNamespaceContext.NAMESPACE_URI; | |
1523 | |
1524 String lower = xrange.getAttributeNS(uri, "from"); | |
1525 String upper = xrange.getAttributeNS(uri, "to"); | |
1526 | |
1527 return new String[] { lower, upper }; | |
1528 } | |
1529 | |
1530 | |
1531 /** Returns null if the (x|y)range-element was not found in request document. | |
1532 * This usally means that the axis are not manually zoomed, i.e. showing | |
1533 * full data extent. */ | |
1534 protected String[] getValueAxisRangeFromRequest() { | |
1535 Element yrange = (Element)XMLUtils.xpath( | |
1536 request, | |
1537 XPATH_CHART_Y_RANGE, | |
1538 XPathConstants.NODE, | |
1539 ArtifactNamespaceContext.INSTANCE); | |
1540 | |
1541 if (yrange == null) { | |
1542 return null; | |
1543 } | |
1544 | |
1545 | |
1546 String uri = ArtifactNamespaceContext.NAMESPACE_URI; | |
1547 | |
1548 String lower = yrange.getAttributeNS(uri, "from"); | |
1549 String upper = yrange.getAttributeNS(uri, "to"); | |
1550 | |
1551 return new String[] { lower, upper }; | |
1552 } | |
1553 | |
1554 | |
1555 /** | |
1556 * Returns the default size of a chart export as array. | |
1557 * | |
1558 * @return the default size of a chart as [width, height]. | |
1559 */ | |
1560 protected int[] getDefaultSize() { | |
1561 return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT }; | |
1562 } | |
1563 | |
1564 | |
1565 /** | |
1566 * Add datasets stored in instance variable <i>datasets</i> to plot. | |
1567 * <i>datasets</i> actually stores instances of AxisDataset, so each of this | |
1568 * datasets is mapped to a specific axis as well. | |
1569 * | |
1570 * @param plot plot to add datasets to. | |
1571 */ | |
1572 protected void addDatasets(XYPlot plot) { | |
1573 logger.debug("addDatasets()"); | |
1574 | |
1575 // AxisDatasets are sorted, but some might be empty. | |
1576 // Thus, generate numbering on the fly. | |
1577 int axisIndex = 0; | |
1578 int datasetIndex = 0; | |
1579 | |
1580 for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) { | |
1581 if (!entry.getValue().isEmpty()) { | |
1582 // Add axis and range information. | |
1583 AxisDataset axisDataset = entry.getValue(); | |
1584 NumberAxis axis = createYAxis(entry.getKey()); | |
1585 | |
1586 plot.setRangeAxis(axisIndex, axis); | |
1587 | |
1588 if (axis.getAutoRangeIncludesZero()) { | |
1589 axisDataset.setRange( | |
1590 Range.expandToInclude(axisDataset.getRange(), 0d)); | |
1591 } | |
1592 | |
1593 setYBounds(axisIndex, expandPointRange(axisDataset.getRange())); | |
1594 | |
1595 // Add contained datasets, mapping to axis. | |
1596 for (XYDataset dataset: axisDataset.getDatasets()) { | |
1597 plot.setDataset(datasetIndex, dataset); | |
1598 plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); | |
1599 | |
1600 applyThemes(plot, dataset, | |
1601 datasetIndex, | |
1602 axisDataset.isArea(dataset)); | |
1603 | |
1604 datasetIndex++; | |
1605 } | |
1606 | |
1607 axisDataset.setPlotAxisIndex(axisIndex); | |
1608 axisIndex++; | |
1609 } | |
1610 } | |
1611 } | |
1612 | |
1613 | |
1614 /** | |
1615 * @param idx "index" of dataset/series (first dataset to be drawn has | |
1616 * index 0), correlates with renderer index. | |
1617 * @param isArea true if the series describes an area and shall be rendered | |
1618 * as such. | |
1619 */ | |
1620 protected void applyThemes( | |
1621 XYPlot plot, | |
1622 XYDataset series, | |
1623 int idx, | |
1624 boolean isArea | |
1625 ) { | |
1626 if (isArea) { | |
1627 applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx); | |
1628 } | |
1629 else { | |
1630 applyLineTheme(plot, series, idx); | |
1631 } | |
1632 } | |
1633 | |
1634 | |
1635 /** | |
1636 * This method applies the themes defined in the series itself. Therefore, | |
1637 * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer | |
1638 * for the series. | |
1639 * | |
1640 * @param plot The plot. | |
1641 * @param dataset The XYDataset which needs to support Series objects. | |
1642 * @param idx The index of the renderer / dataset. | |
1643 */ | |
1644 protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) { | |
1645 logger.debug("Apply LineTheme for dataset at index: " + idx); | |
1646 | |
1647 LegendItemCollection lic = new LegendItemCollection(); | |
1648 LegendItemCollection anno = plot.getFixedLegendItems(); | |
1649 | |
1650 Font legendFont = createLegendLabelFont(); | |
1651 | |
1652 XYLineAndShapeRenderer renderer = createRenderer(plot, idx); | |
1653 | |
1654 for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { | |
1655 Series series = getSeriesOf(dataset, s); | |
1656 | |
1657 if (series instanceof StyledSeries) { | |
1658 Style style = ((StyledSeries) series).getStyle(); | |
1659 style.applyTheme(renderer, s); | |
1660 } | |
1661 | |
1662 // special case: if there is just one single item, we need to enable | |
1663 // points for this series, otherwise we would not see anything in | |
1664 // the chart area. | |
1665 if (series.getItemCount() == 1) { | |
1666 renderer.setSeriesShapesVisible(s, true); | |
1667 } | |
1668 | |
1669 LegendItem legendItem = renderer.getLegendItem(idx, s); | |
1670 if (legendItem.getLabel().endsWith(" ") || | |
1671 legendItem.getLabel().endsWith("interpol")) { | |
1672 legendItem = null; | |
1673 } | |
1674 | |
1675 if (legendItem != null) { | |
1676 legendItem.setLabelFont(legendFont); | |
1677 lic.add(legendItem); | |
1678 } | |
1679 else { | |
1680 logger.warn("Could not get LegentItem for renderer: " | |
1681 + idx + ", series-idx " + s); | |
1682 } | |
1683 } | |
1684 | |
1685 if (anno != null) { | |
1686 lic.addAll(anno); | |
1687 } | |
1688 | |
1689 plot.setFixedLegendItems(lic); | |
1690 | |
1691 plot.setRenderer(idx, renderer); | |
1692 } | |
1693 | |
1694 | |
1695 /** | |
1696 * @param plot The plot. | |
1697 * @param area A StyledAreaSeriesCollection object. | |
1698 * @param idx The index of the dataset. | |
1699 */ | |
1700 protected void applyAreaTheme( | |
1701 XYPlot plot, | |
1702 StyledAreaSeriesCollection area, | |
1703 int idx | |
1704 ) { | |
1705 LegendItemCollection lic = new LegendItemCollection(); | |
1706 LegendItemCollection anno = plot.getFixedLegendItems(); | |
1707 | |
1708 Font legendFont = createLegendLabelFont(); | |
1709 | |
1710 logger.debug("Registering an 'area'renderer at idx: " + idx); | |
1711 | |
1712 StableXYDifferenceRenderer dRenderer = | |
1713 new StableXYDifferenceRenderer(); | |
1714 | |
1715 if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) { | |
1716 dRenderer.setPositivePaint(createTransparentPaint()); | |
1717 } | |
1718 | |
1719 plot.setRenderer(idx, dRenderer); | |
1720 | |
1721 area.applyTheme(dRenderer); | |
1722 | |
1723 // i18n | |
1724 dRenderer.setAreaLabelNumberFormat(Formatter.getFormatter(context.getMeta(), 2, 4)); | |
1725 | |
1726 dRenderer.setAreaLabelTemplate(Resources.getMsg( | |
1727 context.getMeta(), "area.label.template", "Area=%sm2")); | |
1728 | |
1729 LegendItem legendItem = dRenderer.getLegendItem(idx, 0); | |
1730 if (legendItem != null) { | |
1731 legendItem.setLabelFont(legendFont); | |
1732 lic.add(legendItem); | |
1733 } | |
1734 else { | |
1735 logger.warn("Could not get LegentItem for renderer: " | |
1736 + idx + ", series-idx " + 0); | |
1737 } | |
1738 | |
1739 if (anno != null) { | |
1740 lic.addAll(anno); | |
1741 } | |
1742 | |
1743 plot.setFixedLegendItems(lic); | |
1744 } | |
1745 | |
1746 | |
1747 /** | |
1748 * Expands a given range if it collapses into one point. | |
1749 * | |
1750 * @param range Range to be expanded if upper == lower bound. | |
1751 * | |
1752 * @return Bounds of point plus 5 percent in each direction. | |
1753 */ | |
1754 private Bounds expandPointRange(Range range) { | |
1755 if (range == null) { | |
1756 return null; | |
1757 } | |
1758 else if (range.getLowerBound() == range.getUpperBound()) { | |
1759 Range expandedRange = ChartHelper.expandRange(range, 5d); | |
1760 return new DoubleBounds(expandedRange.getLowerBound(), expandedRange.getUpperBound()); | |
1761 } | |
1762 | |
1763 return new DoubleBounds(range.getLowerBound(), range.getUpperBound()); | |
1764 } | |
1765 | |
1766 | |
1767 /** | |
1768 * Creates a new instance of EnhancedLineAndShapeRenderer. | |
1769 * | |
1770 * @param plot The plot which is set for the new renderer. | |
1771 * @param idx This value is not used in the current implementation. | |
1772 * | |
1773 * @return a new instance of EnhancedLineAndShapeRenderer. | |
1774 */ | |
1775 protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) { | |
1776 logger.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx); | |
1777 | |
1778 EnhancedLineAndShapeRenderer r = | |
1779 new EnhancedLineAndShapeRenderer(true, false); | |
1780 | |
1781 r.setPlot(plot); | |
1782 | |
1783 return r; | |
1784 } | |
1785 | |
1786 | |
1787 /** | |
1788 * Creates a new instance of <i>IdentifiableNumberAxis</i>. | |
1789 * | |
1790 * @param idx The index of the new axis. | |
1791 * @param label The label of the new axis. | |
1792 * | |
1793 * @return an instance of IdentifiableNumberAxis. | |
1794 */ | |
1795 protected NumberAxis createNumberAxis(int idx, String label) { | |
1796 YAxisWalker walker = getYAxisWalker(); | |
1797 | |
1798 return new IdentifiableNumberAxis(walker.getId(idx), label); | |
1799 } | |
1800 | |
1801 | |
1802 /** | |
1803 * Create Y (range) axis for given index. | |
1804 * Shall be overriden by subclasses. | |
1805 */ | |
1806 protected NumberAxis createYAxis(int index) { | |
1807 YAxisWalker walker = getYAxisWalker(); | |
1808 | |
1809 Font labelFont = new Font( | |
1810 DEFAULT_FONT_NAME, | |
1811 Font.BOLD, | |
1812 getYAxisFontSize(index)); | |
1813 | |
1814 IdentifiableNumberAxis axis = new IdentifiableNumberAxis( | |
1815 walker.getId(index), | |
1816 getYAxisLabel(index)); | |
1817 | |
1818 axis.setAutoRangeIncludesZero(false); | |
1819 axis.setLabelFont(labelFont); | |
1820 axis.setTickLabelFont(labelFont); | |
1821 | |
1822 return axis; | |
1823 } | |
1824 | |
1825 | |
1826 /** | |
1827 * Creates a new LegendItem with <i>name</i> and font provided by | |
1828 * <i>createLegendLabelFont()</i>. | |
1829 * | |
1830 * @param theme The theme of the chart line. | |
1831 * @param name The displayed name of the item. | |
1832 * | |
1833 * @return a new LegendItem instance. | |
1834 */ | |
1835 public LegendItem createLegendItem(ThemeDocument theme, String name) { | |
1836 // OPTIMIZE Pass font, parsed Theme items. | |
1837 | |
1838 Color color = theme.parseLineColorField(); | |
1839 if (color == null) { | |
1840 color = Color.BLACK; | |
1841 } | |
1842 | |
1843 LegendItem legendItem = new LegendItem(name, color); | |
1844 | |
1845 legendItem.setLabelFont(createLegendLabelFont()); | |
1846 return legendItem; | |
1847 } | |
1848 | |
1849 | |
1850 /** | |
1851 * Creates Font (Family and size) to use when creating Legend Items. The | |
1852 * font size depends in the return value of <i>getLegendFontSize()</i>. | |
1853 * | |
1854 * @return a new Font instance with <i>DEFAULT_FONT_NAME</i>. | |
1855 */ | |
1856 protected Font createLegendLabelFont() { | |
1857 return new Font( | |
1858 DEFAULT_FONT_NAME, | |
1859 Font.PLAIN, | |
1860 getLegendFontSize() | |
1861 ); | |
1862 } | |
1863 | |
1864 | |
1865 /** | |
1866 * Create new legend entries, dependent on settings. | |
1867 * @param plot The plot for which to modify the legend. | |
1868 */ | |
1869 public void aggregateLegendEntries(XYPlot plot) { | |
1870 int AGGR_THRESHOLD = 0; | |
1871 | |
1872 if (getChartSettings() == null) { | |
1873 return; | |
1874 } | |
1875 Integer threshold = getChartSettings().getLegendSection() | |
1876 .getAggregationThreshold(); | |
1877 | |
1878 AGGR_THRESHOLD = (threshold != null) ? threshold.intValue() : 0; | |
1879 | |
1880 LegendProcessor.aggregateLegendEntries(plot, AGGR_THRESHOLD); | |
1881 } | |
1882 | |
1883 | |
1884 /** | |
1885 * Returns a transparently textured paint. | |
1886 * | |
1887 * @return a transparently textured paint. | |
1888 */ | |
1889 protected static Paint createTransparentPaint() { | |
1890 // TODO why not use a transparent color? | |
1891 BufferedImage texture = new BufferedImage( | |
1892 1, 1, BufferedImage.TYPE_4BYTE_ABGR); | |
1893 | |
1894 return new TexturePaint( | |
1895 texture, new Rectangle2D.Double(0d, 0d, 0d, 0d)); | |
1896 } | |
1897 | |
1898 | |
1899 protected void preparePDFContext(CallContext context) { | |
1900 int[] dimension = getExportDimension(); | |
1901 | |
1902 context.putContextValue("chart.width", dimension[0]); | |
1903 context.putContextValue("chart.height", dimension[1]); | |
1904 context.putContextValue("chart.marginLeft", 5f); | |
1905 context.putContextValue("chart.marginRight", 5f); | |
1906 context.putContextValue("chart.marginTop", 5f); | |
1907 context.putContextValue("chart.marginBottom", 5f); | |
1908 context.putContextValue( | |
1909 "chart.page.format", | |
1910 ChartExportHelper.DEFAULT_PAGE_SIZE); | |
1911 } | |
1912 | |
1913 | |
1914 protected void prepareSVGContext(CallContext context) { | |
1915 int[] dimension = getExportDimension(); | |
1916 | |
1917 context.putContextValue("chart.width", dimension[0]); | |
1918 context.putContextValue("chart.height", dimension[1]); | |
1919 context.putContextValue( | |
1920 "chart.encoding", | |
1921 ChartExportHelper.DEFAULT_ENCODING); | |
1922 } | |
1923 | |
1924 /** | |
1925 * Retuns the call context. May be null if init hasn't been called yet. | |
1926 * | |
1927 * @return the CallContext instance | |
1928 */ | |
1929 public CallContext getCallContext() { | |
1930 return context; | |
1931 } | |
1932 } |