comparison artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java @ 9123:1cc7653ca84f

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

http://dive4elements.wald.intevation.org