no.met.jtimeseries.chart.Utility.java Source code

Java tutorial

Introduction

Here is the source code for no.met.jtimeseries.chart.Utility.java

Source

/*******************************************************************************
 *   Copyright (C) 2016 MET Norway
 *   Contact information:
 *   Norwegian Meteorological Institute
 *   Henrik Mohns Plass 1
 *   0313 OSLO
 *   NORWAY
 *
 *   This file is part of jTimeseries
 *   jTimeseries is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *   jTimeseries 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 General Public License for more details.
 *   You should have received a copy of the GNU General Public License
 *   along with jTimeseries; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *******************************************************************************/
package no.met.jtimeseries.chart;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Image;
import java.awt.Paint;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import no.met.jtimeseries.data.dataset.ArrowDataset;
import no.met.jtimeseries.data.dataset.CloudDataset;
import no.met.jtimeseries.data.dataset.DefaultArrowDataset;
import no.met.jtimeseries.data.dataset.DefaultCloudDataset;
import no.met.jtimeseries.data.dataset.DefaultImageDataset;
import no.met.jtimeseries.data.dataset.ImageDataset;
import no.met.jtimeseries.data.item.AbstractValueItem;
import no.met.jtimeseries.data.item.NumberValueItem;
import no.met.phenomenen.NumberPhenomenon;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.Minute;
import org.jfree.data.time.Month;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.time.Second;
import org.jfree.data.time.Year;
import org.jfree.data.xy.DefaultWindDataset;
import org.jfree.data.xy.WindDataset;

public class Utility {

    public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";

    /**
     * Calculate the time at the threshold value
     * 
     * @param time1
     *            The first time string
     * @param value1
     *            The first value
     * @param time2
     *            The second time String
     * @param value2
     *            The second value
     * @param value3
     *            The threshold value
     * @return The time at the threshold
     */
    public static Date timeOfThreshold(Date time1, double value1, Date time2, double value2, double value3) {
        DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        long date3 = 0;
        long date1 = time1.getTime();
        long date2 = time2.getTime();

        double slope = (date2 - date1) / (value2 - value1);
        double bias = date2 - slope * value2;
        date3 = new Double(slope * value3 + bias).longValue();
        return new Date(date3);
    }

    /**
     * The the hour of a time.
     * 
     * @param time
     *            The time string. Such as "2012-03-20T14:00:00Z"
     * @return The hour. Such as 14 for the above case
     */
    public static int getHourOfDay(Date time) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time);
        return calendar.get(Calendar.HOUR_OF_DAY);
    }

    public static int getHourOfDayUTC(Date time) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
        calendar.setTime(time);
        return calendar.get(Calendar.HOUR_OF_DAY);
    }

    /**
     * @param orig
     *            The orginal date to add hours to
     * @param hours
     *            The number of hours to add.
     * @return A new date object with the specified number of hours added to it.
     */
    public static Date getDateWithAddedHours(Date orig, int hours) {

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
        calendar.setTime(orig);
        calendar.add(Calendar.HOUR_OF_DAY, hours);
        return calendar.getTime();

    }

    /**
     * Add value points that corresponding values are threshold values into the
     * value list
     * 
     * @param value
     *            The value list
     * @param Threshold
     *            The threshold value
     * @return New value list that contains threshold values
     */
    public static List<Double> addValueOfThreshold(List<Double> value, double threshold) {
        List<Double> valueWithThreshold = new ArrayList<Double>();

        if (value.size() >= 2) {
            double value1, value2;
            int index = 0;
            valueWithThreshold.add(index++, value.get(0));

            for (int i = 1; i < value.size(); i++) {
                value1 = value.get(i - 1);
                value2 = value.get(i);
                // if the threshold value is between two points,
                // then add a threshold point between the two points
                if (value1 < threshold && value2 > threshold || value1 > threshold && value2 < threshold) {
                    valueWithThreshold.add(index++, new Double(threshold));
                }
                valueWithThreshold.add(index++, value2);
            }
        }
        return valueWithThreshold;
    }

    /**
     * Add time points that the corresponding values are threshold values into
     * the time series
     * 
     * @param time
     *            Time List
     * @param value
     *            Value List
     * @param threshold
     *            The threshold value
     * @return New time list that contains threshold values
     */
    public static List<Date> addTimeOfThreshold(List<Date> time, List<Double> value, double threshold) {
        List<Date> timeWithThreshold = new ArrayList<Date>();
        if (value.size() >= 2) {
            double value1, value2;
            Date time1, time2;
            int index = 0;
            timeWithThreshold.add(index++, time.get(0));

            for (int i = 1; i < value.size(); i++) {
                value1 = value.get(i - 1);
                value2 = value.get(i);
                time1 = time.get(i - 1);
                time2 = time.get(i);
                // if the threshold value is between two points,
                // then add a threshold point between the two points
                if (value1 < threshold && value2 > threshold || value1 > threshold && value2 < threshold) {
                    timeWithThreshold.add(index++, timeOfThreshold(time1, value1, time2, value2, threshold));
                }
                timeWithThreshold.add(index++, time.get(i));
            }
        }
        return timeWithThreshold;
    }

    /**
     * Add time points that the corresponding values are threshold values into
     * the time series
     * 
     * @param time
     *            Time List
     * @param value
     *            Value List
     * @param threshold
     *            The threshold value
     * @return New time list that contains threshold values
     */
    public static List<NumberValueItem> addThresholdItems(List<NumberValueItem> items, double threshold) {

        List<NumberValueItem> itemsWithThreshold = new ArrayList<NumberValueItem>();
        if (items.size() >= 2) {
            double value1, value2;
            Date time1, time2;
            itemsWithThreshold.add(items.get(0));

            for (int i = 1; i < items.size(); i++) {
                value1 = items.get(i - 1).getValue();
                value2 = items.get(i).getValue();
                time1 = items.get(i - 1).getTimeFrom();
                time2 = items.get(i).getTimeFrom();

                if (value1 < threshold && value2 > threshold || value1 > threshold && value2 < threshold) {

                    Date timeOfThreshold = timeOfThreshold(time1, value1, time2, value2, threshold);
                    itemsWithThreshold.add(new NumberValueItem(timeOfThreshold, threshold));
                }
                itemsWithThreshold.add(items.get(i));
            }
        }

        return itemsWithThreshold;
    }

    /**
     * Calculate the hour difference between two time
     * 
     * @param timeFrom
     *            The start time
     * @param timeTo
     *            The end time
     * @return The hour difference
     */
    public static int hourDifference(Date timeFrom, Date timeTo) {
        double diff = (timeTo.getTime() - timeFrom.getTime()) / 1000 / 60 / 60;
        return new Double(diff).intValue();
    }

    /**
     * Removes time values closer than the specified interval (ensures that
     * dates are minHourInterval apart or more).
     * 
     * @param orig
     *            Original date list
     * @param minHourInterval
     *            The minimum interval hours
     * @param startPoint
     *            Set the start point
     * @return The filtered date list
     */
    public static List<Date> filterMinimumHourInterval(List<Date> orig, int minHourInterval, int startPoint) {
        if (orig.size() < 2) // nothing to filter
            return orig;
        ArrayList<Date> filtered = new ArrayList<Date>();
        filtered.add(new Date(orig.get(startPoint).getTime()));
        long hourDiff;
        if (orig.size() >= 2) {
            Date prevDate = new Date(orig.get(startPoint).getTime());
            for (int i = startPoint + 1; i < orig.size();) {
                hourDiff = ((orig.get(i).getTime() - prevDate.getTime()) / 3600000);
                // if hourdiff >= minHourInterval then add it directly
                if (hourDiff >= minHourInterval) {
                    filtered.add(new Date(orig.get(i).getTime()));
                    prevDate = new Date(orig.get(i).getTime());
                    i++;
                } else {
                    while (hourDiff < minHourInterval && i < orig.size() - 1) {
                        i++;
                        hourDiff = ((orig.get(i).getTime() - prevDate.getTime()) / 3600000);
                    }
                    filtered.add(new Date(orig.get(i).getTime()));
                    prevDate = new Date(orig.get(i).getTime());
                    i++;
                }
            }
        }
        filtered.remove(filtered.size() - 1);
        return filtered;
    }

    /**
     * Create a JFreeChart WindDataset from WindDirection and WindSpeed
     */
    public static WindDataset toChartWindDataset(NumberPhenomenon direction, NumberPhenomenon speed) {
        List<Date> timeList = direction.getTime();
        List<Double> degreeList = direction.getValue();
        List<Double> speedList = speed.getValue();

        Object[][] timeSeries = new Object[timeList.size()][];
        for (int i = 0; i < timeList.size(); i++) {
            timeSeries[i] = new Object[] { new Date(timeList.get(i).getTime()), new Double(degreeList.get(i)),
                    new Double(speedList.get(i)) };
        }
        Object[][][] dataSetArray = { timeSeries };
        return new DefaultWindDataset(dataSetArray);
    }

    public static ArrowDataset toChartArrowDataset(NumberPhenomenon direction, NumberPhenomenon position,
            double offset) {
        List<Date> timeList = direction.getTime();
        List<Double> degreeList = direction.getValue();
        List<Double> positionList = new ArrayList<Double>();
        if (offset < -1 || offset > 1) {
            offset = 0.1;
        }
        for (int i = 0; i < degreeList.size(); i++) {
            positionList.add(null);
        }

        if (position != null)
            positionList = position.getValue();

        Object[][] timeSeries = new Object[timeList.size()][];
        for (int i = 0; i < timeList.size(); i++) {
            timeSeries[i] = new Object[] { new Date(timeList.get(i).getTime()), new Double(degreeList.get(i)),
                    positionList.get(i) == null ? null : new Double(positionList.get(i)), new Double(offset) };
        }
        Object[][][] dataSetArray = { timeSeries };
        return new DefaultArrowDataset(dataSetArray);
    }

    public static ImageDataset toChartImageDataset(List<Date> time, List<Image> images, List<Double> position,
            double offset) {

        List<Double> positionList = new ArrayList<Double>();
        if (offset < -1 || offset > 1) {
            offset = 0.1;
        }
        if (position == null)
            for (int i = 0; i < images.size(); i++) {
                positionList.add(null);
            }
        else
            positionList = position;

        Object[][] timeSeries = new Object[time.size()][];
        for (int i = 0; i < time.size(); i++) {
            timeSeries[i] = new Object[] { new Date(time.get(i).getTime()), images.get(i),
                    positionList.get(i) == null ? null : new Double(positionList.get(i)), new Double(offset) };
        }
        Object[][][] dataSetArray = { timeSeries };
        return new DefaultImageDataset(dataSetArray);
    }

    /**
     * Create a JFreeChart CloudDataset from Cloud data
     */
    public static CloudDataset toChartCloudDataset(NumberPhenomenon fog, NumberPhenomenon highClouds,
            NumberPhenomenon mediumClouds, NumberPhenomenon lowClouds) {

        // assume all phenomenon has same size. Should be ok for data from
        // api.met.no
        int numItems = fog.getTimes().size();
        Object[][] timeSeries = new Object[fog.getTimes().size()][];
        for (int i = 0; i < numItems; i++) {
            timeSeries[i] = new Object[] { fog.getItem(i).getTimeFrom(),
                    ((NumberValueItem) fog.getItem(i)).getValue(),
                    ((NumberValueItem) highClouds.getItem(i)).getValue(),
                    ((NumberValueItem) mediumClouds.getItem(i)).getValue(),
                    ((NumberValueItem) lowClouds.getItem(i)).getValue() };
        }

        Object[][][] dataSetArray = { timeSeries };
        return new DefaultCloudDataset(dataSetArray);

    }

    /**
     * Return regulartimeperiod for create dataset
     * 
     * @param timeBase
     * @param time
     * @return
     */
    public static RegularTimePeriod getPeriod(TimeBase timeBase, Date date) {
        RegularTimePeriod period = null;

        if (timeBase == TimeBase.YEAR)
            period = new Year(date);
        else if (timeBase == TimeBase.MONTH)
            period = new Month(date);
        else if (timeBase == TimeBase.DAY)
            period = new Day(date);
        else if (timeBase == TimeBase.HOUR)
            period = new Hour(date);
        else if (timeBase == TimeBase.MINUTE)
            period = new Minute(date);
        else if (timeBase == TimeBase.SECOND)
            period = new Second(date);
        else if (timeBase == TimeBase.HOUR_3)
            return new FlexibleHour(date, 3);
        else if (timeBase == TimeBase.HOUR_6)
            return new FlexibleHour(date, 6);
        else if (timeBase == TimeBase.HOUR_12)
            return new FlexibleHour(date, 12);
        else if (timeBase == TimeBase.HOUR_24)
            return new FlexibleHour(date, 24);

        return period;
    }

    /**
     * Set the timebase automatically according to the time difference between
     * the first two time
     * 
     * @param xAxis
     *            time axis
     * @return the time base
     */
    public static TimeBase autoTimeBase(List<Date> xAxis) {
        Date time1 = xAxis.get(0);
        Date time2 = xAxis.get(1);

        return calculateTimeBase(time1, time2);
    }

    /**
     * Set the timebase automatically according to the time difference between
     * the first two time
     * 
     * @param xAxis
     *            time axis
     * @return the time base
     */
    public static TimeBase autoTimeBaseFromItems(List<? extends AbstractValueItem> items) {

        // need at least two items for the calculation
        if (items.size() < 2) {
            return null;
        }

        Date time1 = items.get(0).getTimeFrom();
        Date time2 = items.get(1).getTimeFrom();

        return calculateTimeBase(time1, time2);

    }

    /**
     * Create a plot with an error message in case of error.
     * 
     * @param width
     *            The width of the plot
     * @return The JFreeChart error plot
     */
    public static JFreeChart createErrorChart(int width) {
        XYPlot plot = new XYPlot();
        plot.setBackgroundPaint(null);
        plot.setBackgroundImage(Symbols.getImage("/error.png"));
        JFreeChart jchart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        jchart.setBorderVisible(false);
        Paint paint = new GradientPaint(0, 0, Color.WHITE, width, 0, Color.WHITE);
        jchart.setBackgroundPaint(paint);

        return jchart;
    }

    private static TimeBase calculateTimeBase(Date time1, Date time2) {
        long diff = time2.getTime() - time1.getTime();
        // if the diff is larger than one year
        if ((diff / 365 / 24 / 60 / 60 / 1000) >= 1)
            return TimeBase.YEAR;
        // if the diff is larger than one month
        else if ((diff / 28 / 24 / 60 / 60 / 1000) >= 1)
            return TimeBase.MONTH;
        // if the diff is larger than one day
        else if ((diff / 24 / 60 / 60 / 1000) >= 1)
            return TimeBase.DAY;
        // if the diff is larger than one hour
        else if ((diff / 60 / 60 / 1000) >= 1)
            return TimeBase.HOUR;
        // if the diff is larger than one minute
        else if ((diff / 60 / 1000) >= 1)
            return TimeBase.MINUTE;
        // if the diff is larger than one second
        else if (diff >= 1000)
            return TimeBase.SECOND;
        return null;
    }
}