teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.exports; ingo@348: gernotbelger@9123: import static org.dive4elements.river.exports.injector.InjectorConstants.CURRENT_KM; gernotbelger@9123: sascha@3257: import java.awt.Font; teichmann@7079: import java.awt.Graphics2D; teichmann@7079: import java.awt.Transparency; sascha@3257: import java.awt.geom.Rectangle2D; sascha@3257: import java.awt.image.BufferedImage; sascha@3257: import java.io.IOException; sascha@3257: import java.io.OutputStream; teichmann@8076: import java.text.NumberFormat; gernotbelger@8892: import java.util.HashMap; sascha@3257: import java.util.Map; sascha@3257: import java.util.SortedMap; sascha@3257: import java.util.TreeMap; sascha@3257: teichmann@7079: import org.dive4elements.artifacts.CallContext; teichmann@7079: import org.dive4elements.artifacts.common.utils.XMLUtils; teichmann@7079: import org.dive4elements.river.java2d.NOPGraphics2D; teichmann@7079: import org.dive4elements.river.jfree.RiverAnnotation; teichmann@7079: import org.dive4elements.river.utils.Formatter; teichmann@7079: import org.jfree.chart.ChartRenderingInfo; sascha@3257: import org.jfree.chart.JFreeChart; sascha@3257: import org.jfree.chart.axis.NumberAxis; sascha@3257: import org.jfree.chart.plot.XYPlot; sascha@3257: import org.jfree.data.Range; sascha@3257: import org.w3c.dom.Document; rrenkert@8370: ingo@348: /** aheinecke@7043: * Implementation of the OutGenerator interface for charts. aheinecke@7043: * It should provide some basic things that equal in all chart types. ingo@348: * ingo@348: */ gernotbelger@9104: public abstract class ChartGenerator2 extends AbstractChartGenerator { ingo@348: gernotbelger@9123: private static final boolean USE_NOP_GRAPHICS = Boolean.getBoolean("info.rendering.nop.graphics"); ingo@2238: tom@8341: /** Map of annotations to add at specific Y-axis. */ gernotbelger@9123: protected SortedMap yAnnotations = new TreeMap<>(); teichmann@7077: gernotbelger@8892: private Map axisNameToAxis = new HashMap<>(); gernotbelger@8892: tom@8341: public void addYAnnotation(RiverAnnotation annotation, int axisIndex) { tom@8341: yAnnotations.put(axisIndex, annotation); tom@8341: } tom@8341: christian@3278: @Override gernotbelger@9123: protected void doGenerate(final CallContext context, final OutputStream out, final String outName) throws IOException { teichmann@8202: log.debug("ChartGenerator2.generate"); ingo@2234: gernotbelger@9123: if (outName.indexOf("chartinfo") > 0) gernotbelger@9123: generateInfo(context, out); gernotbelger@9123: else gernotbelger@9123: generateImage(context); teichmann@7079: } teichmann@7079: gernotbelger@9123: /** gernotbelger@9123: * Generate only meta infos gernotbelger@9123: */ gernotbelger@9123: private void generateInfo(final CallContext context, final OutputStream out) { teichmann@7079: teichmann@8202: log.debug("ChartInfoGenerator2.generateInfo"); teichmann@7079: gernotbelger@9123: JFreeChart chart = generateChart(context); teichmann@7079: teichmann@7079: int[] size = getSize(); teichmann@7079: if (size == null) { teichmann@7079: size = getDefaultSize(); teichmann@7079: } teichmann@7079: teichmann@7079: ChartRenderingInfo info = new ChartRenderingInfo(); teichmann@7079: teichmann@7079: long startTime = System.currentTimeMillis(); teichmann@7079: teichmann@7079: if (USE_NOP_GRAPHICS) { teichmann@7079: BufferedImage image = teichmann@7079: new BufferedImage(size[0], size[1], Transparency.BITMASK); teichmann@7079: teichmann@7079: Graphics2D g2d = image.createGraphics(); teichmann@7079: Graphics2D nop = new NOPGraphics2D(g2d); teichmann@7079: teichmann@7079: chart.draw( teichmann@7079: nop, teichmann@7079: new Rectangle2D.Double(0, 0, size[0], size[1]), teichmann@7079: null, teichmann@7079: info); teichmann@7079: teichmann@7079: nop.dispose(); teichmann@7079: } teichmann@7079: else { teichmann@7079: chart.createBufferedImage( teichmann@7079: size[0], size[1], Transparency.BITMASK, info); teichmann@7079: } teichmann@7079: teichmann@7079: long stopTime = System.currentTimeMillis(); teichmann@7079: teichmann@8202: if (log.isDebugEnabled()) { teichmann@8202: log.debug("Rendering info took: " + teichmann@7079: (stopTime-startTime) + "ms"); teichmann@7079: } teichmann@7079: teichmann@7079: aheinecke@7084: InfoGeneratorHelper2 helper = new InfoGeneratorHelper2(this); teichmann@7079: Document doc = helper.createInfoDocument(chart, info); teichmann@7079: teichmann@7079: XMLUtils.toStream(doc, out); teichmann@7079: } teichmann@7079: ingo@2236: /** ingo@2236: * Creates a new ChartSection. ingo@2236: * ingo@2236: * @return a new ChartSection. ingo@2236: */ gernotbelger@9123: @Override gernotbelger@9123: protected ChartSection buildChartSection(final CallContext context) { ingo@2236: ChartSection chartSection = new ChartSection(); gernotbelger@9123: chartSection.setTitle(getChartTitle(context)); gernotbelger@9123: chartSection.setSubtitle(getChartSubtitlePure(context)); felix@3613: chartSection.setDisplayGrid(isGridVisible()); felix@3615: chartSection.setDisplayLogo(showLogo()); felix@3618: chartSection.setLogoVPlacement(logoVPlace()); felix@3618: chartSection.setLogoHPlacement(logoHPlace()); ingo@2236: return chartSection; ingo@2236: } ingo@2236: gernotbelger@9123: protected String interpolateVariables(final CallContext context, String s) { tom@8732: log.debug("Interpolate variables in string '" + s + "'"); teichmann@8074: Object radius = context.getContextValue("radius"); teichmann@8074: if (radius instanceof Double) { teichmann@8076: NumberFormat f = Formatter.getCSVFormatter(context); tom@8732: s = s.replace("$RADIUS", f.format(radius) + " km"); teichmann@8074: } tom@8732: else { tom@8732: s = s.replace("$RADIUS", "-"); tom@8732: } tom@8732: rrenkert@8370: Object km = context.getContextValue(CURRENT_KM); rrenkert@8370: if (km instanceof Number && s.contains("$KM")) { tom@8387: NumberFormat f = Formatter.getCalculationKm(context.getMeta()); tom@8732: s = s.replace("$KM", f.format(km)); rrenkert@8370: } tom@8732: teichmann@8074: return s; teichmann@8074: } ingo@2234: ingo@2234: /** ingo@2234: * Returns the subtitle of a chart. The return value depends on the ingo@2234: * existence of ChartSettings: if there are ChartSettings set, this method ingo@2234: * returns the chart title provided by those settings. Otherwise, this ingo@2234: * method returns getDefaultChartSubtitle(). gernotbelger@9123: * @param context ingo@2234: * ingo@2234: * @return the subtitle of a chart. ingo@2234: */ gernotbelger@9123: protected String getChartSubtitlePure(CallContext context) { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: teichmann@8076: String subTitle = chartSettings != null teichmann@8076: ? getChartSubtitle(chartSettings) gernotbelger@9123: : getDefaultChartSubtitle(context); teichmann@8076: gernotbelger@9123: String defSubTitle = getDefaultChartSubtitle(context); teichmann@8076: teichmann@8076: if (subTitle == null) { teichmann@8076: subTitle = defSubTitle != null ? defSubTitle : ""; ingo@2234: } ingo@2234: teichmann@8076: return subTitle; teichmann@8076: } teichmann@8076: gernotbelger@9123: @Override gernotbelger@9123: protected String getChartSubtitle(CallContext context) { gernotbelger@9123: return interpolateVariables(context, getChartSubtitlePure(context)); ingo@2234: } ingo@2234: aheinecke@7068: /** aheinecke@7068: * Glue between axis names and index. aheinecke@7068: */ aheinecke@7068: protected abstract String axisIndexToName(int index); ingo@2234: ingo@2234: /** ingo@2234: * This method returns the font size for an Y axis. If the font size is ingo@2234: * specified in ChartSettings (if chartSettings is set), this size is ingo@2234: * returned. Otherwise the default font size 12 is returned. ingo@2234: * ingo@2234: * @return the font size for the x axis. ingo@2234: */ aheinecke@7068: protected int getYAxisFontSize(int index) { ingo@2234: ChartSettings chartSettings = getChartSettings(); ingo@2234: if (chartSettings == null) { ingo@2234: return DEFAULT_FONT_SIZE; ingo@2234: } ingo@2234: aheinecke@7068: AxisSection as = chartSettings.getAxisSection(axisIndexToName(index)); bjoern@4376: if (as == null) { bjoern@4376: return DEFAULT_FONT_SIZE; bjoern@4376: } ingo@2234: Integer fontSize = as.getFontSize(); ingo@2234: ingo@2234: return fontSize != null ? fontSize : DEFAULT_FONT_SIZE; ingo@2234: } ingo@2234: aheinecke@7084: protected abstract String getYAxisLabel(String axisName); ingo@2233: ingo@2234: /** ingo@2236: * This method searches for a specific axis in the settings if ingo@2236: * settings is set. If the axis was found, this method returns the ingo@2236: * specified axis range if the axis range is fixed. Otherwise, this method ingo@2236: * returns null. ingo@2236: * ingo@2236: * @param axisId The identifier of an axis. ingo@2236: * ingo@2236: * @return the specified axis range from settings if the axis is ingo@2236: * fixed, otherwise null. ingo@2236: */ ingo@2236: public Range getRangeForAxisFromSettings(String axisId) { ingo@2236: ChartSettings chartSettings = getChartSettings(); ingo@2236: if (chartSettings == null) { ingo@2236: return null; ingo@2236: } ingo@2236: ingo@2236: AxisSection as = chartSettings.getAxisSection(axisId); bjoern@4376: bjoern@4376: if (as == null) { bjoern@4376: return null; bjoern@4376: } bjoern@4376: gernotbelger@9123: if (as.isFixed()) { ingo@2236: Double upper = as.getUpperRange(); ingo@2236: Double lower = as.getLowerRange(); ingo@2236: ingo@2236: if (upper != null && lower != null) { ingo@2236: return lower < upper ingo@2236: ? new Range(lower, upper) ingo@2236: : new Range(upper, lower); ingo@2236: } ingo@2236: } ingo@2236: ingo@2236: return null; ingo@2236: } ingo@2236: ingo@2242: /** ingo@2233: * Creates a new instance of IdentifiableNumberAxis. ingo@2233: * ingo@2233: * @param idx The index of the new axis. ingo@2233: * @param label The label of the new axis. ingo@2233: * ingo@2233: * @return an instance of IdentifiableNumberAxis. ingo@2233: */ gernotbelger@9123: protected final NumberAxis createNumberAxis(int idx, String label) { aheinecke@7068: return new IdentifiableNumberAxis(axisIndexToName(idx), label); ingo@2233: } ingo@2234: ingo@2236: /** ingo@2238: * Create Y (range) axis for given index. ingo@2238: * Shall be overriden by subclasses. ingo@2238: */ gernotbelger@9123: @Override ingo@2238: protected NumberAxis createYAxis(int index) { ingo@2238: ingo@2238: Font labelFont = new Font( ingo@2238: DEFAULT_FONT_NAME, ingo@2238: Font.BOLD, ingo@2238: getYAxisFontSize(index)); ingo@2238: aheinecke@7084: String axisName = axisIndexToName(index); aheinecke@7084: gernotbelger@8892: IdentifiableNumberAxis axis = new IdentifiableNumberAxis(axisName, getYAxisLabel(axisName)); ingo@2238: ingo@2238: axis.setAutoRangeIncludesZero(false); ingo@2238: axis.setLabelFont(labelFont); ingo@2590: axis.setTickLabelFont(labelFont); gernotbelger@8885: // REMARK: we overwrite the default values to 0.0, because in earlier version margins were never applied. gernotbelger@8885: axis.setLowerMargin(0); gernotbelger@8885: axis.setUpperMargin(0); ingo@2238: gernotbelger@8892: /* remember axis for lookup */ gernotbelger@8892: axisNameToAxis.put( axisName, axis ); gernotbelger@8892: ingo@2238: return axis; ingo@2238: } ingo@2238: gernotbelger@8892: public final IdentifiableNumberAxis getAxis(final String axisName) { gernotbelger@8892: return axisNameToAxis.get(axisName); gernotbelger@8892: } gernotbelger@8935: gernotbelger@8935: /** Returns the number of registered y-axes */ gernotbelger@8935: public final int getNumYAxes() { gernotbelger@8935: return axisNameToAxis.size(); gernotbelger@8935: } gernotbelger@9123: gernotbelger@9123: protected final void addYAnnotationsToRenderer(final XYPlot plot) { gernotbelger@9123: final AnnotationRenderer annotationRenderer = new AnnotationRenderer(getChartSettings(), getDatasets(), DEFAULT_FONT_NAME); gernotbelger@9123: annotationRenderer.addYAnnotationsToRenderer(plot, this.yAnnotations); gernotbelger@9123: } gernotbelger@8892: }