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

http://dive4elements.wald.intevation.org