Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java @ 7116:3c7471b929d1
Merge branch generator-refectoring into default.
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Tue, 24 Sep 2013 12:29:44 +0200 |
parents | 41567bf1e131 |
children | 8c20f16ab650 |
comparison
equal
deleted
inserted
replaced
7095:bdadffad35b1 | 7116:3c7471b929d1 |
---|---|
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 java.awt.BasicStroke; | |
12 import java.awt.Color; | |
13 import java.awt.Font; | |
14 import java.awt.Graphics2D; | |
15 import java.awt.Paint; | |
16 import java.awt.Stroke; | |
17 import java.awt.TexturePaint; | |
18 import java.awt.Transparency; | |
19 | |
20 import java.awt.geom.Rectangle2D; | |
21 | |
22 import java.awt.image.BufferedImage; | |
23 | |
24 import java.io.IOException; | |
25 import java.io.OutputStream; | |
26 | |
27 import java.util.ArrayList; | |
28 import java.util.List; | |
29 import java.util.Locale; | |
30 import java.util.Map; | |
31 import java.util.SortedMap; | |
32 import java.util.TreeMap; | |
33 | |
34 import javax.xml.xpath.XPathConstants; | |
35 | |
36 import org.apache.log4j.Logger; | |
37 | |
38 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; | |
39 import org.dive4elements.artifactdatabase.state.Settings; | |
40 | |
41 import org.dive4elements.artifacts.Artifact; | |
42 import org.dive4elements.artifacts.ArtifactNamespaceContext; | |
43 import org.dive4elements.artifacts.CallContext; | |
44 import org.dive4elements.artifacts.CallMeta; | |
45 import org.dive4elements.artifacts.PreferredLocale; | |
46 | |
47 import org.dive4elements.artifacts.common.utils.XMLUtils; | |
48 | |
49 import org.dive4elements.river.artifacts.D4EArtifact; | |
50 | |
51 import org.dive4elements.river.artifacts.access.RangeAccess; | |
52 | |
53 import org.dive4elements.river.artifacts.resources.Resources; | |
54 | |
55 import org.dive4elements.river.collections.D4EArtifactCollection; | |
56 | |
57 import org.dive4elements.river.java2d.NOPGraphics2D; | |
58 | |
59 import org.dive4elements.river.jfree.AxisDataset; | |
60 import org.dive4elements.river.jfree.Bounds; | |
61 import org.dive4elements.river.jfree.DoubleBounds; | |
62 import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer; | |
63 import org.dive4elements.river.jfree.RiverAnnotation; | |
64 import org.dive4elements.river.jfree.StableXYDifferenceRenderer; | |
65 import org.dive4elements.river.jfree.Style; | |
66 import org.dive4elements.river.jfree.StyledAreaSeriesCollection; | |
67 import org.dive4elements.river.jfree.StyledSeries; | |
68 | |
69 import org.dive4elements.river.model.River; | |
70 | |
71 import org.dive4elements.river.themes.ThemeDocument; | |
72 | |
73 import org.dive4elements.river.utils.Formatter; | |
74 import org.dive4elements.river.utils.RiverUtils; | |
75 | |
76 import org.jfree.chart.ChartRenderingInfo; | |
77 import org.jfree.chart.JFreeChart; | |
78 import org.jfree.chart.LegendItem; | |
79 import org.jfree.chart.LegendItemCollection; | |
80 | |
81 import org.jfree.chart.axis.NumberAxis; | |
82 | |
83 import org.jfree.chart.plot.XYPlot; | |
84 | |
85 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; | |
86 | |
87 import org.jfree.chart.title.TextTitle; | |
88 | |
89 import org.jfree.data.Range; | |
90 | |
91 import org.jfree.data.general.Series; | |
92 | |
93 import org.jfree.data.xy.XYDataset; | |
94 | |
95 import org.jfree.ui.RectangleInsets; | |
96 | |
97 import org.w3c.dom.Document; | |
98 import org.w3c.dom.Element; | |
99 | |
100 /** | |
101 * Implementation of the OutGenerator interface for charts. | |
102 * It should provide some basic things that equal in all chart types. | |
103 * | |
104 */ | |
105 public abstract class ChartGenerator2 implements OutGenerator { | |
106 | |
107 private static Logger logger = Logger.getLogger(ChartGenerator2.class); | |
108 | |
109 public static final boolean USE_NOP_GRAPHICS = | |
110 Boolean.getBoolean("info.rendering.nop.graphics"); | |
111 | |
112 | |
113 public static final int DEFAULT_CHART_WIDTH = 600; | |
114 public static final int DEFAULT_CHART_HEIGHT = 400; | |
115 public static final String DEFAULT_CHART_FORMAT = "png"; | |
116 public static final Color DEFAULT_GRID_COLOR = Color.GRAY; | |
117 public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f; | |
118 public static final int DEFAULT_FONT_SIZE = 12; | |
119 public static final String DEFAULT_FONT_NAME = "Tahoma"; | |
120 | |
121 | |
122 public static final String XPATH_CHART_SIZE = | |
123 "/art:action/art:attributes/art:size"; | |
124 | |
125 public static final String XPATH_CHART_FORMAT = | |
126 "/art:action/art:attributes/art:format/@art:value"; | |
127 | |
128 public static final String XPATH_CHART_X_RANGE = | |
129 "/art:action/art:attributes/art:xrange"; | |
130 | |
131 public static final String XPATH_CHART_Y_RANGE = | |
132 "/art:action/art:attributes/art:yrange"; | |
133 | |
134 | |
135 /** The document of the incoming out() request.*/ | |
136 protected Document request; | |
137 | |
138 /** The output stream where the data should be written to.*/ | |
139 protected OutputStream out; | |
140 | |
141 /** The CallContext object.*/ | |
142 protected CallContext context; | |
143 | |
144 protected D4EArtifactCollection collection; | |
145 | |
146 /** The artifact that is used to decorate the chart with meta information.*/ | |
147 protected Artifact master; | |
148 | |
149 /** The settings that should be used during output creation.*/ | |
150 protected Settings settings; | |
151 | |
152 /** Map of datasets ("index"). */ | |
153 protected SortedMap<Integer, AxisDataset> datasets; | |
154 | |
155 /** List of annotations to insert in plot. */ | |
156 protected List<RiverAnnotation> annotations = new ArrayList<RiverAnnotation>(); | |
157 | |
158 protected abstract List<AxisSection> buildYAxisSections(); | |
159 | |
160 protected String outName; | |
161 | |
162 /** | |
163 * Default constructor that initializes internal data structures. | |
164 */ | |
165 public ChartGenerator2() { | |
166 datasets = new TreeMap<Integer, AxisDataset>(); | |
167 } | |
168 | |
169 /** | |
170 * Adds annotations to list. The given annotation will be visible. | |
171 */ | |
172 public void addAnnotations(RiverAnnotation annotation) { | |
173 annotations.add(annotation); | |
174 } | |
175 | |
176 /** | |
177 * This method needs to be implemented by concrete subclasses to create new | |
178 * instances of JFreeChart. | |
179 * | |
180 * @return a new instance of a JFreeChart. | |
181 */ | |
182 public abstract JFreeChart generateChart(); | |
183 | |
184 | |
185 /** For every outable (i.e. facets), this function is | |
186 * called and handles the data accordingly. */ | |
187 @Override | |
188 public abstract void doOut( | |
189 ArtifactAndFacet bundle, | |
190 ThemeDocument attr, | |
191 boolean visible); | |
192 | |
193 | |
194 | |
195 protected abstract Series getSeriesOf(XYDataset dataset, int idx); | |
196 | |
197 /** | |
198 * Returns the default title of a chart. | |
199 * | |
200 * @return the default title of a chart. | |
201 */ | |
202 protected abstract String getDefaultChartTitle(); | |
203 | |
204 protected abstract String getDefaultYAxisLabel(String axisName); | |
205 | |
206 | |
207 /** | |
208 * Returns the default X-Axis label of a chart. | |
209 * | |
210 * @return the default X-Axis label of a chart. | |
211 */ | |
212 protected abstract String getDefaultXAxisLabel(); | |
213 | |
214 /** | |
215 * This method is used to create new AxisDataset instances which may differ | |
216 * in concrete subclasses. | |
217 * | |
218 * @param idx The index of an axis. | |
219 */ | |
220 protected abstract AxisDataset createAxisDataset(int idx); | |
221 | |
222 | |
223 /** | |
224 * Combines the ranges of the X axis at index <i>idx</i>. | |
225 * | |
226 * @param bounds A new Bounds. | |
227 * @param idx The index of the X axis that should be comined with | |
228 * <i>range</i>. | |
229 */ | |
230 protected abstract void combineXBounds(Bounds bounds, int idx); | |
231 | |
232 | |
233 /** | |
234 * Combines the ranges of the Y axis at index <i>idx</i>. | |
235 * | |
236 * @param bounds A new Bounds. | |
237 * @param index The index of the Y axis that should be comined with. | |
238 * <i>range</i>. | |
239 */ | |
240 protected abstract void combineYBounds(Bounds bounds, int index); | |
241 | |
242 | |
243 /** | |
244 * This method is used to determine the ranges for axes at a given index. | |
245 * | |
246 * @param index The index of the axes at the plot. | |
247 * | |
248 * @return a Range[] with [xrange, yrange]; | |
249 */ | |
250 public abstract Range[] getRangesForAxis(int index); | |
251 | |
252 public abstract Bounds getXBounds(int axis); | |
253 | |
254 protected abstract void setXBounds(int axis, Bounds bounds); | |
255 | |
256 public abstract Bounds getYBounds(int axis); | |
257 | |
258 protected abstract void setYBounds(int axis, Bounds bounds); | |
259 | |
260 | |
261 /** | |
262 * This method retrieves the chart subtitle by calling getChartSubtitle() | |
263 * and adds it as TextTitle to the chart. | |
264 * The default implementation of getChartSubtitle() returns the same | |
265 * as getDefaultChartSubtitle() which must be implemented by derived | |
266 * classes. If you want to add multiple subtitles to the chart override | |
267 * this method and add your subtitles manually. | |
268 * | |
269 * @param chart The JFreeChart chart object. | |
270 */ | |
271 protected void addSubtitles(JFreeChart chart) { | |
272 String subtitle = getChartSubtitle(); | |
273 | |
274 if (subtitle != null && subtitle.length() > 0) { | |
275 chart.addSubtitle(new TextTitle(subtitle)); | |
276 } | |
277 } | |
278 | |
279 /** | |
280 * Generate chart. | |
281 */ | |
282 @Override | |
283 public void generate() throws IOException { | |
284 | |
285 logger.debug("ChartGenerator2.generate"); | |
286 | |
287 if (outName.indexOf("chartinfo") > 0) { | |
288 generateInfo(); | |
289 } | |
290 else { | |
291 generateImage(); | |
292 } | |
293 } | |
294 | |
295 | |
296 /** Generate only meta infos */ | |
297 private void generateInfo() throws IOException { | |
298 | |
299 logger.debug("ChartInfoGenerator2.generateInfo"); | |
300 | |
301 JFreeChart chart = generateChart(); | |
302 | |
303 int[] size = getSize(); | |
304 if (size == null) { | |
305 size = getDefaultSize(); | |
306 } | |
307 | |
308 ChartRenderingInfo info = new ChartRenderingInfo(); | |
309 | |
310 long startTime = System.currentTimeMillis(); | |
311 | |
312 if (USE_NOP_GRAPHICS) { | |
313 BufferedImage image = | |
314 new BufferedImage(size[0], size[1], Transparency.BITMASK); | |
315 | |
316 Graphics2D g2d = image.createGraphics(); | |
317 Graphics2D nop = new NOPGraphics2D(g2d); | |
318 | |
319 chart.draw( | |
320 nop, | |
321 new Rectangle2D.Double(0, 0, size[0], size[1]), | |
322 null, | |
323 info); | |
324 | |
325 nop.dispose(); | |
326 } | |
327 else { | |
328 chart.createBufferedImage( | |
329 size[0], size[1], Transparency.BITMASK, info); | |
330 } | |
331 | |
332 long stopTime = System.currentTimeMillis(); | |
333 | |
334 if (logger.isDebugEnabled()) { | |
335 logger.debug("Rendering info took: " + | |
336 (stopTime-startTime) + "ms"); | |
337 } | |
338 | |
339 | |
340 InfoGeneratorHelper2 helper = new InfoGeneratorHelper2(this); | |
341 Document doc = helper.createInfoDocument(chart, info); | |
342 | |
343 XMLUtils.toStream(doc, out); | |
344 } | |
345 | |
346 /** Generate the diagram as an image. */ | |
347 private void generateImage() throws IOException { | |
348 logger.debug("ChartGenerator2.generateImage"); | |
349 | |
350 JFreeChart chart = generateChart(); | |
351 | |
352 String format = getFormat(); | |
353 int[] size = getSize(); | |
354 | |
355 if (size == null) { | |
356 size = getExportDimension(); | |
357 } | |
358 | |
359 context.putContextValue("chart.width", size[0]); | |
360 context.putContextValue("chart.height", size[1]); | |
361 | |
362 if (format.equals(ChartExportHelper.FORMAT_PNG)) { | |
363 context.putContextValue("chart.image.format", "png"); | |
364 | |
365 ChartExportHelper.exportImage( | |
366 out, | |
367 chart, | |
368 context); | |
369 } | |
370 else if (format.equals(ChartExportHelper.FORMAT_PDF)) { | |
371 preparePDFContext(context); | |
372 | |
373 ChartExportHelper.exportPDF( | |
374 out, | |
375 chart, | |
376 context); | |
377 } | |
378 else if (format.equals(ChartExportHelper.FORMAT_SVG)) { | |
379 prepareSVGContext(context); | |
380 | |
381 ChartExportHelper.exportSVG( | |
382 out, | |
383 chart, | |
384 context); | |
385 } | |
386 else if (format.equals(ChartExportHelper.FORMAT_CSV)) { | |
387 context.putContextValue("chart.image.format", "csv"); | |
388 | |
389 ChartExportHelper.exportCSV( | |
390 out, | |
391 chart, | |
392 context); | |
393 } | |
394 } | |
395 | |
396 | |
397 @Override | |
398 public void init(String outName, Document request, OutputStream out, CallContext context) { | |
399 logger.debug("ChartGenerator2.init"); | |
400 | |
401 this.outName = outName; | |
402 this.request = request; | |
403 this.out = out; | |
404 this.context = context; | |
405 } | |
406 | |
407 | |
408 /** Sets the master artifact. */ | |
409 @Override | |
410 public void setMasterArtifact(Artifact master) { | |
411 this.master = master; | |
412 } | |
413 | |
414 | |
415 /** | |
416 * Gets the master artifact. | |
417 * @return the master artifact. | |
418 */ | |
419 public Artifact getMaster() { | |
420 return master; | |
421 } | |
422 | |
423 | |
424 /** Sets the collection. */ | |
425 @Override | |
426 public void setCollection(D4EArtifactCollection collection) { | |
427 this.collection = collection; | |
428 } | |
429 | |
430 | |
431 @Override | |
432 public void setSettings(Settings settings) { | |
433 this.settings = settings; | |
434 } | |
435 | |
436 | |
437 /** | |
438 * Returns an instance of <i>ChartSettings</i> with a chart specific section | |
439 * but with no axes settings. | |
440 * | |
441 * @return an instance of <i>ChartSettings</i>. | |
442 */ | |
443 @Override | |
444 public Settings getSettings() { | |
445 if (this.settings != null) { | |
446 return this.settings; | |
447 } | |
448 | |
449 ChartSettings settings = new ChartSettings(); | |
450 | |
451 ChartSection chartSection = buildChartSection(); | |
452 LegendSection legendSection = buildLegendSection(); | |
453 ExportSection exportSection = buildExportSection(); | |
454 | |
455 settings.setChartSection(chartSection); | |
456 settings.setLegendSection(legendSection); | |
457 settings.setExportSection(exportSection); | |
458 | |
459 List<AxisSection> axisSections = buildAxisSections(); | |
460 for (AxisSection axisSection: axisSections) { | |
461 settings.addAxisSection(axisSection); | |
462 } | |
463 | |
464 return settings; | |
465 } | |
466 | |
467 | |
468 /** | |
469 * Creates a new <i>ChartSection</i>. | |
470 * | |
471 * @return a new <i>ChartSection</i>. | |
472 */ | |
473 protected ChartSection buildChartSection() { | |
474 ChartSection chartSection = new ChartSection(); | |
475 chartSection.setTitle(getChartTitle()); | |
476 chartSection.setSubtitle(getChartSubtitle()); | |
477 chartSection.setDisplayGrid(isGridVisible()); | |
478 chartSection.setDisplayLogo(showLogo()); | |
479 chartSection.setLogoVPlacement(logoVPlace()); | |
480 chartSection.setLogoHPlacement(logoHPlace()); | |
481 return chartSection; | |
482 } | |
483 | |
484 | |
485 /** | |
486 * Creates a new <i>LegendSection</i>. | |
487 * | |
488 * @return a new <i>LegendSection</i>. | |
489 */ | |
490 protected LegendSection buildLegendSection() { | |
491 LegendSection legendSection = new LegendSection(); | |
492 legendSection.setVisibility(isLegendVisible()); | |
493 legendSection.setFontSize(getLegendFontSize()); | |
494 legendSection.setAggregationThreshold(10); | |
495 return legendSection; | |
496 } | |
497 | |
498 | |
499 /** | |
500 * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b> | |
501 * and <b>HEIGHT=400</b>. | |
502 * | |
503 * @return a new <i>ExportSection</i>. | |
504 */ | |
505 protected ExportSection buildExportSection() { | |
506 ExportSection exportSection = new ExportSection(); | |
507 exportSection.setWidth(600); | |
508 exportSection.setHeight(400); | |
509 return exportSection; | |
510 } | |
511 | |
512 | |
513 /** | |
514 * Creates a list of Sections that contains all axes of the chart (including | |
515 * X and Y axes). | |
516 * | |
517 * @return a list of Sections for each axis in this chart. | |
518 */ | |
519 protected List<AxisSection> buildAxisSections() { | |
520 List<AxisSection> axisSections = new ArrayList<AxisSection>(); | |
521 | |
522 axisSections.addAll(buildXAxisSections()); | |
523 axisSections.addAll(buildYAxisSections()); | |
524 | |
525 return axisSections; | |
526 } | |
527 | |
528 | |
529 /** | |
530 * Creates a new Section for chart's X axis. | |
531 * | |
532 * @return a List that contains a Section for the X axis. | |
533 */ | |
534 protected List<AxisSection> buildXAxisSections() { | |
535 List<AxisSection> axisSections = new ArrayList<AxisSection>(); | |
536 | |
537 String identifier = "X"; | |
538 | |
539 AxisSection axisSection = new AxisSection(); | |
540 axisSection.setIdentifier(identifier); | |
541 axisSection.setLabel(getXAxisLabel()); | |
542 axisSection.setFontSize(14); | |
543 axisSection.setFixed(false); | |
544 | |
545 // XXX We are able to find better default ranges that [0,0], but the Y | |
546 // axes currently have no better ranges set. | |
547 axisSection.setUpperRange(0d); | |
548 axisSection.setLowerRange(0d); | |
549 | |
550 axisSections.add(axisSection); | |
551 | |
552 return axisSections; | |
553 } | |
554 | |
555 | |
556 /** | |
557 * Returns the <i>settings</i> as <i>ChartSettings</i>. | |
558 * | |
559 * @return the <i>settings</i> as <i>ChartSettings</i> or null, if | |
560 * <i>settings</i> is not an instance of <i>ChartSettings</i>. | |
561 */ | |
562 public ChartSettings getChartSettings() { | |
563 if (settings instanceof ChartSettings) { | |
564 return (ChartSettings) settings; | |
565 } | |
566 | |
567 return null; | |
568 } | |
569 | |
570 | |
571 /** | |
572 * Returns the chart title provided by <i>settings</i>. | |
573 * | |
574 * @param settings A ChartSettings object. | |
575 * | |
576 * @return the title provided by <i>settings</i> or null if no | |
577 * <i>ChartSection</i> is provided by <i>settings</i>. | |
578 * | |
579 * @throws NullPointerException if <i>settings</i> is null. | |
580 */ | |
581 public String getChartTitle(ChartSettings settings) { | |
582 ChartSection cs = settings.getChartSection(); | |
583 return cs != null ? cs.getTitle() : null; | |
584 } | |
585 | |
586 | |
587 /** | |
588 * Returns the chart subtitle provided by <i>settings</i>. | |
589 * | |
590 * @param settings A ChartSettings object. | |
591 * | |
592 * @return the subtitle provided by <i>settings</i> or null if no | |
593 * <i>ChartSection</i> is provided by <i>settings</i>. | |
594 * | |
595 * @throws NullPointerException if <i>settings</i> is null. | |
596 */ | |
597 public String getChartSubtitle(ChartSettings settings) { | |
598 ChartSection cs = settings.getChartSection(); | |
599 return cs != null ? cs.getSubtitle() : null; | |
600 } | |
601 | |
602 | |
603 /** | |
604 * Returns a boolean object that determines if the chart grid should be | |
605 * visible or not. This information needs to be provided by <i>settings</i>, | |
606 * otherweise the default is true. | |
607 * | |
608 * @param settings A ChartSettings object. | |
609 * | |
610 * @return true, if the chart grid should be visible otherwise false. | |
611 * | |
612 * @throws NullPointerException if <i>settings</i> is null. | |
613 */ | |
614 public boolean isGridVisible(ChartSettings settings) { | |
615 ChartSection cs = settings.getChartSection(); | |
616 Boolean displayGrid = cs.getDisplayGrid(); | |
617 | |
618 return displayGrid != null ? displayGrid : true; | |
619 } | |
620 | |
621 | |
622 /** | |
623 * Returns a boolean object that determines if the chart legend should be | |
624 * visible or not. This information needs to be provided by <i>settings</i>, | |
625 * otherwise the default is true. | |
626 * | |
627 * @param settings A ChartSettings object. | |
628 * | |
629 * @return true, if the chart legend should be visible otherwise false. | |
630 * | |
631 * @throws NullPointerException if <i>settings</i> is null. | |
632 */ | |
633 public boolean isLegendVisible(ChartSettings settings) { | |
634 LegendSection ls = settings.getLegendSection(); | |
635 Boolean displayLegend = ls.getVisibility(); | |
636 | |
637 return displayLegend != null ? displayLegend : true; | |
638 } | |
639 | |
640 | |
641 /** | |
642 * Returns the legend font size specified in <i>settings</i> or null if no | |
643 * <i>LegendSection</i> is provided by <i>settings</i>. | |
644 * | |
645 * @param settings A ChartSettings object. | |
646 * | |
647 * @return the legend font size or null. | |
648 * | |
649 * @throws NullPointerException if <i>settings</i> is null. | |
650 */ | |
651 public Integer getLegendFontSize(ChartSettings settings) { | |
652 LegendSection ls = settings.getLegendSection(); | |
653 return ls != null ? ls.getFontSize() : null; | |
654 } | |
655 | |
656 | |
657 /** | |
658 * Returns the title of a chart. The return value depends on the existence | |
659 * of ChartSettings: if there are ChartSettings set, this method returns the | |
660 * chart title provided by those settings. Otherwise, this method returns | |
661 * getDefaultChartTitle(). | |
662 * | |
663 * @return the title of a chart. | |
664 */ | |
665 protected String getChartTitle() { | |
666 ChartSettings chartSettings = getChartSettings(); | |
667 | |
668 if (chartSettings != null) { | |
669 return getChartTitle(chartSettings); | |
670 } | |
671 | |
672 return getDefaultChartTitle(); | |
673 } | |
674 | |
675 | |
676 /** | |
677 * Returns the subtitle of a chart. The return value depends on the | |
678 * existence of ChartSettings: if there are ChartSettings set, this method | |
679 * returns the chart title provided by those settings. Otherwise, this | |
680 * method returns getDefaultChartSubtitle(). | |
681 * | |
682 * @return the subtitle of a chart. | |
683 */ | |
684 protected String getChartSubtitle() { | |
685 ChartSettings chartSettings = getChartSettings(); | |
686 | |
687 if (chartSettings != null) { | |
688 return getChartSubtitle(chartSettings); | |
689 } | |
690 | |
691 return getDefaultChartSubtitle(); | |
692 } | |
693 | |
694 | |
695 /** | |
696 * This method always returns null. Override it in subclasses that require | |
697 * subtitles. | |
698 * | |
699 * @return null. | |
700 */ | |
701 protected String getDefaultChartSubtitle() { | |
702 // Override this method in subclasses | |
703 return null; | |
704 } | |
705 | |
706 | |
707 /** | |
708 * This method is used to determine, if the chart's legend is visible or | |
709 * not. If a <i>settings</i> instance is set, this instance determines the | |
710 * visibility otherwise, this method returns true as default if no | |
711 * <i>settings</i> is set. | |
712 * | |
713 * @return true, if the legend should be visible, otherwise false. | |
714 */ | |
715 protected boolean isLegendVisible() { | |
716 ChartSettings chartSettings = getChartSettings(); | |
717 if (chartSettings != null) { | |
718 return isLegendVisible(chartSettings); | |
719 } | |
720 | |
721 return true; | |
722 } | |
723 | |
724 | |
725 /** Where to place the logo. */ | |
726 protected String logoHPlace() { | |
727 ChartSettings chartSettings = getChartSettings(); | |
728 if (chartSettings != null) { | |
729 ChartSection cs = chartSettings.getChartSection(); | |
730 String place = cs.getLogoHPlacement(); | |
731 | |
732 return place; | |
733 } | |
734 return "center"; | |
735 } | |
736 | |
737 | |
738 /** Where to place the logo. */ | |
739 protected String logoVPlace() { | |
740 ChartSettings chartSettings = getChartSettings(); | |
741 if (chartSettings != null) { | |
742 ChartSection cs = chartSettings.getChartSection(); | |
743 String place = cs.getLogoVPlacement(); | |
744 | |
745 return place; | |
746 } | |
747 return "top"; | |
748 } | |
749 | |
750 | |
751 /** Return the logo id from settings. */ | |
752 protected String showLogo(ChartSettings chartSettings) { | |
753 if (chartSettings != null) { | |
754 ChartSection cs = chartSettings.getChartSection(); | |
755 String logo = cs.getDisplayLogo(); | |
756 | |
757 return logo; | |
758 } | |
759 return "none"; | |
760 } | |
761 | |
762 | |
763 /** | |
764 * This method is used to determine if a logo should be added to the plot. | |
765 * | |
766 * @return logo name (null if none). | |
767 */ | |
768 protected String showLogo() { | |
769 ChartSettings chartSettings = getChartSettings(); | |
770 return showLogo(chartSettings); | |
771 } | |
772 | |
773 | |
774 /** | |
775 * This method is used to determine the font size of the chart's legend. If | |
776 * a <i>settings</i> instance is set, this instance determines the font | |
777 * size, otherwise this method returns 12 as default if no <i>settings</i> | |
778 * is set or if it doesn't provide a legend font size. | |
779 * | |
780 * @return a legend font size. | |
781 */ | |
782 protected int getLegendFontSize() { | |
783 Integer fontSize = null; | |
784 | |
785 ChartSettings chartSettings = getChartSettings(); | |
786 if (chartSettings != null) { | |
787 fontSize = getLegendFontSize(chartSettings); | |
788 } | |
789 | |
790 return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; | |
791 } | |
792 | |
793 | |
794 /** | |
795 * This method is used to determine if the resulting chart should display | |
796 * grid lines or not. <b>Note: this method always returns true!</b> | |
797 * | |
798 * @return true, if the chart should display grid lines, otherwise false. | |
799 */ | |
800 protected boolean isGridVisible() { | |
801 return true; | |
802 } | |
803 | |
804 | |
805 /** | |
806 * Returns the X-Axis label of a chart. | |
807 * | |
808 * @return the X-Axis label of a chart. | |
809 */ | |
810 protected String getXAxisLabel() { | |
811 ChartSettings chartSettings = getChartSettings(); | |
812 if (chartSettings == null) { | |
813 return getDefaultXAxisLabel(); | |
814 } | |
815 | |
816 AxisSection as = chartSettings.getAxisSection("X"); | |
817 if (as != null) { | |
818 String label = as.getLabel(); | |
819 | |
820 if (label != null) { | |
821 return label; | |
822 } | |
823 } | |
824 | |
825 return getDefaultXAxisLabel(); | |
826 } | |
827 | |
828 | |
829 /** | |
830 * This method returns the font size for the X axis. If the font size is | |
831 * specified in ChartSettings (if <i>chartSettings</i> is set), this size is | |
832 * returned. Otherwise the default font size 12 is returned. | |
833 * | |
834 * @return the font size for the x axis. | |
835 */ | |
836 protected int getXAxisLabelFontSize() { | |
837 ChartSettings chartSettings = getChartSettings(); | |
838 if (chartSettings == null) { | |
839 return DEFAULT_FONT_SIZE; | |
840 } | |
841 | |
842 AxisSection as = chartSettings.getAxisSection("X"); | |
843 Integer fontSize = as.getFontSize(); | |
844 | |
845 return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; | |
846 } | |
847 | |
848 /** | |
849 * Glue between axis names and index. | |
850 */ | |
851 protected abstract String axisIndexToName(int index); | |
852 | |
853 /** | |
854 * This method returns the font size for an Y axis. If the font size is | |
855 * specified in ChartSettings (if <i>chartSettings</i> is set), this size is | |
856 * returned. Otherwise the default font size 12 is returned. | |
857 * | |
858 * @return the font size for the x axis. | |
859 */ | |
860 protected int getYAxisFontSize(int index) { | |
861 ChartSettings chartSettings = getChartSettings(); | |
862 if (chartSettings == null) { | |
863 return DEFAULT_FONT_SIZE; | |
864 } | |
865 | |
866 AxisSection as = chartSettings.getAxisSection(axisIndexToName(index)); | |
867 if (as == null) { | |
868 return DEFAULT_FONT_SIZE; | |
869 } | |
870 Integer fontSize = as.getFontSize(); | |
871 | |
872 return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; | |
873 } | |
874 | |
875 /** | |
876 * This method returns the export dimension specified in ChartSettings as | |
877 * int array [width,height]. | |
878 * | |
879 * @return an int array with [width,height]. | |
880 */ | |
881 protected int[] getExportDimension() { | |
882 ChartSettings chartSettings = getChartSettings(); | |
883 if (chartSettings == null) { | |
884 return new int[] { 600, 400 }; | |
885 } | |
886 | |
887 ExportSection export = chartSettings.getExportSection(); | |
888 Integer width = export.getWidth(); | |
889 Integer height = export.getHeight(); | |
890 | |
891 if (width != null && height != null) { | |
892 return new int[] { width, height }; | |
893 } | |
894 | |
895 return new int[] { 600, 400 }; | |
896 } | |
897 | |
898 protected abstract String getYAxisLabel(String axisName); | |
899 | |
900 /** | |
901 * This method searches for a specific axis in the <i>settings</i> if | |
902 * <i>settings</i> is set. If the axis was found, this method returns the | |
903 * specified axis range if the axis range is fixed. Otherwise, this method | |
904 * returns null. | |
905 * | |
906 * @param axisId The identifier of an axis. | |
907 * | |
908 * @return the specified axis range from <i>settings</i> if the axis is | |
909 * fixed, otherwise null. | |
910 */ | |
911 public Range getRangeForAxisFromSettings(String axisId) { | |
912 ChartSettings chartSettings = getChartSettings(); | |
913 if (chartSettings == null) { | |
914 return null; | |
915 } | |
916 | |
917 AxisSection as = chartSettings.getAxisSection(axisId); | |
918 | |
919 if (as == null) { | |
920 return null; | |
921 } | |
922 | |
923 Boolean fixed = as.isFixed(); | |
924 | |
925 if (fixed != null && fixed) { | |
926 Double upper = as.getUpperRange(); | |
927 Double lower = as.getLowerRange(); | |
928 | |
929 if (upper != null && lower != null) { | |
930 return lower < upper | |
931 ? new Range(lower, upper) | |
932 : new Range(upper, lower); | |
933 } | |
934 } | |
935 | |
936 return null; | |
937 } | |
938 | |
939 | |
940 /** | |
941 * Adds a new AxisDataset which contains <i>dataset</i> at index <i>idx</i>. | |
942 * | |
943 * @param dataset An XYDataset. | |
944 * @param idx The axis index. | |
945 * @param visible Determines, if the dataset should be visible or not. | |
946 */ | |
947 public void addAxisDataset(XYDataset dataset, int idx, boolean visible) { | |
948 if (dataset == null || idx < 0) { | |
949 return; | |
950 } | |
951 | |
952 AxisDataset axisDataset = getAxisDataset(idx); | |
953 | |
954 Bounds[] xyBounds = ChartHelper.getBounds(dataset); | |
955 | |
956 if (xyBounds == null) { | |
957 logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx); | |
958 return; | |
959 } | |
960 | |
961 if (visible) { | |
962 if (logger.isDebugEnabled()) { | |
963 logger.debug("Add new AxisDataset at index: " + idx); | |
964 logger.debug("X extent: " + xyBounds[0]); | |
965 logger.debug("Y extent: " + xyBounds[1]); | |
966 } | |
967 | |
968 axisDataset.addDataset(dataset); | |
969 } | |
970 | |
971 combineXBounds(xyBounds[0], 0); | |
972 combineYBounds(xyBounds[1], idx); | |
973 } | |
974 | |
975 | |
976 /** | |
977 * This method grants access to the AxisDatasets stored in <i>datasets</i>. | |
978 * If no AxisDataset exists for index <i>idx</i>, a new AxisDataset is | |
979 * created using <i>createAxisDataset()</i>. | |
980 * | |
981 * @param idx The index of the desired AxisDataset. | |
982 * | |
983 * @return an existing or new AxisDataset. | |
984 */ | |
985 public AxisDataset getAxisDataset(int idx) { | |
986 AxisDataset axisDataset = datasets.get(idx); | |
987 | |
988 if (axisDataset == null) { | |
989 axisDataset = createAxisDataset(idx); | |
990 datasets.put(idx, axisDataset); | |
991 } | |
992 | |
993 return axisDataset; | |
994 } | |
995 | |
996 | |
997 /** | |
998 * Adjust some Stroke/Grid parameters for <i>plot</i>. The chart | |
999 * <i>Settings</i> are applied in this method. | |
1000 * | |
1001 * @param plot The XYPlot which is adapted. | |
1002 */ | |
1003 protected void adjustPlot(XYPlot plot) { | |
1004 Stroke gridStroke = new BasicStroke( | |
1005 DEFAULT_GRID_LINE_WIDTH, | |
1006 BasicStroke.CAP_BUTT, | |
1007 BasicStroke.JOIN_MITER, | |
1008 3.0f, | |
1009 new float[] { 3.0f }, | |
1010 0.0f); | |
1011 | |
1012 ChartSettings cs = getChartSettings(); | |
1013 boolean isGridVisible = cs != null ? isGridVisible(cs) : true; | |
1014 | |
1015 plot.setDomainGridlineStroke(gridStroke); | |
1016 plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR); | |
1017 plot.setDomainGridlinesVisible(isGridVisible); | |
1018 | |
1019 plot.setRangeGridlineStroke(gridStroke); | |
1020 plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR); | |
1021 plot.setRangeGridlinesVisible(isGridVisible); | |
1022 | |
1023 plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d)); | |
1024 } | |
1025 | |
1026 | |
1027 /** | |
1028 * This helper mehtod is used to extract the current locale from instance | |
1029 * vairable <i>context</i>. | |
1030 * | |
1031 * @return the current locale. | |
1032 */ | |
1033 protected Locale getLocale() { | |
1034 CallMeta meta = context.getMeta(); | |
1035 PreferredLocale[] prefs = meta.getLanguages(); | |
1036 | |
1037 int len = prefs != null ? prefs.length : 0; | |
1038 | |
1039 Locale[] locales = new Locale[len]; | |
1040 | |
1041 for (int i = 0; i < len; i++) { | |
1042 locales[i] = prefs[i].getLocale(); | |
1043 } | |
1044 | |
1045 return meta.getPreferredLocale(locales); | |
1046 } | |
1047 | |
1048 | |
1049 /** | |
1050 * Look up \param key in i18n dictionary. | |
1051 * @param key key for which to find i18nd version. | |
1052 * @param def default, returned if lookup failed. | |
1053 * @return value found in i18n dictionary, \param def if no value found. | |
1054 */ | |
1055 public String msg(String key, String def) { | |
1056 return Resources.getMsg(context.getMeta(), key, def); | |
1057 } | |
1058 | |
1059 /** | |
1060 * Look up \param key in i18n dictionary. | |
1061 * @param key key for which to find i18nd version. | |
1062 * @return value found in i18n dictionary, key itself if failed. | |
1063 */ | |
1064 public String msg(String key) { | |
1065 return Resources.getMsg(context.getMeta(), key, key); | |
1066 } | |
1067 | |
1068 public String msg(String key, String def, Object[] args) { | |
1069 return Resources.getMsg(context.getMeta(), key, def, args); | |
1070 } | |
1071 | |
1072 | |
1073 protected String getRiverName() { | |
1074 D4EArtifact flys = (D4EArtifact) master; | |
1075 | |
1076 River river = RiverUtils.getRiver(flys); | |
1077 return (river != null) ? river.getName() : ""; | |
1078 } | |
1079 | |
1080 | |
1081 protected double[] getRange() { | |
1082 D4EArtifact flys = (D4EArtifact) master; | |
1083 | |
1084 RangeAccess rangeAccess = new RangeAccess(flys); | |
1085 return rangeAccess.getKmRange(); | |
1086 } | |
1087 | |
1088 | |
1089 /** | |
1090 * Returns the size of a chart export as array which has been specified by | |
1091 * the incoming request document. | |
1092 * | |
1093 * @return the size of a chart as [width, height] or null if no width or | |
1094 * height are given in the request document. | |
1095 */ | |
1096 protected int[] getSize() { | |
1097 int[] size = new int[2]; | |
1098 | |
1099 Element sizeEl = (Element)XMLUtils.xpath( | |
1100 request, | |
1101 XPATH_CHART_SIZE, | |
1102 XPathConstants.NODE, | |
1103 ArtifactNamespaceContext.INSTANCE); | |
1104 | |
1105 if (sizeEl != null) { | |
1106 String uri = ArtifactNamespaceContext.NAMESPACE_URI; | |
1107 | |
1108 String w = sizeEl.getAttributeNS(uri, "width"); | |
1109 String h = sizeEl.getAttributeNS(uri, "height"); | |
1110 | |
1111 if (w.length() > 0 && h.length() > 0) { | |
1112 try { | |
1113 size[0] = Integer.parseInt(w); | |
1114 size[1] = Integer.parseInt(h); | |
1115 } | |
1116 catch (NumberFormatException nfe) { | |
1117 logger.warn("Wrong values for chart width/height."); | |
1118 } | |
1119 } | |
1120 } | |
1121 | |
1122 return size[0] > 0 && size[1] > 0 ? size : null; | |
1123 } | |
1124 | |
1125 | |
1126 /** | |
1127 * This method returns the format specified in the <i>request</i> document | |
1128 * or <i>DEFAULT_CHART_FORMAT</i> if no format is specified in | |
1129 * <i>request</i>. | |
1130 * | |
1131 * @return the format used to export this chart. | |
1132 */ | |
1133 protected String getFormat() { | |
1134 String format = (String) XMLUtils.xpath( | |
1135 request, | |
1136 XPATH_CHART_FORMAT, | |
1137 XPathConstants.STRING, | |
1138 ArtifactNamespaceContext.INSTANCE); | |
1139 | |
1140 return format == null || format.length() == 0 | |
1141 ? DEFAULT_CHART_FORMAT | |
1142 : format; | |
1143 } | |
1144 | |
1145 | |
1146 /** | |
1147 * Returns the X-Axis range as String array from request document. | |
1148 * If the (x|y)range elements are not found in request document, return | |
1149 * null (i.e. not zoomed). | |
1150 * | |
1151 * @return a String array with [lower, upper], null if not in document. | |
1152 */ | |
1153 protected String[] getDomainAxisRangeFromRequest() { | |
1154 Element xrange = (Element)XMLUtils.xpath( | |
1155 request, | |
1156 XPATH_CHART_X_RANGE, | |
1157 XPathConstants.NODE, | |
1158 ArtifactNamespaceContext.INSTANCE); | |
1159 | |
1160 if (xrange == null) { | |
1161 return null; | |
1162 } | |
1163 | |
1164 String uri = ArtifactNamespaceContext.NAMESPACE_URI; | |
1165 | |
1166 String lower = xrange.getAttributeNS(uri, "from"); | |
1167 String upper = xrange.getAttributeNS(uri, "to"); | |
1168 | |
1169 return new String[] { lower, upper }; | |
1170 } | |
1171 | |
1172 | |
1173 /** Returns null if the (x|y)range-element was not found in request document. | |
1174 * This usally means that the axis are not manually zoomed, i.e. showing | |
1175 * full data extent. */ | |
1176 protected String[] getValueAxisRangeFromRequest() { | |
1177 Element yrange = (Element)XMLUtils.xpath( | |
1178 request, | |
1179 XPATH_CHART_Y_RANGE, | |
1180 XPathConstants.NODE, | |
1181 ArtifactNamespaceContext.INSTANCE); | |
1182 | |
1183 if (yrange == null) { | |
1184 return null; | |
1185 } | |
1186 | |
1187 | |
1188 String uri = ArtifactNamespaceContext.NAMESPACE_URI; | |
1189 | |
1190 String lower = yrange.getAttributeNS(uri, "from"); | |
1191 String upper = yrange.getAttributeNS(uri, "to"); | |
1192 | |
1193 return new String[] { lower, upper }; | |
1194 } | |
1195 | |
1196 | |
1197 /** | |
1198 * Returns the default size of a chart export as array. | |
1199 * | |
1200 * @return the default size of a chart as [width, height]. | |
1201 */ | |
1202 protected int[] getDefaultSize() { | |
1203 return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT }; | |
1204 } | |
1205 | |
1206 | |
1207 /** | |
1208 * Add datasets stored in instance variable <i>datasets</i> to plot. | |
1209 * <i>datasets</i> actually stores instances of AxisDataset, so each of this | |
1210 * datasets is mapped to a specific axis as well. | |
1211 * | |
1212 * @param plot plot to add datasets to. | |
1213 */ | |
1214 protected void addDatasets(XYPlot plot) { | |
1215 logger.debug("addDatasets()"); | |
1216 | |
1217 // AxisDatasets are sorted, but some might be empty. | |
1218 // Thus, generate numbering on the fly. | |
1219 int axisIndex = 0; | |
1220 int datasetIndex = 0; | |
1221 | |
1222 for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) { | |
1223 if (!entry.getValue().isEmpty()) { | |
1224 // Add axis and range information. | |
1225 AxisDataset axisDataset = entry.getValue(); | |
1226 NumberAxis axis = createYAxis(entry.getKey()); | |
1227 | |
1228 plot.setRangeAxis(axisIndex, axis); | |
1229 | |
1230 if (axis.getAutoRangeIncludesZero()) { | |
1231 axisDataset.setRange( | |
1232 Range.expandToInclude(axisDataset.getRange(), 0d)); | |
1233 } | |
1234 | |
1235 setYBounds(axisIndex, expandPointRange(axisDataset.getRange())); | |
1236 | |
1237 // Add contained datasets, mapping to axis. | |
1238 for (XYDataset dataset: axisDataset.getDatasets()) { | |
1239 plot.setDataset(datasetIndex, dataset); | |
1240 plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); | |
1241 | |
1242 applyThemes(plot, dataset, | |
1243 datasetIndex, | |
1244 axisDataset.isArea(dataset)); | |
1245 | |
1246 datasetIndex++; | |
1247 } | |
1248 | |
1249 axisDataset.setPlotAxisIndex(axisIndex); | |
1250 axisIndex++; | |
1251 } | |
1252 } | |
1253 } | |
1254 | |
1255 | |
1256 /** | |
1257 * @param idx "index" of dataset/series (first dataset to be drawn has | |
1258 * index 0), correlates with renderer index. | |
1259 * @param isArea true if the series describes an area and shall be rendered | |
1260 * as such. | |
1261 */ | |
1262 protected void applyThemes( | |
1263 XYPlot plot, | |
1264 XYDataset series, | |
1265 int idx, | |
1266 boolean isArea | |
1267 ) { | |
1268 if (isArea) { | |
1269 applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx); | |
1270 } | |
1271 else { | |
1272 applyLineTheme(plot, series, idx); | |
1273 } | |
1274 } | |
1275 | |
1276 | |
1277 /** | |
1278 * This method applies the themes defined in the series itself. Therefore, | |
1279 * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer | |
1280 * for the series. | |
1281 * | |
1282 * @param plot The plot. | |
1283 * @param dataset The XYDataset which needs to support Series objects. | |
1284 * @param idx The index of the renderer / dataset. | |
1285 */ | |
1286 protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) { | |
1287 logger.debug("Apply LineTheme for dataset at index: " + idx); | |
1288 | |
1289 LegendItemCollection lic = new LegendItemCollection(); | |
1290 LegendItemCollection anno = plot.getFixedLegendItems(); | |
1291 | |
1292 Font legendFont = createLegendLabelFont(); | |
1293 | |
1294 XYLineAndShapeRenderer renderer = createRenderer(plot, idx); | |
1295 | |
1296 for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { | |
1297 Series series = getSeriesOf(dataset, s); | |
1298 | |
1299 if (series instanceof StyledSeries) { | |
1300 Style style = ((StyledSeries) series).getStyle(); | |
1301 style.applyTheme(renderer, s); | |
1302 } | |
1303 | |
1304 // special case: if there is just one single item, we need to enable | |
1305 // points for this series, otherwise we would not see anything in | |
1306 // the chart area. | |
1307 if (series.getItemCount() == 1) { | |
1308 renderer.setSeriesShapesVisible(s, true); | |
1309 } | |
1310 | |
1311 LegendItem legendItem = renderer.getLegendItem(idx, s); | |
1312 if (legendItem.getLabel().endsWith(" ") || | |
1313 legendItem.getLabel().endsWith("interpol")) { | |
1314 legendItem = null; | |
1315 } | |
1316 | |
1317 if (legendItem != null) { | |
1318 legendItem.setLabelFont(legendFont); | |
1319 lic.add(legendItem); | |
1320 } | |
1321 else { | |
1322 logger.warn("Could not get LegentItem for renderer: " | |
1323 + idx + ", series-idx " + s); | |
1324 } | |
1325 } | |
1326 | |
1327 if (anno != null) { | |
1328 lic.addAll(anno); | |
1329 } | |
1330 | |
1331 plot.setFixedLegendItems(lic); | |
1332 | |
1333 plot.setRenderer(idx, renderer); | |
1334 } | |
1335 | |
1336 | |
1337 /** | |
1338 * @param plot The plot. | |
1339 * @param area A StyledAreaSeriesCollection object. | |
1340 * @param idx The index of the dataset. | |
1341 */ | |
1342 protected void applyAreaTheme( | |
1343 XYPlot plot, | |
1344 StyledAreaSeriesCollection area, | |
1345 int idx | |
1346 ) { | |
1347 LegendItemCollection lic = new LegendItemCollection(); | |
1348 LegendItemCollection anno = plot.getFixedLegendItems(); | |
1349 | |
1350 Font legendFont = createLegendLabelFont(); | |
1351 | |
1352 logger.debug("Registering an 'area'renderer at idx: " + idx); | |
1353 | |
1354 StableXYDifferenceRenderer dRenderer = | |
1355 new StableXYDifferenceRenderer(); | |
1356 | |
1357 if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) { | |
1358 dRenderer.setPositivePaint(createTransparentPaint()); | |
1359 } | |
1360 | |
1361 plot.setRenderer(idx, dRenderer); | |
1362 | |
1363 area.applyTheme(dRenderer); | |
1364 | |
1365 // i18n | |
1366 dRenderer.setAreaLabelNumberFormat(Formatter.getFormatter(context.getMeta(), 2, 4)); | |
1367 | |
1368 dRenderer.setAreaLabelTemplate(Resources.getMsg( | |
1369 context.getMeta(), "area.label.template", "Area=%sm2")); | |
1370 | |
1371 LegendItem legendItem = dRenderer.getLegendItem(idx, 0); | |
1372 if (legendItem != null) { | |
1373 legendItem.setLabelFont(legendFont); | |
1374 lic.add(legendItem); | |
1375 } | |
1376 else { | |
1377 logger.warn("Could not get LegentItem for renderer: " | |
1378 + idx + ", series-idx " + 0); | |
1379 } | |
1380 | |
1381 if (anno != null) { | |
1382 lic.addAll(anno); | |
1383 } | |
1384 | |
1385 plot.setFixedLegendItems(lic); | |
1386 } | |
1387 | |
1388 | |
1389 /** | |
1390 * Expands a given range if it collapses into one point. | |
1391 * | |
1392 * @param range Range to be expanded if upper == lower bound. | |
1393 * | |
1394 * @return Bounds of point plus 5 percent in each direction. | |
1395 */ | |
1396 private Bounds expandPointRange(Range range) { | |
1397 if (range == null) { | |
1398 return null; | |
1399 } | |
1400 else if (range.getLowerBound() == range.getUpperBound()) { | |
1401 Range expandedRange = ChartHelper.expandRange(range, 5d); | |
1402 return new DoubleBounds(expandedRange.getLowerBound(), expandedRange.getUpperBound()); | |
1403 } | |
1404 | |
1405 return new DoubleBounds(range.getLowerBound(), range.getUpperBound()); | |
1406 } | |
1407 | |
1408 | |
1409 /** | |
1410 * Creates a new instance of EnhancedLineAndShapeRenderer. | |
1411 * | |
1412 * @param plot The plot which is set for the new renderer. | |
1413 * @param idx This value is not used in the current implementation. | |
1414 * | |
1415 * @return a new instance of EnhancedLineAndShapeRenderer. | |
1416 */ | |
1417 protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) { | |
1418 logger.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx); | |
1419 | |
1420 EnhancedLineAndShapeRenderer r = | |
1421 new EnhancedLineAndShapeRenderer(true, false); | |
1422 | |
1423 r.setPlot(plot); | |
1424 | |
1425 return r; | |
1426 } | |
1427 | |
1428 | |
1429 /** | |
1430 * Creates a new instance of <i>IdentifiableNumberAxis</i>. | |
1431 * | |
1432 * @param idx The index of the new axis. | |
1433 * @param label The label of the new axis. | |
1434 * | |
1435 * @return an instance of IdentifiableNumberAxis. | |
1436 */ | |
1437 protected NumberAxis createNumberAxis(int idx, String label) { | |
1438 return new IdentifiableNumberAxis(axisIndexToName(idx), label); | |
1439 } | |
1440 | |
1441 | |
1442 /** | |
1443 * Create Y (range) axis for given index. | |
1444 * Shall be overriden by subclasses. | |
1445 */ | |
1446 protected NumberAxis createYAxis(int index) { | |
1447 | |
1448 Font labelFont = new Font( | |
1449 DEFAULT_FONT_NAME, | |
1450 Font.BOLD, | |
1451 getYAxisFontSize(index)); | |
1452 | |
1453 String axisName = axisIndexToName(index); | |
1454 | |
1455 IdentifiableNumberAxis axis = new IdentifiableNumberAxis( | |
1456 axisName, getYAxisLabel(axisName)); | |
1457 | |
1458 axis.setAutoRangeIncludesZero(false); | |
1459 axis.setLabelFont(labelFont); | |
1460 axis.setTickLabelFont(labelFont); | |
1461 | |
1462 return axis; | |
1463 } | |
1464 | |
1465 | |
1466 /** | |
1467 * Creates a new LegendItem with <i>name</i> and font provided by | |
1468 * <i>createLegendLabelFont()</i>. | |
1469 * | |
1470 * @param theme The theme of the chart line. | |
1471 * @param name The displayed name of the item. | |
1472 * | |
1473 * @return a new LegendItem instance. | |
1474 */ | |
1475 public LegendItem createLegendItem(ThemeDocument theme, String name) { | |
1476 // OPTIMIZE Pass font, parsed Theme items. | |
1477 | |
1478 Color color = theme.parseLineColorField(); | |
1479 if (color == null) { | |
1480 color = Color.BLACK; | |
1481 } | |
1482 | |
1483 LegendItem legendItem = new LegendItem(name, color); | |
1484 | |
1485 legendItem.setLabelFont(createLegendLabelFont()); | |
1486 return legendItem; | |
1487 } | |
1488 | |
1489 | |
1490 /** | |
1491 * Creates Font (Family and size) to use when creating Legend Items. The | |
1492 * font size depends in the return value of <i>getLegendFontSize()</i>. | |
1493 * | |
1494 * @return a new Font instance with <i>DEFAULT_FONT_NAME</i>. | |
1495 */ | |
1496 protected Font createLegendLabelFont() { | |
1497 return new Font( | |
1498 DEFAULT_FONT_NAME, | |
1499 Font.PLAIN, | |
1500 getLegendFontSize() | |
1501 ); | |
1502 } | |
1503 | |
1504 | |
1505 /** | |
1506 * Create new legend entries, dependent on settings. | |
1507 * @param plot The plot for which to modify the legend. | |
1508 */ | |
1509 public void aggregateLegendEntries(XYPlot plot) { | |
1510 int AGGR_THRESHOLD = 0; | |
1511 | |
1512 if (getChartSettings() == null) { | |
1513 return; | |
1514 } | |
1515 Integer threshold = getChartSettings().getLegendSection() | |
1516 .getAggregationThreshold(); | |
1517 | |
1518 AGGR_THRESHOLD = (threshold != null) ? threshold.intValue() : 0; | |
1519 | |
1520 LegendProcessor.aggregateLegendEntries(plot, AGGR_THRESHOLD); | |
1521 } | |
1522 | |
1523 | |
1524 /** | |
1525 * Returns a transparently textured paint. | |
1526 * | |
1527 * @return a transparently textured paint. | |
1528 */ | |
1529 protected static Paint createTransparentPaint() { | |
1530 // TODO why not use a transparent color? | |
1531 BufferedImage texture = new BufferedImage( | |
1532 1, 1, BufferedImage.TYPE_4BYTE_ABGR); | |
1533 | |
1534 return new TexturePaint( | |
1535 texture, new Rectangle2D.Double(0d, 0d, 0d, 0d)); | |
1536 } | |
1537 | |
1538 | |
1539 protected void preparePDFContext(CallContext context) { | |
1540 int[] dimension = getExportDimension(); | |
1541 | |
1542 context.putContextValue("chart.width", dimension[0]); | |
1543 context.putContextValue("chart.height", dimension[1]); | |
1544 context.putContextValue("chart.marginLeft", 5f); | |
1545 context.putContextValue("chart.marginRight", 5f); | |
1546 context.putContextValue("chart.marginTop", 5f); | |
1547 context.putContextValue("chart.marginBottom", 5f); | |
1548 context.putContextValue( | |
1549 "chart.page.format", | |
1550 ChartExportHelper.DEFAULT_PAGE_SIZE); | |
1551 } | |
1552 | |
1553 | |
1554 protected void prepareSVGContext(CallContext context) { | |
1555 int[] dimension = getExportDimension(); | |
1556 | |
1557 context.putContextValue("chart.width", dimension[0]); | |
1558 context.putContextValue("chart.height", dimension[1]); | |
1559 context.putContextValue( | |
1560 "chart.encoding", | |
1561 ChartExportHelper.DEFAULT_ENCODING); | |
1562 } | |
1563 | |
1564 /** | |
1565 * Retuns the call context. May be null if init hasn't been called yet. | |
1566 * | |
1567 * @return the CallContext instance | |
1568 */ | |
1569 public CallContext getCallContext() { | |
1570 return context; | |
1571 } | |
1572 } |