/*
* -------------------------------------------------------------------------
* $Id: SunSpots.java,v 1.2 2004/05/26 19:16:36 estewart Exp $
* -------------------------------------------------------------------------
* Copyright (c) 1999 Visual Numerics Inc. All Rights Reserved.
*
* This software is confidential information which is proprietary to
* and a trade secret of Visual Numerics, Inc. Use, duplication or
* disclosure is subject to the terms of an appropriate license
* agreement.
*
* VISUAL NUMERICS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. VISUAL
* NUMERICS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
* ITS DERIVATIVES.
*--------------------------------------------------------------------------
*/
package com.imsl.demo.SunSpots;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.text.*;
import com.imsl.stat.*;
import com.imsl.chart.*;
import com.imsl.demo.gallery.Describe;
public class SunSpots extends JFrameChart
implements ActionListener, ChangeListener, MouseListener, MouseMotionListener {
private Chart chart;
private AxisXY axis;
private Data data, forecastData, upperLimit, lowerLimit;
private JSlider slider;
private JTextField predField, probField, displayField;
private boolean toForecast, zoom, iszoom;
private double[] from, to, current;
private ARMA arma;
private SimpleDateFormat dateFormat = new SimpleDateFormat("M/yyyy");
public SunSpots(boolean exitOnClose) {
if (!exitOnClose) {
// remove the WindowListener, installed by JFrameChart, that
// exits the application when the window is closed.
Object l[] = getListeners(java.awt.event.WindowListener.class);
for (int k = 0; k < l.length; k++) {
removeWindowListener((java.awt.event.WindowListener)l[k]);
}
}
Describe des = new Describe(this, "/com/imsl/demo/SunSpots/SunSpots.html");
des.show();
Dimension ds = des.getSize();
Dimension ss = getToolkit().getScreenSize();
int h = Math.min(ss.width/2, ss.height-ds.height-32);
int w = (int)(h/0.8);
setSize(w, h);
setLocation(ss.width-ds.width, ds.height);
// Set default values.
toForecast = zoom = false;
chart = getChart();
axis = new AxisXY(chart);
axis.getAxisX().getAxisLabel().setTextFormat(dateFormat);
axis.getAxisX().getAxisLabel().setTextAngle(90);
axis.getAxisX().getAxisTitle().setTitle("Year");
from = new double[2];
to = new double[2];
current = new double[2];
// Add MouseListener and MouseMotionListener to chart panel.
chart.getLegend().setPaint(true);
getPanel().addMouseListener(this);
getPanel().addMouseMotionListener(this);
Container cp = getContentPane();
// Create components in control panel.
JButton draw = new JButton("Forecast");
draw.addActionListener(this);
JButton reset = new JButton("Reset");
reset.addActionListener(this);
JLabel prob = new JLabel("Confidence", JLabel.CENTER);
probField = new JTextField("0.95", 4);
slider = new JSlider(1, 31, 11);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(1);
slider.setSnapToTicks(true);
slider.addChangeListener(this);
JLabel pred = new JLabel("Predicts", JLabel.CENTER);
predField = new JTextField("11", 3);
predField.setEditable(false);
JPanel buttonPanel = new JPanel();
buttonPanel.add(draw);
buttonPanel.add(prob);
buttonPanel.add(probField);
buttonPanel.add(slider);
buttonPanel.add(pred);
buttonPanel.add(predField);
buttonPanel.add(reset);
// Create the display panel
JPanel displayPanel = new JPanel();
displayField = new JTextField("Current position: X = ?? Y = ??", 53);
displayField.setBorder(null);
displayField.setCaretPosition(0);
displayField.setEditable(false);
displayPanel.add(displayField);
cp.add(buttonPanel, BorderLayout.NORTH);
cp.add(displayPanel, BorderLayout.SOUTH);
drawGraph();
setResizable(false);
}
// Get the description line.
private String getDescription() {
DecimalFormat nf = new DecimalFormat("0.0000");
StringBuffer sb = new StringBuffer();
if (iszoom) {
sb.append("Zooming In from: ");
sb.append("X = " + dateFormat.format(new Date((long) from[0])) + " Y = " + nf.format(from[1]));
sb.append(" To: ");
sb.append("X = " + dateFormat.format(new Date((long) to[0])) + " Y = " + nf.format(to[1]));
} else {
sb.append("Cursor At: X = " + dateFormat.format(new Date((long) current[0])) +
" Y = " + nf.format(current[1]));
}
return sb.toString();
}
// Draw the graph.
private void drawGraph() {
double[] y = {100.8, 81.6, 66.5, 34.8, 30.6, 7, 19.8, 92.5,
154.4, 125.9, 84.8, 68.1, 38.5, 22.8, 10.2, 24.1, 82.9,
132, 130.9, 118.1, 89.9, 66.6, 60, 46.9, 41, 21.3, 16,
6.4, 4.1, 6.8, 14.5, 34, 45, 43.1, 47.5, 42.2, 28.1, 10.1,
8.1, 2.5, 0, 1.4, 5, 12.2, 13.9, 35.4, 45.8, 41.1, 30.4,
23.9, 15.7, 6.6, 4, 1.8, 8.5, 16.6, 36.3, 49.7, 62.5, 67,
71, 47.8, 27.5, 8.5, 13.2, 56.9, 121.5, 138.3, 103.2,
85.8, 63.2, 36.8, 24.2, 10.7, 15, 40.1, 61.5, 98.5, 124.3,
95.9, 66.5, 64.5, 54.2, 39, 20.6, 6.7, 4.3, 22.8, 54.8,
93.8, 95.7, 77.2, 59.1, 44, 47, 30.5, 16.3, 7.3, 37.3,
73.9};
double[] x = new double[y.length];
// If in zooming mode, then zoom the graph. Otherwise, use default ranges.
if (zoom) {
axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisX().setWindow(Math.min(from[0], to[0]), Math.max(from[0], to[0]));
axis.getAxisY().setWindow(Math.min(from[1], to[1]), Math.max(from[1], to[1]));
} else {
axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisX().setWindow((new GregorianCalendar(1770, 0, 1)).getTimeInMillis(),
(new GregorianCalendar(1910, 0, 1)).getTimeInMillis());
axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
//axis.getAxisY().setWindow(-145.0, 235.0);
axis.getAxisY().setWindow(-50.0, 200.0);
}
// Draw the actual data.
for (int i = 0; i < y.length; i++) {
x[i] = (new GregorianCalendar(1770 + i, 0, 1)).getTimeInMillis();
}
data = new Data(axis, x, y);
data.setTitle("Actual data");
data.setDataType(Data.DATA_TYPE_LINE | Data.DATA_TYPE_MARKER);
data.setMarkerType(Data.MARKER_TYPE_FILLED_CIRCLE);
data.setMarkerSize(0.7);
data.setMarkerColor(Color.blue);
data.setLineColor(Color.blue);
// Use ARMA class (ARMA(2,1)) to get the forecast using backorigin = 3.
// Number of predicts and confidence level are from the interface.
if (toForecast) {
try {
int num = slider.getValue();
// Do this if Forecast is clicked.
if (arma == null) {
arma = new ARMA(2, 1, y);
arma.setRelativeError(0.0);
arma.setMaxIterations(0);
arma.compute();
arma.setConfidence(Double.parseDouble(probField.getText()));
arma.setBackwardOrigin(3);
}
double[][] forecast = arma.forecast(num);
double[] tmp = new double[num];
double[] x1 = new double[num];
// Draw the forecast data.
for (int i = 0; i < num; i++) {
tmp[i] = forecast[i][3];
x1[i] = (new GregorianCalendar(1870+i, 0, 1)).getTimeInMillis();
}
// Draw upper and lower limits.
double[] dev = arma.getDeviations();
double[] ul = new double[dev.length];
double[] ll = new double[dev.length];
double min = 0.0;
for (int i = 0; i < dev.length; i++) {
ul[i] = tmp[i] + dev[i];
ll[i] = tmp[i] - dev[i];
if (min > ll[i]) min = ll[i];
}
min = min - 0.01;
upperLimit = new Data(axis, x1, ul);
upperLimit.setTitle("Upper Limit");
upperLimit.setDataType(Data.DATA_TYPE_LINE | Data.DATA_TYPE_FILL);
upperLimit.setReference(min);
upperLimit.setFillColor(Color.yellow);
upperLimit.setFillOutlineColor(Color.white);
lowerLimit = new Data(axis, x1, ll);
lowerLimit.setTitle("Lower Limit");
lowerLimit.setDataType(Data.DATA_TYPE_LINE | Data.DATA_TYPE_FILL);
lowerLimit.setReference(min);
lowerLimit.setFillColor(Color.white);
lowerLimit.setFillOutlineColor(Color.white);
forecastData = new Data(axis, x1, tmp);
forecastData.setTitle("Forecast");
forecastData.setDataType(Data.DATA_TYPE_LINE);
forecastData.setLineColor(Color.red);
} catch (Exception e) {
}
}
}
// Get the information from the interface and redraw the chart.
private void update() {
double tmp = 0.0;
// Check the range for the confidence level. Use 0.95 if there is
// an error in the input.
try {
tmp = Double.parseDouble(probField.getText());
} catch (Exception exception) {
tmp = 0.95;
probField.setText("0.95");
} finally {
if (data != null) data.remove();
if (forecastData != null) forecastData.remove();
if (upperLimit != null) upperLimit.remove();
if (lowerLimit != null) lowerLimit.remove();
if ((tmp <= 0.0) || (tmp >= 1.0)) probField.setText("0.95");
drawGraph();
repaint();
}
}
// Implement ActionListener
public void actionPerformed(ActionEvent e) {
// If Reset is clicked, re-size the chart using autoscale.
// If Draw is clicked, redraw the chart.
if (e.getActionCommand().equals("Reset")) {
zoom = false;
} else {
toForecast = true;
arma = null;
}
update();
}
// Implement ChangeListener
public void stateChanged(ChangeEvent e) {
// Update text field for number of predicts.
predField.setText(Integer.toString(slider.getValue()));
}
// Implement MouseListener
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
// Get the starting position for zooming.
axis.mapDeviceToUser(e.getX(), e.getY(), from);
axis.mapDeviceToUser(e.getX(), e.getY(), to);
zoom = true;
iszoom = true;
}
public void mouseReleased(MouseEvent e) {
int x = e.getX();
int y = e.getY();
// The selection must be from 2 different points.
int[] point = new int[2];
axis.mapUserToDevice(from[0], from[1], point);
if (point[0] == x) return;
if (point[1] == y) return;
// Get the ending position for zooming.
axis.mapDeviceToUser(x, y, to);
iszoom = false;
update();
}
public void mouseEntered(MouseEvent e) {
setCursor(new java.awt.Cursor(java.awt.Cursor.CROSSHAIR_CURSOR));
}
public void mouseExited(MouseEvent e) {
setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
}
// Implement MouseMotionListener
public void mouseDragged(MouseEvent e) {
// Draw the selection square and update display information.
paint(e, true);
displayField.setText(getDescription());
}
public void mouseMoved(MouseEvent e) {
// Update display information.
axis.mapDeviceToUser(e.getX(), e.getY(), current);
displayField.setText(getDescription());
}
// Draw the selection in different color.
protected void paint(MouseEvent mouseEvent, boolean erase) {
Graphics2D g = (Graphics2D) SunSpots.this.getPanel().getGraphics();
if (erase) paint(g);
axis.mapDeviceToUser(mouseEvent.getX(), mouseEvent.getY(), to);
paint(g);
g.dispose();
}
protected void paint(Graphics2D g) {
Color colorFill = new Color(255,0,255,128);
Color colorOutline = new Color(0,255,0);
int[] pointA = new int[2];
int[] pointB = new int[2];
axis.mapUserToDevice(from[0], from[1], pointA);
axis.mapUserToDevice(to[0], to[1], pointB);
Shape shape = new Rectangle(Math.min(pointA[0], pointB[0]),
Math.min(pointA[1], pointB[1]), Math.abs(pointA[0]-pointB[0]),
Math.abs(pointA[1]-pointB[1]));
g.setXORMode(colorOutline);
g.draw(shape);
g.setXORMode(colorFill);
g.fill(shape);
}
public static void main(String args[]) {
boolean exitOnClose = true;
if (args.length > 0 && args[0].equals("-noexit")) exitOnClose = false;
new SunSpots(exitOnClose).show();
}
}