ec.util.chart.swing.JTimeSeriesRendererSupport.java Source code

Java tutorial

Introduction

Here is the source code for ec.util.chart.swing.JTimeSeriesRendererSupport.java

Source

/*
 * Copyright 2013 National Bank of Belgium
 * 
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved 
 * by the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * 
 * http://ec.europa.eu/idabc/eupl
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and 
 * limitations under the Licence.
 */
package ec.util.chart.swing;

import ec.util.chart.TimeSeriesChart.RendererType;
import static ec.util.chart.TimeSeriesChart.RendererType.AREA;
import static ec.util.chart.TimeSeriesChart.RendererType.COLUMN;
import static ec.util.chart.TimeSeriesChart.RendererType.LINE;
import static ec.util.chart.TimeSeriesChart.RendererType.MARKER;
import static ec.util.chart.TimeSeriesChart.RendererType.SPLINE;
import static ec.util.chart.TimeSeriesChart.RendererType.STACKED_AREA;
import static ec.util.chart.TimeSeriesChart.RendererType.STACKED_COLUMN;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.EnumSet;
import javax.annotation.Nonnull;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.block.LabelBlock;
import org.jfree.chart.labels.XYItemLabelGenerator;
import org.jfree.chart.labels.XYSeriesLabelGenerator;
import org.jfree.chart.plot.CrosshairState;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.AbstractRenderer;
import org.jfree.chart.renderer.xy.StackedXYAreaRenderer2;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYAreaRenderer2;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRendererState;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.renderer.xy.XYSplineRenderer;
import org.jfree.data.Range;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.Size2D;

/**
 *
 * @author Philippe Charles
 */
abstract class JTimeSeriesRendererSupport implements XYItemLabelGenerator, XYSeriesLabelGenerator {

    abstract public Color getPlotColor();

    abstract public Color getSeriesColor(int series);

    abstract public Stroke getSeriesStroke(int series);

    abstract public Color getSeriesLabelColor(int series);

    abstract public String getSeriesLabel(int series);

    abstract public Font getSeriesLabelFont(int series);

    abstract public boolean isSeriesLabelVisible(int series);

    abstract public Color getObsColor(int series, int item);

    abstract public Stroke getObsStroke(int series, int item);

    abstract public boolean isObsHighlighted(int series, int item);

    abstract public String getObsLabel(int series, int item);

    abstract public Font getObsLabelFont(int series, int item);

    abstract public boolean isObsLabelVisible(int series, int item);

    @Override
    final public String generateLabel(XYDataset dataset, int series, int item) {
        return getObsLabel(series, item);
    }

    @Override
    final public String generateLabel(XYDataset dataset, int series) {
        return getSeriesLabel(series);
    }

    final public void drawItemLabel(Graphics2D g2, XYDataset dataset, int series, int item, double x, double y) {
        String label = generateLabel(dataset, series, item);
        Font font = getObsLabelFont(series, item);
        Color fillColor = getObsColor(series, item);
        Paint paint = getForegroundColor(SwingColorSchemeSupport.getLuminance(fillColor));
        Paint outlinePaint = getPlotColor();
        Stroke outlineStroke = AbstractRenderer.DEFAULT_STROKE;
        drawToolTip(g2, x, y, 3d, label, font, paint, fillColor, outlinePaint, outlineStroke);
    }

    @Nonnull
    public EnumSet<RendererType> getSupportedRendererTypes() {
        return EnumSet.of(LINE, COLUMN, SPLINE, STACKED_COLUMN, MARKER, AREA, STACKED_AREA);
    }

    @Nonnull
    public XYItemRenderer createRenderer(@Nonnull RendererType type) {
        switch (type) {
        case LINE:
            return new LineRenderer(this);
        case SPLINE:
            return new SplineRenderer(this);
        case COLUMN:
            return new BarRenderer(this);
        case MARKER:
            return new MarkerRenderer(this);
        case STACKED_COLUMN:
            return new StackedBarRenderer(this);
        case AREA:
            return new AreaRenderer(this);
        case STACKED_AREA:
            return new StackedAreaRenderer(this);
        }
        throw new RuntimeException("Not implemented");
    }

    //<editor-fold defaultstate="collapsed" desc="Renderers implementation">
    private static final Shape ITEM_SHAPE = new Ellipse2D.Double(-3, -3, 6, 6);

    private static class LineRenderer extends XYLineAndShapeRenderer {

        protected final JTimeSeriesRendererSupport support;

        public LineRenderer(JTimeSeriesRendererSupport support) {
            this.support = support;
            setBaseItemLabelsVisible(true);
            setAutoPopulateSeriesShape(false);
            setAutoPopulateSeriesFillPaint(false);
            setAutoPopulateSeriesOutlineStroke(false);
            setLegendLine(ITEM_SHAPE);
            setBaseShape(ITEM_SHAPE);
            setUseFillPaint(true);
            setLegendItemLabelGenerator(support);
            setBaseItemLabelGenerator(support);
        }

        @Override
        public boolean isItemLabelVisible(int series, int item) {
            return support.isObsLabelVisible(series, item);
        }

        @Override
        public boolean getItemShapeVisible(int series, int item) {
            return support.isObsHighlighted(series, item);
        }

        @Override
        public boolean isSeriesVisibleInLegend(int series) {
            return support.isSeriesLabelVisible(series);
        }

        @Override
        public Paint getSeriesPaint(int series) {
            return support.getSeriesColor(series);
        }

        @Override
        public Paint getItemPaint(int series, int item) {
            return support.getObsColor(series, item);
        }

        @Override
        public Paint getItemFillPaint(int series, int item) {
            return support.getPlotColor();
        }

        @Override
        public Paint getLegendTextPaint(int series) {
            return support.getSeriesLabelColor(series);
        }

        @Override
        public Stroke getSeriesStroke(int series) {
            return support.getSeriesStroke(series);
        }

        @Override
        public Stroke getItemStroke(int series, int item) {
            return support.getObsStroke(series, item);
        }

        @Override
        public Stroke getItemOutlineStroke(int series, int item) {
            return support.getSeriesStroke(series);
        }

        @Override
        public Font getLegendTextFont(int series) {
            return support.getSeriesLabelFont(series);
        }

        @Override
        protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, XYDataset dataset, int series,
                int item, double x, double y, boolean negative) {
            // drawn in third pass
        }

        @Override
        protected void drawFirstPassShape(Graphics2D g2, int pass, int series, int item, Shape shape) {
            g2.setStroke(getItemStroke(series, item));
            g2.setPaint(getSeriesPaint(series));
            g2.draw(shape);
        }

        @Override
        public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info,
                XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item,
                CrosshairState crosshairState, int pass) {
            super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item,
                    crosshairState, pass);
            if (pass == 2 && getItemVisible(series, item) && isItemLabelVisible(series, item)) {
                double x1 = dataset.getXValue(series, item);
                double y1 = dataset.getYValue(series, item);
                double transX1 = domainAxis.valueToJava2D(x1, dataArea, plot.getDomainAxisEdge());
                double transY1 = rangeAxis.valueToJava2D(y1, dataArea, plot.getRangeAxisEdge());
                support.drawItemLabel(g2, dataset, series, item, transX1, transY1);
            }
        }

        @Override
        public int getPassCount() {
            return 3;
        }
    };

    private static class SplineRenderer extends XYSplineRenderer {

        private final JTimeSeriesRendererSupport support;

        public SplineRenderer(JTimeSeriesRendererSupport support) {
            this.support = support;
            setAutoPopulateSeriesShape(false);
            setLegendLine(ITEM_SHAPE);
            setBaseShape(ITEM_SHAPE);
            setUseFillPaint(true);
            setLegendItemLabelGenerator(support);
            setBaseItemLabelGenerator(support);
        }

        @Override
        public boolean isSeriesVisibleInLegend(int series) {
            return support.isSeriesLabelVisible(series);
        }

        @Override
        public boolean getItemShapeVisible(int series, int item) {
            return support.isObsHighlighted(series, item);
        }

        @Override
        public boolean isItemLabelVisible(int series, int item) {
            return support.isObsLabelVisible(series, item);
        }

        @Override
        public Paint getSeriesPaint(int series) {
            return support.getSeriesColor(series);
        }

        @Override
        public Paint getItemPaint(int series, int item) {
            // always use last item, see: XYSplineRenderer#drawPrimaryLineAsPath()
            return support.getSeriesColor(series);
        }

        @Override
        public Paint getItemFillPaint(int series, int item) {
            return support.getPlotColor();
        }

        @Override
        public Paint getLegendTextPaint(int series) {
            return support.getSeriesLabelColor(series);
        }

        @Override
        public Stroke getSeriesStroke(int series) {
            return support.getSeriesStroke(series);
        }

        @Override
        public Stroke getItemStroke(int series, int item) {
            // always use last item, see: XYSplineRenderer#drawPrimaryLineAsPath()
            return support.getSeriesStroke(series);
        }

        @Override
        public Stroke getItemOutlineStroke(int series, int item) {
            return support.getSeriesStroke(series);
        }

        @Override
        public Font getLegendTextFont(int series) {
            return support.getSeriesLabelFont(series);
        }

        @Override
        protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, XYDataset dataset, int series,
                int item, double x, double y, boolean negative) {
            support.drawItemLabel(g2, dataset, series, item, x, y);
        }
    }

    private static class BarRenderer extends ClusteredXYBarRenderer2 {

        private final JTimeSeriesRendererSupport support;

        public BarRenderer(JTimeSeriesRendererSupport support) {
            this.support = support;
            setAutoPopulateSeriesOutlineStroke(false);
            setMargin(.1);
            setLegendBar(ITEM_SHAPE);
            setShadowVisible(false);
            setDrawBarOutline(true);
            setBaseItemLabelsVisible(true);
            setBarPainter(new StandardXYBarPainter()); // avoid gradient
            setDrawBarOutline(false);
            setLegendItemLabelGenerator(support);
            setBaseItemLabelGenerator(support);
        }

        @Override
        public boolean isItemLabelVisible(int series, int item) {
            return support.isObsLabelVisible(series, item);
        }

        @Override
        public boolean isSeriesVisibleInLegend(int series) {
            return support.isSeriesLabelVisible(series);
        }

        @Override
        public Paint getSeriesPaint(int series) {
            return support.getSeriesColor(series);
        }

        @Override
        public Paint getItemPaint(int series, int item) {
            Color result = support.getObsColor(series, item);
            return support.isObsHighlighted(series, item) ? result.brighter() : result;
        }

        @Override
        public Paint getLegendTextPaint(int series) {
            return support.getSeriesLabelColor(series);
        }

        @Override
        public Font getLegendTextFont(int series) {
            return support.getSeriesLabelFont(series);
        }

        @Override
        protected void drawItemLabel(Graphics2D g2, XYDataset dataset, int series, int item, XYPlot plot,
                XYItemLabelGenerator generator, Rectangle2D bar, boolean negative) {
            support.drawItemLabel(g2, dataset, series, item, bar.getCenterX(), bar.getCenterY());
        }
    }

    private static class MarkerRenderer extends LineRenderer {

        public MarkerRenderer(JTimeSeriesRendererSupport support) {
            super(support);
        }

        @Override
        public Boolean getSeriesLinesVisible(int series) {
            return Boolean.FALSE;
        }

        @Override
        public boolean getItemShapeVisible(int series, int item) {
            return true;
        }

        @Override
        public Stroke getItemOutlineStroke(int series, int item) {
            return support.getObsStroke(series, item);
        }

        @Override
        public boolean getItemShapeFilled(int series, int item) {
            return support.isObsHighlighted(series, item);
        }

        @Override
        public Paint getItemFillPaint(int series, int item) {
            return support.getObsColor(series, item);
        }
    }

    private static class StackedBarRenderer extends StackedXYBarRenderer {

        private final JTimeSeriesRendererSupport support;

        public StackedBarRenderer(JTimeSeriesRendererSupport support) {
            this.support = support;
            setAutoPopulateSeriesOutlineStroke(false);
            setMargin(.1);
            setLegendBar(ITEM_SHAPE);
            setShadowVisible(false);
            setDrawBarOutline(true);
            setBaseItemLabelsVisible(true);
            setBarPainter(new StandardXYBarPainter()); // avoid gradient
            setDrawBarOutline(false);
            setLegendItemLabelGenerator(support);
            setBaseItemLabelGenerator(support);
        }

        @Override
        public Range findRangeBounds(XYDataset dataset) {
            // Fix NumberAxis#setAutoRangeIncludesZero()
            return dataset.getSeriesCount() != 0 ? super.findRangeBounds(dataset) : null;
        }

        @Override
        public boolean isItemLabelVisible(int series, int item) {
            return support.isObsLabelVisible(series, item);
        }

        @Override
        public boolean isSeriesVisibleInLegend(int series) {
            return support.isSeriesLabelVisible(series);
        }

        @Override
        public Paint getSeriesPaint(int series) {
            return support.getSeriesColor(series);
        }

        @Override
        public Paint getItemPaint(int series, int item) {
            Color result = support.getObsColor(series, item);
            return support.isObsHighlighted(series, item) ? result.brighter() : result;
        }

        @Override
        public Paint getLegendTextPaint(int series) {
            return support.getSeriesLabelColor(series);
        }

        @Override
        public Font getLegendTextFont(int series) {
            return support.getSeriesLabelFont(series);
        }

        @Override
        protected void drawItemLabel(Graphics2D g2, XYDataset dataset, int series, int item, XYPlot plot,
                XYItemLabelGenerator generator, Rectangle2D bar, boolean negative) {
            support.drawItemLabel(g2, dataset, series, item, bar.getCenterX(), bar.getCenterY());
        }
    }

    private static final class AreaRenderer extends XYAreaRenderer2 {

        private final JTimeSeriesRendererSupport support;

        public AreaRenderer(JTimeSeriesRendererSupport support) {
            this.support = support;
            setAutoPopulateSeriesShape(false);
            setAutoPopulateSeriesFillPaint(false);
            setAutoPopulateSeriesOutlineStroke(false);
            setBaseItemLabelsVisible(true);
            setLegendItemLabelGenerator(support);
            setBaseItemLabelGenerator(support);
            setLegendArea(ITEM_SHAPE);
        }

        @Override
        public boolean isItemLabelVisible(int row, int column) {
            return support.isObsLabelVisible(row, column);
        }

        @Override
        public boolean isSeriesVisibleInLegend(int series) {
            return support.isSeriesLabelVisible(series);
        }

        @Override
        public Paint getSeriesPaint(int series) {
            return support.getSeriesColor(series);
        }

        @Override
        public Paint getItemPaint(int series, int item) {
            Color result = support.getObsColor(series, item);
            return support.isObsHighlighted(series, item) ? result.brighter() : result;
        }

        @Override
        public Paint getLegendTextPaint(int series) {
            return support.getSeriesLabelColor(series);
        }

        @Override
        public Font getLegendTextFont(int series) {
            return support.getSeriesLabelFont(series);
        }

        // FIXME: method never called
        @Override
        protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, XYDataset dataset, int series,
                int item, double x, double y, boolean negative) {
            support.drawItemLabel(g2, dataset, series, item, x, y);
        }
    }

    private static final class StackedAreaRenderer extends StackedXYAreaRenderer2 {

        private final JTimeSeriesRendererSupport support;

        public StackedAreaRenderer(JTimeSeriesRendererSupport support) {
            this.support = support;
            setAutoPopulateSeriesShape(false);
            setAutoPopulateSeriesFillPaint(false);
            setAutoPopulateSeriesOutlineStroke(false);
            setBaseItemLabelsVisible(true);
            setLegendItemLabelGenerator(support);
            setBaseItemLabelGenerator(support);
            setLegendArea(ITEM_SHAPE);
        }

        @Override
        public boolean isItemLabelVisible(int row, int column) {
            return support.isObsLabelVisible(row, column);
        }

        @Override
        public boolean isSeriesVisibleInLegend(int series) {
            return support.isSeriesLabelVisible(series);
        }

        @Override
        public Paint getSeriesPaint(int series) {
            return support.getSeriesColor(series);
        }

        @Override
        public Paint getItemPaint(int series, int item) {
            Color result = support.getObsColor(series, item);
            return support.isObsHighlighted(series, item) ? result.brighter() : result;
        }

        @Override
        public Paint getLegendTextPaint(int series) {
            return support.getSeriesLabelColor(series);
        }

        @Override
        public Font getLegendTextFont(int series) {
            return support.getSeriesLabelFont(series);
        }

        // FIXME: method never called
        @Override
        protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, XYDataset dataset, int series,
                int item, double x, double y, boolean negative) {
            support.drawItemLabel(g2, dataset, series, item, x, y);
        }
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Custom tooltip">
    // package-visible to be used by Charts.
    static void drawToolTip(Graphics2D g2, double x, double y, double anchorOffset, String label, Font font,
            Paint paint, Paint fillPaint, Paint outlinePaint, Stroke outlineStroke) {
        LabelBlock block = new LabelBlock(label/*.replace("\n", "")*/, font, paint);
        block.setMargin(3, 3, 3, 3);

        Rectangle2D hotspot = createHotspot(g2, x, y, anchorOffset + 10, block.arrange(g2));
        Shape shape = createShape(x, y, hotspot);

        if (fillPaint != null) {
            g2.setPaint(fillPaint);
            g2.fill(shape);
        }

        if (outlinePaint != null && outlineStroke != null) {
            g2.setStroke(outlineStroke);
            g2.setPaint(outlinePaint);
            g2.draw(shape);
        }

        block.draw(g2, hotspot);
    }

    private static Color getForegroundColor(double luminance) {
        return (luminance > 127) ? Color.BLACK : Color.WHITE;
    }

    private static Shape createShape(double x, double y, Rectangle2D hotspot) {
        Area result = new Area(new RoundRectangle2D.Double(hotspot.getX(), hotspot.getY(), hotspot.getWidth(),
                hotspot.getHeight(), 8, 8));

        boolean right = hotspot.getMinX() > x;

        Polygon po = new Polygon();
        po.addPoint(0, 0);
        po.addPoint(0, 10);
        po.addPoint(10, 0);
        AffineTransform af = new AffineTransform();
        if (right) {
            af.translate(hotspot.getX() - 7, hotspot.getY() + hotspot.getHeight() / 2);
            af.rotate(-Math.PI / 4);
        } else {
            af.translate(hotspot.getMaxX() + 7, hotspot.getY() + hotspot.getHeight() / 2);
            af.rotate(Math.PI * 3 / 4);
        }

        Shape shape = af.createTransformedShape(po);
        result.add(new Area(shape));
        return result;
    }

    private static Rectangle2D createHotspot(Graphics2D g2, double x, double y, double xOffset, Size2D blockSize) {
        Rectangle bounds = g2.getClipBounds();
        bounds = new Rectangle(bounds.x + 2, bounds.y + 2, bounds.width - 4, bounds.height - 4);
        return createHotspot(bounds, x, y, xOffset, blockSize);
    }

    private static Rectangle2D createHotspot(Rectangle bounds, double x, double y, double xOffset,
            Size2D blockSize) {
        double xx = (x + xOffset + blockSize.width < bounds.getMaxX()) ? (x + xOffset)
                : (x - xOffset - blockSize.width);
        double halfHeight = blockSize.height / 2;
        double yy = (y - halfHeight < bounds.getMinY()) ? (bounds.getMinY())
                : (y + halfHeight > bounds.getMaxY()) ? (bounds.getMaxY() - blockSize.height) : (y - halfHeight);
        return new Rectangle2D.Double(xx, yy, blockSize.width, blockSize.height);
    }
    //</editor-fold>
}