edu.fullerton.timeseriesapp.TimeSeriesApp.java Source code

Java tutorial

Introduction

Here is the source code for edu.fullerton.timeseriesapp.TimeSeriesApp.java

Source

/*
 * Copyright (C) 2014 Joseph Areeda <joseph.areeda at ligo.org>
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.fullerton.timeseriesapp;

import com.areeda.jaDatabaseSupport.Database;
import edu.fullerton.jspWebUtils.WebUtilException;
import edu.fullerton.ldvjutils.ChanInfo;
import edu.fullerton.ldvjutils.LdvTableException;
import edu.fullerton.ldvtables.ChannelTable;
import edu.fullerton.ldvjutils.TimeInterval;
import edu.fullerton.viewerplugin.ChanDataBuffer;
import edu.fullerton.viewerplugin.PluginSupport;
import edu.fullerton.viewerplugin.ScaledAxisNumberFormat;
import java.awt.BasicStroke;
import java.awt.Color;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.DefaultXYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import viewerconfig.ViewConfigException;
import viewerconfig.ViewerConfig;

/**
 *
 * @author Joseph Areeda <joseph.areeda at ligo.org>
 */
public class TimeSeriesApp {

    private static long startMs;
    // database info

    private Database db;
    private ChannelTable chanTbl;
    private ChanInfo chanInfo;
    // program spec
    private final String version = "0.0.0";
    private final String programName = "SpectrumApp.jar";
    private final int debugLevel = 1;

    // command line parameters
    private boolean logX;
    private boolean logY;
    private boolean dodetrend;

    private Integer outX;
    private Integer outY;
    private Integer startGPS = 0;
    private Integer duration;
    private Integer xTicks;
    private Integer yTicks;

    private Float secPerFFT;
    private Float overlap;
    private Float fmin;
    private Float fmax;

    private String ofileName;

    // input specification
    private String[] channelNames;
    private String server;
    private String cType; // chan type eg raw, rds, ...

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        int stat;
        startMs = System.currentTimeMillis();
        try {
            TimeSeriesApp me = new TimeSeriesApp();

            // decode command line and set parameters
            boolean doit = me.processArgs(args);

            // generate image
            if (doit) {
                stat = me.doPlot();
            } else {
                stat = 10;
            }
        } catch (Exception ex) {
            Logger.getLogger(TimeSeriesApp.class.getName()).log(Level.SEVERE, null, ex);
            stat = 11;
        }
        // timing info
        double elapsedSec = (System.currentTimeMillis() - startMs) / 1000.;

        System.out.format("Run time: %1$.2f sec%n", elapsedSec);
        System.exit(stat);

    }

    private String timeAxis;
    private float lineThickness = 2;
    private String outFilename;

    /**
     * Process the command line arguments and set fields
     *
     * @param args the main passed in arguments
     * @return true if we continue false means exit (didn't want to do it here)
     */
    private boolean processArgs(String[] args) {
        boolean ret;
        TimeSeriesCommandLine cmd = new TimeSeriesCommandLine();
        if (cmd.parseCommand(args, programName, version)) {
            channelNames = cmd.getChannelNames();
            server = cmd.getServer();
            cType = cmd.getcType();

            duration = cmd.getDuration() < 1 ? 20 : cmd.getDuration();

            startGPS = cmd.getStartGPS();
            secPerFFT = cmd.getSecPerFFT() <= 0 ? 1 : cmd.getSecPerFFT();

            Float ft = cmd.getFmax();
            fmax = ft == null || ft < 0 ? 0.f : ft;

            ft = cmd.getFmin();
            fmin = ft == null || ft < 0 ? 0.f : ft;

            logX = cmd.isLogX();
            logY = cmd.isLogY();

            ofileName = cmd.getOfileName().isEmpty() ? "/tmp/test.png" : cmd.getOfileName();
            outX = Math.max(1024, cmd.getOutX());
            outY = Math.max(768, cmd.getOutY());

            xTicks = cmd.getxTicks() < 3 ? 7 : cmd.getxTicks();
            yTicks = cmd.getyTicks() < 3 ? 7 : cmd.getyTicks();

            timeAxis = cmd.getTimeAxis();
            outFilename = cmd.getOfileName();

            ret = true;
        } else {
            ret = false;
        }

        return ret;
    }

    private int doPlot() throws SQLException, WebUtilException, LdvTableException, ViewConfigException {
        int ret;
        HashSet<Integer> selections = getSelections();
        ArrayList<TimeInterval> times = getTimes();
        ArrayList<ChanDataBuffer> data = getData(selections, times);

        if (data.isEmpty()) {
            System.err.println("No data to plot.");
            ret = 3;
        } else {
            makePlot(data, false);
            ret = 0;
        }
        return ret;
    }

    private HashSet<Integer> getSelections() throws SQLException, LdvTableException, ViewConfigException {
        HashSet<Integer> ret = new HashSet<>();
        long strt = System.currentTimeMillis();
        getDbTables();

        {
            int n;
            if (channelNames == null || channelNames.length == 0) {
                throw new IllegalArgumentException("No Channel specified");
            }
            if ((server == null || server.isEmpty()) && (cType == null || cType.isEmpty())) {
                for (String channelName : channelNames) {
                    n = chanTbl.getBestMatch(channelName);
                    ret.add(n);
                }
            } else {
                if (cType == null || cType.isEmpty()) {
                    cType = "raw";
                }
                for (String channelName : channelNames) {
                    TreeSet<ChanInfo> chSet = chanTbl.getAsSet(server, channelName, cType, 10);
                    if (chSet.size() > 1) {
                        System.err.println("Warning: more than one channel matches: " + channelName);
                    }

                    for (ChanInfo ci : chSet) {
                        Integer id = ci.getId();
                        ret.add(id);
                    }
                }
            }
            if (ret.isEmpty()) {
                for (String channelName : channelNames) {
                    System.err.println("Channel requested was not found: " + channelName);
                }
            }
        }
        return ret;

    }

    private ArrayList<TimeInterval> getTimes() {

        TimeInterval ti = new TimeInterval(startGPS, startGPS + duration);
        ArrayList<TimeInterval> ret = new ArrayList<>();
        ret.add(ti);
        return ret;
    }

    private ArrayList<ChanDataBuffer> getData(HashSet<Integer> selections, ArrayList<TimeInterval> times)
            throws WebUtilException {

        ArrayList<ChanDataBuffer> bufList;
        try {
            bufList = ChanDataBuffer.dataBufFactory(db, selections, times, null, null, false);
            // is there any preprocessing to be done?
            for (ChanDataBuffer dbuf : bufList) {
                if (dodetrend) {
                    dbuf.detrend();
                }
            }
        } catch (Exception ex) {
            throw new WebUtilException(ex);
        }

        return bufList;

    }

    /**
     * Connect to the database and create table objects we need
     */
    private void getDbTables() throws LdvTableException, SQLException, ViewConfigException {
        if (db == null) {
            ViewerConfig vc = new ViewerConfig();
            db = vc.getDb();
            if (db == null) {
                throw new LdvTableException("Can't connect to LigoDV-web database");
            }
        }
        if (chanTbl == null) {
            chanTbl = new ChannelTable(db);
        }
    }

    public ArrayList<Integer> makePlot(ArrayList<ChanDataBuffer> dbufs, boolean compact) throws WebUtilException {
        int imageId;
        try {
            PluginSupport psupport = new PluginSupport();
            String gtitle = psupport.getTitle(dbufs, compact);
            String xAxisLabel = "";
            XYSeriesCollection xyds = new XYSeriesCollection();
            TimeSeriesCollection mtds = new TimeSeriesCollection();

            compact = dbufs.size() > 2 ? false : compact;
            for (ChanDataBuffer dbuf : dbufs) {
                int npts = dbuf.getDataLength();
                int sum = 1;
                if (npts > 2000) {
                    sum = npts / 2000;
                }
                String legend = psupport.getLegend(dbuf, compact);
                if (timeAxis.equalsIgnoreCase("utc")) {
                    TimeSeries ts = psupport.getTimeSeries(dbuf, legend, sum);
                    xAxisLabel = "Time (UTC)";
                    mtds.addSeries(ts);
                } else {
                    boolean isDt = timeAxis.equalsIgnoreCase("dt");
                    XYSeries xys = psupport.addXySeries(dbuf, legend, isDt, sum);
                    xAxisLabel = psupport.getxAxisLabel();
                    xyds.addSeries(xys);
                }
            }
            Double minx, miny, maxx, maxy;
            Double[] rng = new Double[4];

            if (timeAxis.equalsIgnoreCase("utc")) {
                PluginSupport.getRangeLimits(mtds, rng);
            } else {
                int skip = 0;
                PluginSupport.getRangeLimits(xyds, rng, skip);
            }
            minx = rng[0];
            miny = rng[1];
            maxx = rng[2];
            maxy = rng[3];

            int exp;
            if (timeAxis.equalsIgnoreCase("utc")) {
                exp = PluginSupport.scaleRange(mtds, miny, maxy);
            } else {
                exp = PluginSupport.scaleRange(xyds, miny, maxy);
            }

            ChartPanel cpnl;
            DefaultXYDataset ds = new DefaultXYDataset();
            JFreeChart chart;
            if (timeAxis.equalsIgnoreCase("utc")) {
                chart = ChartFactory.createTimeSeriesChart(gtitle, "Time (UTC)", "Counts", ds, true, true, false);
            } else {
                chart = ChartFactory.createXYLineChart(gtitle, xAxisLabel, "Counts", ds, PlotOrientation.VERTICAL,
                        true, false, false);
            }
            chart.setBackgroundPaint(Color.WHITE);
            chart.setAntiAlias(true);

            XYPlot plot = (XYPlot) chart.getPlot();
            plot.setBackgroundPaint(Color.white);
            plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
            plot.setDomainGridlinePaint(Color.LIGHT_GRAY);

            NumberAxis rangeAxis = new NumberAxis("Counts");
            ScaledAxisNumberFormat sanf = new ScaledAxisNumberFormat();
            sanf.setExp(exp);
            NumberTickUnit tickUnit;
            double plotRange;
            if (maxy != 0 && Math.abs(maxy - miny) < Math.abs(maxy) * 1e-30) {
                // this garbage is to get jFreeChart to always put labels on the Y axis
                double dt = Math.abs(miny) / 10;
                double scaledMin = (miny - dt) * Math.pow(10., exp);
                double scaledMax = (maxy + dt) * Math.pow(10., exp);
                rangeAxis.setRange(scaledMin, scaledMax);
                plotRange = scaledMax - scaledMin;
                rangeAxis.setAutoRange(false);
            } else {
                sanf.setMinMax(miny, maxy);
                plotRange = maxy - miny;
                rangeAxis.setAutoRange(true);
            }
            tickUnit = rangeAxis.getTickUnit();
            double tickSize = tickUnit.getSize();
            int nticks = (int) ((plotRange) / tickSize);
            if (nticks > yTicks) {
                double newTickSize = plotRange / yTicks;
                rangeAxis.setTickUnit(new NumberTickUnit(newTickSize));
            }
            rangeAxis.setNumberFormatOverride(sanf);
            rangeAxis.setAutoRangeIncludesZero(false);
            plot.setRangeAxis(rangeAxis);

            if (timeAxis.equalsIgnoreCase("utc")) {
                plot.setDataset(0, mtds);

            } else {
                plot.setDataset(0, xyds);
            }

            // Set the line thickness
            XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) plot.getRenderer();
            BasicStroke str = new BasicStroke(lineThickness);
            int n = plot.getSeriesCount();
            for (int i = 0; i < n; i++) {
                r.setSeriesStroke(i, str);
            }

            if (compact) {
                chart.removeLegend();
            }
            cpnl = new ChartPanel(chart);
            if (outFilename.isEmpty()) {
                imageId = psupport.saveImageAsPNG(cpnl);
            } else {
                imageId = 0;
                psupport.saveImageAsPdfFile(chart, outFilename);
            }

        } catch (SQLException | NoSuchAlgorithmException | IOException ex) {
            throw new WebUtilException(ex);
        }
        ArrayList<Integer> ret = new ArrayList<>();
        ret.add(imageId);
        return ret;
    }
}