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 }

http://dive4elements.wald.intevation.org