org.kalypso.ogc.sensor.diagview.jfreechart.ObservationPlot.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.ogc.sensor.diagview.jfreechart.ObservationPlot.java

Source

/*--------------- Kalypso-Header --------------------------------------------------------------------
    
 This file is part of kalypso.
 Copyright (C) 2004, 2005 by:
    
 Technical University Hamburg-Harburg (TUHH)
 Institute of River and coastal engineering
 Denickestr. 22
 21073 Hamburg, Germany
 http://www.tuhh.de/wb
    
 and
    
 Bjoernsen Consulting Engineers (BCE)
 Maria Trost 3
 56070 Koblenz, Germany
 http://www.bjoernsen.de
    
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
    
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.
    
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
 Contact:
    
 E-Mail:
 belger@bjoernsen.de
 schlienger@bjoernsen.de
 v.doemming@tuhh.de
    
 ---------------------------------------------------------------------------------------------------*/
package org.kalypso.ogc.sensor.diagview.jfreechart;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TimeZone;
import java.util.logging.Logger;

import org.jfree.chart.annotations.XYPointerAnnotation;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickUnit;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.TickUnitSource;
import org.jfree.chart.axis.TickUnits;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.IntervalMarker;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.ui.Layer;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.Spacer;
import org.jfree.ui.TextAnchor;
import org.kalypso.contribs.java.lang.reflect.ClassUtilities;
import org.kalypso.contribs.java.lang.reflect.ClassUtilityException;
import org.kalypso.core.KalypsoCorePlugin;
import org.kalypso.ogc.sensor.DateRange;
import org.kalypso.ogc.sensor.IAxis;
import org.kalypso.ogc.sensor.IObservation;
import org.kalypso.ogc.sensor.SensorException;
import org.kalypso.ogc.sensor.diagview.AxisMapping;
import org.kalypso.ogc.sensor.diagview.DiagView;
import org.kalypso.ogc.sensor.diagview.DiagViewCurve;
import org.kalypso.ogc.sensor.diagview.DiagViewCurve.AlarmLevel;
import org.kalypso.ogc.sensor.diagview.DiagramAxis;
import org.kalypso.ogc.sensor.metadata.ITimeseriesConstants;
import org.kalypso.ogc.sensor.template.ObsViewItem;
import org.kalypso.ogc.sensor.timeseries.TimeseriesUtils;
import org.kalypso.ui.internal.i18n.Messages;

/**
 * A plot for IObservation.
 * 
 * @author schlienger
 */
public class ObservationPlot extends XYPlot {
    /** maps the diagram axis (from the template) to the chart axis */
    private final transient Map<DiagramAxis, ValueAxis> m_diag2chartAxis = new HashMap<>();

    /** maps the chart axis to its position in the plot */
    private final transient Map<ValueAxis, Integer> m_chartAxes2Pos = new HashMap<>();

    /** maps the diagram axes (from the template) to a dataset */
    private final transient Map<DiagramAxis, CurveDataset> m_diagAxis2ds = new HashMap<>();

    /** maps the diagram curve to the data serie */
    private final transient Map<DiagViewCurve, XYCurveSerie> m_curve2serie = new HashMap<>();

    /** maps the series to their datasets */
    private final transient Map<XYCurveSerie, CurveDataset> m_serie2dataset = new HashMap<>();

    private final transient Map<Double, AlarmLevelPlotElement> m_yConsts = new HashMap<>();

    private final transient Map<Long, Marker> m_markers = new HashMap<>();

    private TimeZone m_timezone;

    /**
     * Constructor.
     */
    public ObservationPlot(final DiagView view) throws SensorException {
        // space between axes and data area
        setAxisOffset(new Spacer(Spacer.ABSOLUTE, 5, 5, 5, 5));

        // standard renderer
        setRenderer(new StandardXYItemRenderer(StandardXYItemRenderer.LINES));

        final TimeZone viewzone = view.getTimezone();
        final TimeZone timezone = viewzone == null ? KalypsoCorePlugin.getDefault().getTimeZone() : viewzone;
        setTimezone(timezone);

        final ObsViewItem[] curves = view.getItems();
        for (final ObsViewItem element : curves)
            addCurve((DiagViewCurve) element);

        setNoDataMessage(Messages.getString("org.kalypso.ogc.sensor.diagview.jfreechart.ObservationPlot.1")); //$NON-NLS-1$
    }

    public final void dispose() {
        clearCurves();
    }

    /**
     * Adds a diagram axis and configures it for the use in this plot.
     * 
     * @param axis
     *          can be null, if present it is used to define a best suited formatter for the chart axis
     */
    private synchronized void addDiagramAxis(final DiagramAxis diagAxis, final IAxis axis) throws SensorException {
        if (diagAxis == null)
            throw new IllegalArgumentException("DiagramAxis is null"); //$NON-NLS-1$

        try {
            final String axisType = axis == null ? null : axis.getType();
            final String axisClass = TimeseriesUtils.getAxisClassFor(axisType);
            if (axisClass == null) {
                final String msg = String.format(
                        "No Axis-Class defined for type '%s'. Must be defined in timeseries.ini or /KalypsoCore/src/org/kalypso/ogc/sensor/timeseries/resource/config.properties", //$NON-NLS-1$
                        axisType);
                throw new SensorException(msg);
            }

            final String axisLabel = diagAxis.toFullString();
            // Small hack:_ if label is null, we need to instantiate with an string, else the reflection does not work.
            final String[] arguments = axisLabel == null ? new String[] { "" } : new String[] { axisLabel }; //$NON-NLS-1$
            final ValueAxis vAxis = (ValueAxis) ClassUtilities.newInstance(axisClass, ValueAxis.class,
                    ObservationPlot.class.getClassLoader(), arguments);
            if (axisLabel == null)
                vAxis.setLabel(null);

            setTimezone(vAxis);
            vAxis.setInverted(diagAxis.isInverted());

            if (diagAxis.getLowerMargin() != null)
                vAxis.setLowerMargin(diagAxis.getLowerMargin().doubleValue());

            if (diagAxis.getUpperMaring() != null)
                vAxis.setUpperMargin(diagAxis.getUpperMaring().doubleValue());

            final AxisLocation loc = getLocation(diagAxis);

            if (diagAxis.getDirection().equals(DiagramAxis.DIRECTION_HORIZONTAL)) {
                final int pos = getAdequateDomainPos();
                setDomainAxis(pos, vAxis);
                setDomainAxisLocation(pos, loc);

                m_chartAxes2Pos.put(vAxis, new Integer(pos));
            } else {
                final int pos = getAdequateRangePos();
                setRangeAxis(pos, vAxis);
                setRangeAxisLocation(pos, loc);

                m_chartAxes2Pos.put(vAxis, new Integer(pos));
            }

            m_diag2chartAxis.put(diagAxis, vAxis);
        } catch (final ClassUtilityException e) {
            throw new SensorException(e);
        }
    }

    /**
     * @return adequate position for a new domain axis
     */
    private int getAdequateDomainPos() {
        final int count = getDomainAxisCount();
        if (count == 0)
            return 0;

        for (int i = 0; i < count; i++)
            if (getDomainAxis(i) == null)
                return i;

        return count;
    }

    /**
     * @return adequate position for a new range axis
     */
    private int getAdequateRangePos() {
        final int count = getRangeAxisCount();
        if (count == 0)
            return 0;

        for (int i = 0; i < count; i++)
            if (getRangeAxis(i) == null)
                return i;

        return count;
    }

    /**
     * @return adequate position for a new dataset
     */
    private int getAdequateDatasetPos() {
        final int count = getDatasetCount();
        if (count == 0)
            return 0;

        for (int i = 0; i < count; i++)
            if (getDataset(i) == null)
                return i;

        return count;
    }

    /**
     * Removes all curves from plot.
     */
    public final synchronized void clearCurves() {
        for (int i = 0; i < getDatasetCount(); i++)
            setDataset(i, null);

        clearBackground();

        m_serie2dataset.clear();
        m_curve2serie.clear();

        m_diagAxis2ds.clear();

        m_chartAxes2Pos.clear();
        m_diag2chartAxis.clear();

        clearDomainMarkers();
        clearAnnotations();

        m_yConsts.clear();
        m_markers.clear();

        clearDomainAxes();
        clearRangeAxes();
    }

    /**
     * Adds a curve to the plot
     */
    public final synchronized void addCurve(final DiagViewCurve curve) throws SensorException {
        if (curve == null || !curve.isShown() || m_curve2serie.containsKey(curve))
            return;

        final AxisMapping[] mings = curve.getMappings();

        IAxis xAxis = null;
        DiagramAxis xDiagAxis = null;
        IAxis yAxis = null;
        DiagramAxis yDiagAxis = null;

        if (mings != null) {
            for (final AxisMapping element : mings) {
                final DiagramAxis diagAxis = element.getDiagramAxis();

                if (diagAxis == null)
                    continue;

                // check if this axis is already present in this plot
                if (!m_diag2chartAxis.containsKey(diagAxis))
                    addDiagramAxis(diagAxis, element.getObservationAxis());

                if (diagAxis.getDirection().equals(DiagramAxis.DIRECTION_HORIZONTAL)) {
                    xAxis = element.getObservationAxis();
                    xDiagAxis = diagAxis;
                } else {
                    yAxis = element.getObservationAxis();
                    yDiagAxis = diagAxis;
                }
            }
        }

        if (xAxis == null || yAxis == null || xDiagAxis == null || yDiagAxis == null)
            throw new IllegalArgumentException(
                    Messages.getString("org.kalypso.ogc.sensor.diagview.jfreechart.ObservationPlot.2", curve)); //$NON-NLS-1$

        final XYCurveSerie serie = new XYCurveSerie(curve, xAxis, yAxis, xDiagAxis, yDiagAxis);

        m_curve2serie.put(curve, serie);

        final DiagramAxis key = yDiagAxis;

        CurveDataset cds = m_diagAxis2ds.get(key);

        if (cds == null) {
            cds = new CurveDataset();

            m_diagAxis2ds.put(key, cds);

            final int pos = getAdequateDatasetPos();
            setDataset(pos, cds);

            final XYItemRenderer renderer = getRenderer(yAxis.getType());
            setRenderer(pos, renderer);

            mapDatasetToDomainAxis(pos, m_chartAxes2Pos.get(m_diag2chartAxis.get(xDiagAxis)).intValue());
            mapDatasetToRangeAxis(pos, m_chartAxes2Pos.get(m_diag2chartAxis.get(yDiagAxis)).intValue());
        }

        // if a curve gets removed meanwhile, the mapping seriespos -> curvecolor
        // gets invalid! always reset all colors of all curves
        final Color curveColor = curve.getColor();
        final Stroke curveStroke = curve.getStroke();
        cds.addCurveSerie(serie, curveColor, curveStroke, getRenderer(indexOf(cds)));

        m_serie2dataset.put(serie, cds);

        analyseCurve(curve);
    }

    private void analyseCurve(final DiagViewCurve curve) throws SensorException {
        final IObservation obs = curve.getObservation();

        if (curve.getView().isFeatureEnabled(ITimeseriesConstants.FEATURE_FORECAST)) {
            // add a marker if the obs is a forecast
            final DateRange fr = TimeseriesUtils.isTargetForecast(obs);
            if (fr != null) {
                final Long begin = new Long(fr.getFrom().getTime());
                if (!m_markers.containsKey(begin)) {
                    final long end = fr.getTo().getTime();
                    final Marker marker = createMarker(begin.doubleValue(), end,
                            Messages.getString("ObservationPlot.0"), //$NON-NLS-1$
                            TimeseriesUtils.getColorForMD(ITimeseriesConstants.MD_VORHERSAGE));

                    addDomainMarker(marker, Layer.BACKGROUND);

                    m_markers.put(begin, marker);
                }
            }
        }

        if (obs != null) {
            // add a constant Y line if obs has alarmstufen
            if (curve.isDisplayAlarmLevel()) {
                final AlarmLevel[] alarms = curve.getAlarmLevels();
                for (final AlarmLevel element : alarms) {
                    final Double value = new Double(element.value);
                    if (!m_yConsts.containsKey(value)) {
                        final XYCurveSerie xyc = m_curve2serie.get(curve);
                        final double x;
                        if (xyc.getItemCount() > 1)
                            x = xyc.getXValue(1).doubleValue();
                        else
                            x = getDomainAxis().getLowerBound();

                        final AlarmLevelPlotElement vac = new AlarmLevelPlotElement(element, x, xyc.getYDiagAxis());
                        m_yConsts.put(value, vac);
                    }
                }
            }
        }
    }

    /**
     * Refreshes the plot in order to take the enabled features of the view into account
     */
    public final void refreshMetaInformation() {
        // clear all markers and extra informations
        clearDomainMarkers();
        clearAnnotations();
        m_yConsts.clear();
        m_markers.clear();

        // step through curves and analyse them

        for (final Object element : m_curve2serie.keySet()) {
            final DiagViewCurve curve = (DiagViewCurve) element;

            try {
                analyseCurve(curve);
            } catch (final SensorException e) {
                Logger.getLogger(getClass().getName()).warning(e.getLocalizedMessage());
            }
        }
    }

    /**
     * Removes the curve from the plot
     */
    public synchronized void removeCurve(final DiagViewCurve curve) {
        final XYCurveSerie serie = m_curve2serie.get(curve);

        if (serie != null) {
            final CurveDataset ds = m_serie2dataset.get(serie);

            if (ds != null) {
                ds.removeCurveSerie(serie);

                // if dataset is empty, also remove it and the range axis to which it
                // belongs
                if (ds.getSeriesCount() == 0) {
                    // and remove the dataset
                    for (int i = 0; i < getDatasetCount(); i++) {
                        if (getDataset(i) == ds) {
                            setDataset(i, null);

                            break;
                        }
                    }

                    // step though axes and remove the one that is associated to
                    // the dataset we want to remove
                    final Iterator<DiagramAxis> it = m_diagAxis2ds.keySet().iterator();
                    while (it.hasNext()) {
                        final DiagramAxis dAxis = it.next();
                        if (m_diagAxis2ds.get(dAxis) == ds) {
                            final ValueAxis cAxis = m_diag2chartAxis.get(dAxis);
                            final Integer pos = m_chartAxes2Pos.get(cAxis);

                            // trick: if it is the only axis, then do not remove it
                            // else NullPointerException in drawQuadrants (JFreeChart)
                            if (getRangeAxis() != getRangeAxis(pos.intValue()) || getRangeAxisCount() > 1) {
                                setRangeAxis(pos.intValue(), null);
                                // m_chartAxes2Pos.remove( cAxis );
                                m_diag2chartAxis.remove(dAxis);
                            }

                            it.remove();

                            // break, that's it
                            break;
                        }
                    }
                }
            }

            m_curve2serie.remove(curve);

            if (m_curve2serie.size() == 0)
                clearCurves();
        }
    }

    private void clearBackground() {
        setBackgroundImage(null);
    }

    /**
     * overwritten to return a default axis when no real axes defined yet
     * 
     * @see org.jfree.chart.plot.XYPlot#getDomainAxis()
     */
    @Override
    public final synchronized ValueAxis getDomainAxis() {
        if (m_diag2chartAxis.size() == 0)
            return new NumberAxis();

        return super.getDomainAxis();
    }

    /**
     * Overriden to return a default axis when no real axes defined yet
     * 
     * @see org.jfree.chart.plot.XYPlot#getRangeAxis()
     */
    @Override
    public final synchronized ValueAxis getRangeAxis() {
        if (m_diag2chartAxis.size() == 0)
            return new NumberAxis();

        for (int i = 0; i < getRangeAxisCount(); i++) {
            final ValueAxis rangeAxis = getRangeAxis(i);
            if (rangeAxis != null)
                return rangeAxis;
        }

        return new NumberAxis();
    }

    /**
     * overriden to also draw our alarmlevels
     * 
     * @see org.jfree.chart.plot.XYPlot#drawAnnotations(java.awt.Graphics2D, java.awt.geom.Rectangle2D, org.jfree.chart.plot.PlotRenderingInfo)
     */
    @Override
    public final synchronized void drawAnnotations(final Graphics2D g2d, final Rectangle2D rec,
            final PlotRenderingInfo arg2) {
        super.drawAnnotations(g2d, rec, arg2);

        drawAlarmLevels(g2d, rec);
    }

    /**
     * Draw alarmlevels (horizontal line and text annotation)
     */
    private void drawAlarmLevels(final Graphics2D g2, final Rectangle2D dataArea) {
        for (final Object element : m_yConsts.keySet()) {
            final AlarmLevelPlotElement vac = m_yConsts.get(element);

            final ValueAxis axis = m_diag2chartAxis.get(vac.getAxis());
            if (axis == null)
                continue;

            if (axis.getRange().contains(vac.getAlarm().value)) {
                final double yy = axis.valueToJava2D(vac.getAlarm().value, dataArea, RectangleEdge.LEFT);
                final Line2D line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy);
                // always set stroke, else we got the stroke from the last drawn line
                g2.setStroke(AlarmLevelPlotElement.STROKE_ALARM);
                g2.setPaint(vac.getAlarm().color);
                g2.draw(line);

                // and draw the text annotation: if annotation is outside (on top); label it below the line
                if (yy < dataArea.getMinY() + 20)
                    vac.getAnnotation().setAngle(Math.toRadians(20));
                else
                    vac.getAnnotation().setAngle(Math.toRadians(340));

                vac.getAnnotation().draw(g2, this, dataArea, getDomainAxis(), axis);
            }
        }
    }

    /**
     * Helper that creates a marker
     */
    private static Marker createMarker(final double start, final double end, final String label,
            final Color color) {
        final IntervalMarker marker = new IntervalMarker(start, end);
        marker.setPaint(color);
        marker.setLabel(label);
        marker.setLabelAnchor(RectangleAnchor.CENTER);
        marker.setLabelTextAnchor(TextAnchor.CENTER);

        return marker;
    }

    /**
     * Returns the adequate renderer for the given axis type.
     */
    private XYItemRenderer getRenderer(final String axisType) {
        // TODO: also overwrite bar renderer in order to hide legend
        if (axisType.equals(ITimeseriesConstants.TYPE_RAINFALL))
            return new XYBarRenderer();

        if (axisType.equals(ITimeseriesConstants.TYPE_POLDER_CONTROL))
            return new XYBarRenderer();

        return new XYCurveRenderer(StandardXYItemRenderer.LINES);
    }

    /**
     * @param diagAxis
     * @return location according to axis
     */
    private static AxisLocation getLocation(final DiagramAxis diagAxis) {
        if (diagAxis.getPosition().equals(DiagramAxis.POSITION_BOTTOM)) {
            return AxisLocation.BOTTOM_OR_LEFT;
        } else if (diagAxis.getPosition().equals(DiagramAxis.POSITION_TOP)) {
            return AxisLocation.TOP_OR_LEFT;
        } else if (diagAxis.getPosition().equals(DiagramAxis.POSITION_LEFT)) {
            return AxisLocation.TOP_OR_LEFT;
        } else if (diagAxis.getPosition().equals(DiagramAxis.POSITION_RIGHT)) {
            return AxisLocation.TOP_OR_RIGHT;
        }

        // default
        return AxisLocation.BOTTOM_OR_LEFT;
    }

    /**
     * Special tick units for kalypso
     */
    public static TickUnitSource createStandardDateTickUnits(final TimeZone zone) {
        if (zone == null) {
            throw new IllegalArgumentException("Null 'zone' argument."); //$NON-NLS-1$
        }
        final TickUnits units = new TickUnits();

        // date formatters
        // DateFormat f1 = new SimpleDateFormat("HH:mm:ss.SSS");
        // DateFormat f2 = new SimpleDateFormat("HH:mm:ss");
        // DateFormat f3 = new SimpleDateFormat("HH:mm");
        // DateFormat f4 = new SimpleDateFormat("d-MMM, HH:mm");
        // DateFormat f5 = new SimpleDateFormat("d-MMM");
        // DateFormat f6 = new SimpleDateFormat("MMM-yyyy");
        // DateFormat f7 = new SimpleDateFormat("yyyy");

        final DateFormat f1 = new SimpleDateFormat("dd.MM HH:mm:ss.SSS"); //$NON-NLS-1$
        final DateFormat f2 = new SimpleDateFormat("dd.MM HH:mm:ss"); //$NON-NLS-1$
        final DateFormat f3 = new SimpleDateFormat("dd.MM HH:mm"); //$NON-NLS-1$
        final DateFormat f4 = new SimpleDateFormat("dd.MM HH:mm"); //$NON-NLS-1$
        final DateFormat f5 = new SimpleDateFormat("dd.MM"); //$NON-NLS-1$
        final DateFormat f6 = new SimpleDateFormat("dd.MM.yy"); //$NON-NLS-1$
        final DateFormat f7 = new SimpleDateFormat("yyyy"); //$NON-NLS-1$

        f1.setTimeZone(zone);
        f2.setTimeZone(zone);
        f3.setTimeZone(zone);
        f4.setTimeZone(zone);
        f5.setTimeZone(zone);
        f6.setTimeZone(zone);
        f7.setTimeZone(zone);

        // milliseconds
        units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 1, f1));
        units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 5, DateTickUnit.MILLISECOND, 1, f1));
        units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 10, DateTickUnit.MILLISECOND, 1, f1));
        units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 25, DateTickUnit.MILLISECOND, 5, f1));
        units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 50, DateTickUnit.MILLISECOND, 10, f1));
        units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 100, DateTickUnit.MILLISECOND, 10, f1));
        units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 250, DateTickUnit.MILLISECOND, 10, f1));
        units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 500, DateTickUnit.MILLISECOND, 50, f1));

        // seconds
        units.add(new DateTickUnit(DateTickUnit.SECOND, 1, DateTickUnit.MILLISECOND, 50, f2));
        units.add(new DateTickUnit(DateTickUnit.SECOND, 5, DateTickUnit.SECOND, 1, f2));
        units.add(new DateTickUnit(DateTickUnit.SECOND, 10, DateTickUnit.SECOND, 1, f2));
        units.add(new DateTickUnit(DateTickUnit.SECOND, 30, DateTickUnit.SECOND, 5, f2));

        // minutes
        units.add(new DateTickUnit(DateTickUnit.MINUTE, 1, DateTickUnit.SECOND, 5, f3));
        units.add(new DateTickUnit(DateTickUnit.MINUTE, 2, DateTickUnit.SECOND, 10, f3));
        units.add(new DateTickUnit(DateTickUnit.MINUTE, 5, DateTickUnit.MINUTE, 1, f3));
        units.add(new DateTickUnit(DateTickUnit.MINUTE, 10, DateTickUnit.MINUTE, 1, f3));
        units.add(new DateTickUnit(DateTickUnit.MINUTE, 15, DateTickUnit.MINUTE, 5, f3));
        units.add(new DateTickUnit(DateTickUnit.MINUTE, 20, DateTickUnit.MINUTE, 5, f3));
        units.add(new DateTickUnit(DateTickUnit.MINUTE, 30, DateTickUnit.MINUTE, 5, f3));

        // hours
        units.add(new DateTickUnit(DateTickUnit.HOUR, 1, DateTickUnit.MINUTE, 5, f3));
        units.add(new DateTickUnit(DateTickUnit.HOUR, 2, DateTickUnit.MINUTE, 10, f3));
        units.add(new DateTickUnit(DateTickUnit.HOUR, 4, DateTickUnit.MINUTE, 30, f3));
        units.add(new DateTickUnit(DateTickUnit.HOUR, 6, DateTickUnit.HOUR, 1, f3));
        units.add(new DateTickUnit(DateTickUnit.HOUR, 12, DateTickUnit.HOUR, 1, f4));

        // days
        units.add(new DateTickUnit(DateTickUnit.DAY, 1, DateTickUnit.HOUR, 1, f5));
        units.add(new DateTickUnit(DateTickUnit.DAY, 2, DateTickUnit.HOUR, 1, f5));
        units.add(new DateTickUnit(DateTickUnit.DAY, 3, DateTickUnit.HOUR, 1, f5));
        units.add(new DateTickUnit(DateTickUnit.DAY, 4, DateTickUnit.HOUR, 1, f5));
        units.add(new DateTickUnit(DateTickUnit.DAY, 5, DateTickUnit.HOUR, 1, f5));
        units.add(new DateTickUnit(DateTickUnit.DAY, 6, DateTickUnit.HOUR, 1, f5));
        units.add(new DateTickUnit(DateTickUnit.DAY, 7, DateTickUnit.DAY, 1, f5));
        units.add(new DateTickUnit(DateTickUnit.DAY, 10, DateTickUnit.DAY, 1, f5));
        units.add(new DateTickUnit(DateTickUnit.DAY, 15, DateTickUnit.DAY, 1, f5));

        // months
        units.add(new DateTickUnit(DateTickUnit.MONTH, 1, DateTickUnit.DAY, 1, f6));
        units.add(new DateTickUnit(DateTickUnit.MONTH, 2, DateTickUnit.DAY, 1, f6));
        units.add(new DateTickUnit(DateTickUnit.MONTH, 3, DateTickUnit.MONTH, 1, f6));
        units.add(new DateTickUnit(DateTickUnit.MONTH, 4, DateTickUnit.MONTH, 1, f6));
        units.add(new DateTickUnit(DateTickUnit.MONTH, 6, DateTickUnit.MONTH, 1, f6));

        // years
        units.add(new DateTickUnit(DateTickUnit.YEAR, 1, DateTickUnit.MONTH, 1, f7));
        units.add(new DateTickUnit(DateTickUnit.YEAR, 2, DateTickUnit.MONTH, 3, f7));
        units.add(new DateTickUnit(DateTickUnit.YEAR, 5, DateTickUnit.YEAR, 1, f7));
        units.add(new DateTickUnit(DateTickUnit.YEAR, 10, DateTickUnit.YEAR, 1, f7));
        units.add(new DateTickUnit(DateTickUnit.YEAR, 25, DateTickUnit.YEAR, 5, f7));
        units.add(new DateTickUnit(DateTickUnit.YEAR, 50, DateTickUnit.YEAR, 10, f7));
        units.add(new DateTickUnit(DateTickUnit.YEAR, 100, DateTickUnit.YEAR, 20, f7));

        return units;

    }

    /**
     * mini helper class for storing a value and a color
     * 
     * @author schlienger
     */
    private static final class AlarmLevelPlotElement {
        /** Stroke, with wich the alarm-levels get drawn */
        public static final Stroke STROKE_ALARM = new BasicStroke(1.0f);

        private final AlarmLevel m_alarm;

        private final String m_label;

        private final DiagramAxis m_axis;

        private final XYPointerAnnotation m_annotation;

        public AlarmLevelPlotElement(final AlarmLevel al, final double xCoord, final DiagramAxis diagAxis) {
            m_alarm = al;
            m_label = al.label + " (" + al.value + ")"; //$NON-NLS-1$ //$NON-NLS-2$
            m_axis = diagAxis;
            m_annotation = new XYPointerAnnotation(al.label, xCoord, al.value, 0);
            getAnnotation().setArrowLength(10.0);
            getAnnotation().setLabelOffset(30);
            getAnnotation().setArrowPaint(al.color);
            getAnnotation().setPaint(al.color);
        }

        @Override
        public String toString() {
            return getClass().getName() + ": " + m_label + " " + getAlarm() + " " + getAxis().getLabel(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }

        protected XYPointerAnnotation getAnnotation() {
            return m_annotation;
        }

        protected AlarmLevel getAlarm() {
            return m_alarm;
        }

        protected DiagramAxis getAxis() {
            return m_axis;
        }
    }

    public final void setTimezone(final TimeZone timezone) {
        m_timezone = timezone;

        for (int i = 0; i < getDomainAxisCount(); i++) {
            final ValueAxis axis = getDomainAxis(i);
            setTimezone(axis);
        }

        for (int i = 0; i < getRangeAxisCount(); i++) {
            final ValueAxis axis = getRangeAxis(i);
            setTimezone(axis);
        }
    }

    private void setTimezone(final ValueAxis axis) {
        if (axis instanceof DateAxis) {
            final DateAxis da = (DateAxis) axis;
            final DateFormat df = da.getDateFormatOverride() == null ? null : da.getDateFormatOverride();
            if (df != null) {
                df.setTimeZone(m_timezone);
                da.setDateFormatOverride(df);
            }

            final TickUnitSource source = createStandardDateTickUnits(m_timezone);
            da.setStandardTickUnits(source);
        }
    }
}