Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/exports/AbstractChartGenerator.java @ 9555:ef5754ba5573
Implemented legend aggregation based on type of themes.
Added theme-editor style configuration for aggregated legend entries.
Only configured themes get aggregated.
author | gernotbelger |
---|---|
date | Tue, 23 Oct 2018 16:26:48 +0200 |
parents | 9dc6427059b2 |
children |
comparison
equal
deleted
inserted
replaced
9554:33ce8eba9806 | 9555:ef5754ba5573 |
---|---|
56 import org.dive4elements.river.jfree.StyledSeries; | 56 import org.dive4elements.river.jfree.StyledSeries; |
57 import org.dive4elements.river.jfree.StyledXYDataset; | 57 import org.dive4elements.river.jfree.StyledXYDataset; |
58 import org.dive4elements.river.themes.ThemeDocument; | 58 import org.dive4elements.river.themes.ThemeDocument; |
59 import org.jfree.chart.JFreeChart; | 59 import org.jfree.chart.JFreeChart; |
60 import org.jfree.chart.LegendItem; | 60 import org.jfree.chart.LegendItem; |
61 import org.jfree.chart.LegendItemCollection; | |
62 import org.jfree.chart.axis.NumberAxis; | 61 import org.jfree.chart.axis.NumberAxis; |
63 import org.jfree.chart.plot.XYPlot; | 62 import org.jfree.chart.plot.XYPlot; |
64 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; | 63 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; |
65 import org.jfree.chart.title.TextTitle; | 64 import org.jfree.chart.title.TextTitle; |
66 import org.jfree.data.Range; | 65 import org.jfree.data.Range; |
94 | 93 |
95 protected static final int DEFAULT_FONT_SIZE = 12; | 94 protected static final int DEFAULT_FONT_SIZE = 12; |
96 | 95 |
97 private static final String DEFAULT_CHART_FORMAT = "png"; | 96 private static final String DEFAULT_CHART_FORMAT = "png"; |
98 | 97 |
98 private static final int DEFAULT_LEGEND_AGGREGATION_THRESHOLD = 10; | |
99 | |
99 private static final String XPATH_CHART_EXPORT = "/art:action/art:attributes/art:export/@art:value"; | 100 private static final String XPATH_CHART_EXPORT = "/art:action/art:attributes/art:export/@art:value"; |
100 | 101 |
101 private static final String XPATH_CHART_SIZE = "/art:action/art:attributes/art:size"; | 102 private static final String XPATH_CHART_SIZE = "/art:action/art:attributes/art:size"; |
102 | 103 |
103 private static final String XPATH_CHART_FORMAT = "/art:action/art:attributes/art:format/@art:value"; | 104 private static final String XPATH_CHART_FORMAT = "/art:action/art:attributes/art:format/@art:value"; |
220 * @param context2 | 221 * @param context2 |
221 * | 222 * |
222 * @return the default title of a chart. | 223 * @return the default title of a chart. |
223 */ | 224 */ |
224 protected abstract String getDefaultChartTitle(CallContext context); | 225 protected abstract String getDefaultChartTitle(CallContext context); |
225 | |
226 /** | |
227 * This method is used to create new AxisDataset instances which may differ | |
228 * in concrete subclasses. | |
229 * | |
230 * @param idx | |
231 * The index of an axis. | |
232 */ | |
233 protected abstract AxisDataset createAxisDataset(int idx); | |
234 | 226 |
235 /** | 227 /** |
236 * Combines the ranges of the X axis at index <i>idx</i>. | 228 * Combines the ranges of the X axis at index <i>idx</i>. |
237 * | 229 * |
238 * @param bounds | 230 * @param bounds |
338 */ | 330 */ |
339 private LegendSection buildLegendSection() { | 331 private LegendSection buildLegendSection() { |
340 final LegendSection legendSection = new LegendSection(); | 332 final LegendSection legendSection = new LegendSection(); |
341 legendSection.setVisibility(isLegendVisible()); | 333 legendSection.setVisibility(isLegendVisible()); |
342 legendSection.setFontSize(getLegendFontSize()); | 334 legendSection.setFontSize(getLegendFontSize()); |
343 legendSection.setAggregationThreshold(10); | 335 legendSection.setAggregationThreshold(DEFAULT_LEGEND_AGGREGATION_THRESHOLD); |
344 return legendSection; | 336 return legendSection; |
345 } | 337 } |
346 | 338 |
347 /** | 339 /** |
348 * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b> | 340 * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b> |
684 return DEFAULT_FONT_SIZE; | 676 return DEFAULT_FONT_SIZE; |
685 | 677 |
686 return fontSize; | 678 return fontSize; |
687 } | 679 } |
688 | 680 |
689 /** | 681 protected final LegendAggregator createLegendBuilder() { |
690 * Creates a new LegendItem with <i>name</i> and font provided by | 682 return new LegendAggregator(getLegendAggregationThreshold(), createLegendLabelFont()); |
691 * <i>createLegendLabelFont()</i>. | 683 } |
692 * | 684 |
693 * @param theme | 685 private int getLegendAggregationThreshold() { |
694 * The theme of the chart line. | |
695 * @param name | |
696 * The displayed name of the item. | |
697 * | |
698 * @return a new LegendItem instance. | |
699 */ | |
700 protected final LegendItem createLegendItem(final ThemeDocument theme, final String name) { | |
701 // OPTIMIZE Pass font, parsed Theme items. | |
702 | |
703 Color color = theme.parseLineColorField(); | |
704 if (color == null) | |
705 color = Color.BLACK; | |
706 | |
707 final LegendItem legendItem = new LegendItem(name, color); | |
708 legendItem.setLabelFont(createLegendLabelFont()); | |
709 return legendItem; | |
710 } | |
711 | |
712 /** | |
713 * Create new legend entries, dependent on settings. | |
714 * | |
715 * @param plot | |
716 * The plot for which to modify the legend. | |
717 */ | |
718 protected final void aggregateLegendEntries(final XYPlot plot) { | |
719 | 686 |
720 final ChartSettings chartSettings = getChartSettings(); | 687 final ChartSettings chartSettings = getChartSettings(); |
721 if (chartSettings == null) | 688 if (chartSettings == null) |
722 return; | 689 return DEFAULT_LEGEND_AGGREGATION_THRESHOLD; |
723 | 690 |
724 final Integer threshold = chartSettings.getLegendSection().getAggregationThreshold(); | 691 final LegendSection legendSection = chartSettings.getLegendSection(); |
725 | 692 if (legendSection == null) |
726 final int aggrThreshold = threshold != null ? threshold.intValue() : 0; | 693 return DEFAULT_LEGEND_AGGREGATION_THRESHOLD; |
727 | 694 |
728 LegendProcessor.aggregateLegendEntries(plot, aggrThreshold); | 695 final Integer threshold = legendSection.getAggregationThreshold(); |
696 if (threshold == null) | |
697 return DEFAULT_LEGEND_AGGREGATION_THRESHOLD; | |
698 | |
699 return threshold; | |
729 } | 700 } |
730 | 701 |
731 /** | 702 /** |
732 * Creates Font (Family and size) to use when creating Legend Items. The | 703 * Creates Font (Family and size) to use when creating Legend Items. The |
733 * font size depends in the return value of <i>getLegendFontSize()</i>. | 704 * font size depends in the return value of <i>getLegendFontSize()</i>. |
816 * datasets is mapped to a specific axis as well. | 787 * datasets is mapped to a specific axis as well. |
817 * | 788 * |
818 * @param plot | 789 * @param plot |
819 * plot to add datasets to. | 790 * plot to add datasets to. |
820 */ | 791 */ |
821 protected void addDatasets(final XYPlot plot) { | 792 protected void addDatasets(final XYPlot plot, final LegendAggregator legendBuilder) { |
822 log.debug("addDatasets()"); | 793 log.debug("addDatasets()"); |
823 | 794 |
824 // AxisDatasets are sorted, but some might be empty. | 795 // AxisDatasets are sorted, but some might be empty. |
825 // Thus, generate numbering on the fly. | 796 // Thus, generate numbering on the fly. |
826 int axisIndex = 0; | 797 int axisIndex = 0; |
844 for (final XYDataset dataset : axisDataset.getDatasets()) { | 815 for (final XYDataset dataset : axisDataset.getDatasets()) { |
845 try { | 816 try { |
846 plot.setDataset(datasetIndex, dataset); | 817 plot.setDataset(datasetIndex, dataset); |
847 plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); | 818 plot.mapDatasetToRangeAxis(datasetIndex, axisIndex); |
848 | 819 |
849 applyThemes(plot, dataset, datasetIndex); | 820 applyThemes(plot, legendBuilder, dataset, datasetIndex); |
850 | 821 |
851 datasetIndex++; | 822 datasetIndex++; |
852 } | 823 } |
853 catch (final RuntimeException re) { | 824 catch (final RuntimeException re) { |
854 log.error(re); | 825 log.error(re); |
855 } | 826 } |
856 } | 827 } |
857 | 828 |
858 axisDataset.setPlotAxisIndex(axisIndex); | |
859 axisIndex++; | 829 axisIndex++; |
860 } | 830 } |
861 } | 831 } |
862 } | 832 } |
863 | 833 |
873 * index 0), correlates with renderer index. | 843 * index 0), correlates with renderer index. |
874 * @param isArea | 844 * @param isArea |
875 * true if the series describes an area and shall be rendered | 845 * true if the series describes an area and shall be rendered |
876 * as such. | 846 * as such. |
877 */ | 847 */ |
878 private void applyThemes(final XYPlot plot, final XYDataset dataset, final int datasetIndex) { | 848 private void applyThemes(final XYPlot plot, final LegendAggregator legendBuilder, final XYDataset dataset, final int datasetIndex) { |
879 | |
880 final Font legendFont = createLegendLabelFont(); | |
881 | 849 |
882 if (dataset instanceof StyledXYDataset) | 850 if (dataset instanceof StyledXYDataset) |
883 ((StyledXYDataset) dataset).applyTheme(this.context.getMeta(), plot, datasetIndex, legendFont); | 851 ((StyledXYDataset) dataset).applyTheme(this.context.getMeta(), plot, legendBuilder, datasetIndex); |
884 else | 852 else |
885 applyLineTheme(plot, dataset, datasetIndex, legendFont); | 853 applyLineTheme(plot, legendBuilder, dataset, datasetIndex); |
886 } | 854 } |
887 | 855 |
888 /** | 856 /** |
889 * Expands a given range if it collapses into one point. | 857 * Expands a given range if it collapses into one point. |
890 * | 858 * |
931 * The XYDataset which needs to support Series objects. | 899 * The XYDataset which needs to support Series objects. |
932 * @param idx | 900 * @param idx |
933 * The index of the renderer / dataset. | 901 * The index of the renderer / dataset. |
934 * @param legendFont2 | 902 * @param legendFont2 |
935 */ | 903 */ |
936 private void applyLineTheme(final XYPlot plot, final XYDataset dataset, final int idx, final Font legendFont) { | 904 private void applyLineTheme(final XYPlot plot, final LegendAggregator legendBuilder, final XYDataset dataset, final int idx) { |
937 log.debug("Apply LineTheme for dataset at index: " + idx); | 905 log.debug("Apply LineTheme for dataset at index: " + idx); |
938 | |
939 final LegendItemCollection lic = new LegendItemCollection(); | |
940 final LegendItemCollection anno = plot.getFixedLegendItems(); | |
941 | 906 |
942 final XYLineAndShapeRenderer renderer = createRenderer(plot, idx); | 907 final XYLineAndShapeRenderer renderer = createRenderer(plot, idx); |
943 | 908 |
944 for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { | 909 for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) { |
945 final Series series = getSeriesOf(dataset, s); | 910 final Series series = getSeriesOf(dataset, s); |
950 } | 915 } |
951 | 916 |
952 // special case: if there is just one single item, we need to enable | 917 // special case: if there is just one single item, we need to enable |
953 // points for this series, otherwise we would not see anything in | 918 // points for this series, otherwise we would not see anything in |
954 // the chart area. | 919 // the chart area. |
955 if (series.getItemCount() == 1) { | 920 // if (series.getItemCount() == 1) { |
956 renderer.setSeriesShapesVisible(s, true); | 921 // renderer.setSeriesShapesVisible(s, true); |
957 } | 922 // } |
958 | 923 |
959 LegendItem legendItem = renderer.getLegendItem(idx, s); | 924 LegendItem legendItem = renderer.getLegendItem(idx, s); |
960 if (legendItem.getLabel().endsWith(" ")) { | 925 if (legendItem.getLabel().endsWith(" ")) { |
961 legendItem = null; | 926 legendItem = null; |
962 } | 927 } |
963 | 928 |
929 final String themeType = series instanceof StyledSeries ? ((StyledSeries) series).getThemeType() : null; | |
930 | |
964 if (legendItem != null) { | 931 if (legendItem != null) { |
965 legendItem.setLabelFont(legendFont); | 932 legendBuilder.addLegendItem(themeType, legendItem); |
966 lic.add(legendItem); | |
967 } else { | 933 } else { |
968 log.warn("Could not get LegentItem for renderer: " + idx + ", series-idx " + s); | 934 log.warn("Could not get LegentItem for renderer: " + idx + ", series-idx " + s); |
969 } | 935 } |
970 } | 936 } |
971 | |
972 if (anno != null) { | |
973 lic.addAll(anno); | |
974 } | |
975 | |
976 plot.setFixedLegendItems(lic); | |
977 | 937 |
978 plot.setRenderer(idx, renderer); | 938 plot.setRenderer(idx, renderer); |
979 } | 939 } |
980 | 940 |
981 private void preparePDFContext(final CallContext context) { | 941 private void preparePDFContext(final CallContext context) { |
1075 */ | 1035 */ |
1076 protected final AxisDataset getAxisDataset(final int idx) { | 1036 protected final AxisDataset getAxisDataset(final int idx) { |
1077 AxisDataset axisDataset = this.datasets.get(idx); | 1037 AxisDataset axisDataset = this.datasets.get(idx); |
1078 | 1038 |
1079 if (axisDataset == null) { | 1039 if (axisDataset == null) { |
1080 axisDataset = createAxisDataset(idx); | 1040 axisDataset = new AxisDataset(); |
1081 this.datasets.put(idx, axisDataset); | 1041 this.datasets.put(idx, axisDataset); |
1082 } | 1042 } |
1083 | 1043 |
1084 return axisDataset; | 1044 return axisDataset; |
1085 } | 1045 } |
1319 */ | 1279 */ |
1320 protected final boolean isGridVisible() { | 1280 protected final boolean isGridVisible() { |
1321 return true; | 1281 return true; |
1322 } | 1282 } |
1323 | 1283 |
1324 protected final void addAnnotationsToRenderer(final XYPlot plot) { | 1284 protected final void addAnnotationsToRenderer(final XYPlot plot, final LegendAggregator legendBuilder) { |
1325 | 1285 |
1326 final AnnotationRenderer annotationRenderer = new AnnotationRenderer(getChartSettings(), this.datasets, DEFAULT_FONT_NAME); | 1286 final AnnotationRenderer annotationRenderer = new AnnotationRenderer(this.datasets); |
1327 annotationRenderer.addAnnotationsToRenderer(plot, this.annotations); | 1287 annotationRenderer.addAnnotationsToRenderer(plot, legendBuilder, this.annotations); |
1328 | 1288 |
1329 doAddFurtherAnnotations(plot, this.annotations); | 1289 doAddFurtherAnnotations(plot, legendBuilder, this.annotations); |
1330 } | 1290 } |
1331 | 1291 |
1332 /** | 1292 /** |
1333 * Allow further annotation processing, override to implement. | 1293 * Allow further annotation processing, override to implement. |
1334 * | 1294 * |
1335 * Does nothing by default. | 1295 * Does nothing by default. |
1336 */ | 1296 */ |
1337 protected void doAddFurtherAnnotations(final XYPlot plot, final List<RiverAnnotation> annotations) { | 1297 protected void doAddFurtherAnnotations(final XYPlot plot, final LegendAggregator legendBuilder, final List<RiverAnnotation> annotations) { |
1338 | 1298 |
1339 } | 1299 } |
1340 } | 1300 } |