altosui: Allow for multiple instances of each state in the graph
[fw/altos] / altosui / AltosGraphTime.java
1
2 // Copyright (c) 2010 Anthony Towns
3 // GPL v2 or later
4
5 package altosui;
6
7 import java.lang.*;
8 import java.io.*;
9 import java.util.concurrent.*;
10 import java.util.*;
11 import java.text.*;
12 import java.awt.Color;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15
16 import org.jfree.chart.ChartUtilities;
17 import org.jfree.chart.JFreeChart;
18 import org.jfree.chart.axis.AxisLocation;
19 import org.jfree.chart.axis.NumberAxis;
20 import org.jfree.chart.labels.StandardXYToolTipGenerator;
21 import org.jfree.chart.plot.PlotOrientation;
22 import org.jfree.chart.plot.XYPlot;
23 import org.jfree.chart.plot.ValueMarker;
24 import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
25 import org.jfree.chart.renderer.xy.XYItemRenderer;
26 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
27 import org.jfree.data.xy.XYSeries;
28 import org.jfree.data.xy.XYSeriesCollection;
29 import org.jfree.ui.RectangleAnchor;
30 import org.jfree.ui.TextAnchor;
31
32 class AltosGraphTime extends AltosGraph {
33     static interface Element {
34         void attachGraph(AltosGraphTime g);
35         void gotTimeData(double time, AltosDataPoint d);
36         void addToPlot(AltosGraphTime g, XYPlot plot);
37     }
38
39     static class TimeAxis implements Element {
40         private int axis;
41         private Color color;
42         private String label;
43         private AxisLocation locn;
44         private double min_y = Double.NaN;
45
46         public TimeAxis(int axis, String label, Color color, AxisLocation locn)
47         {
48             this.axis = axis;
49             this.color = color;
50             this.label = label;
51             this.locn = locn;
52         }
53
54         public void setLowerBound(double min_y) {
55             this.min_y = min_y;
56         }
57
58         public void attachGraph(AltosGraphTime g) { return; }
59         public void gotTimeData(double time, AltosDataPoint d) { return; }
60
61         public void addToPlot(AltosGraphTime g, XYPlot plot) {
62             NumberAxis numAxis = new NumberAxis(label);
63             if (!Double.isNaN(min_y))
64                 numAxis.setLowerBound(min_y);
65             plot.setRangeAxis(axis, numAxis);
66             plot.setRangeAxisLocation(axis, locn);
67             numAxis.setLabelPaint(color);
68             numAxis.setTickLabelPaint(color);
69             numAxis.setAutoRangeIncludesZero(false);
70         }
71     }
72
73     abstract static class TimeSeries implements Element {
74         protected XYSeries series;
75         private String axisName;
76         private Color color;
77
78         public TimeSeries(String axisName, String label, Color color) {
79             this.series = new XYSeries(label);
80             this.axisName = axisName;
81             this.color = color;
82         }
83
84         public void attachGraph(AltosGraphTime g) {
85             g.setAxis(this, axisName, color);
86         }
87         abstract public void gotTimeData(double time, AltosDataPoint d);
88
89         public void addToPlot(AltosGraphTime g, XYPlot plot) {
90             XYSeriesCollection dataset = new XYSeriesCollection();
91             dataset.addSeries(this.series);
92
93             XYItemRenderer renderer = new StandardXYItemRenderer();
94             renderer.setSeriesPaint(0, color);
95
96             int dataNum = g.getDataNum(this);
97             int axisNum = g.getAxisNum(this);
98
99             plot.setDataset(dataNum, dataset);
100             plot.mapDatasetToRangeAxis(dataNum, axisNum);
101             plot.setRenderer(dataNum, renderer);
102         }
103     }
104
105     static class StateMarker implements Element {
106         private LinkedList<Double> times = new LinkedList<Double>();
107         private String name;
108         private int state;
109         private int prev_state = Altos.ao_flight_startup;
110
111         StateMarker(int state, String name) {
112             this.state = state;
113             this.name = name;
114         }
115
116         public void attachGraph(AltosGraphTime g) { return; }
117         public void gotTimeData(double time, AltosDataPoint d) {
118             if (prev_state != state && d.state() == state)
119                 times.add(time);
120             prev_state = d.state();
121         }
122
123         public void addToPlot(AltosGraphTime g, XYPlot plot) {
124             for (double time : times) {
125                 ValueMarker m = new ValueMarker(time);
126                 m.setLabel(name);
127                 m.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
128                 m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
129                 plot.addDomainMarker(m);
130             }
131         }
132     }
133
134     private String callsign = null;
135     private Integer serial = null;
136     private Integer flight = null; 
137
138     private ArrayList<Element> elements;
139     private HashMap<String,Integer> axes;
140     private HashMap<Element,Integer> datasets;
141     private ArrayList<Integer> datasetAxis;
142
143     public AltosGraphTime(String title) {
144         this.filename = title.toLowerCase().replaceAll("[^a-z0-9]","_")+".png";
145         this.title = title;
146         this.elements = new ArrayList<Element>();
147         this.axes = new HashMap<String,Integer>();
148         this.datasets = new HashMap<Element,Integer>();
149         this.datasetAxis = new ArrayList<Integer>();
150     }
151
152     public AltosGraphTime addElement(Element e) {
153         e.attachGraph(this);
154         elements.add(e);
155         return this;
156     }
157
158     public void setAxis(Element ds, String axisName, Color color) {
159         Integer axisNum = axes.get(axisName);
160         int dsNum = datasetAxis.size();
161         if (axisNum == null) {
162             axisNum = newAxis(axisName, color);
163         }
164         datasets.put(ds, dsNum);
165         datasetAxis.add(axisNum);
166     }
167
168     public int getAxisNum(Element ds) {
169         return datasetAxis.get( datasets.get(ds) ).intValue();
170     }
171     public int getDataNum(Element ds) {
172         return datasets.get(ds).intValue();
173     }
174
175     private Integer newAxis(String name, Color color) {
176         int cnt = axes.size();
177         AxisLocation locn = AxisLocation.BOTTOM_OR_LEFT;
178         if (cnt > 0) {
179             locn = AxisLocation.TOP_OR_RIGHT;
180         }
181         Integer res = new Integer(cnt);
182         axes.put(name, res);
183         this.addElement(new TimeAxis(cnt, name, color, locn));
184         return res;
185     }
186
187     public void addData(AltosDataPoint d) {
188         double time = d.time();
189         for (Element e : elements) {
190             e.gotTimeData(time, d);
191         }
192         if (callsign == null) callsign = d.callsign();
193         if (serial == null) serial = new Integer(d.serial());
194         if (flight == null) flight = new Integer(d.flight());
195     }
196
197     public JFreeChart createChart() {
198         NumberAxis xAxis = new NumberAxis("Time (s)");
199         xAxis.setAutoRangeIncludesZero(false);
200         XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
201         XYPlot plot = new XYPlot();
202         plot.setDomainAxis(xAxis);
203         plot.setRenderer(renderer);
204         plot.setOrientation(PlotOrientation.VERTICAL);
205
206         if (serial != null && flight != null) {
207             title = serial + "/" + flight + ": " + title;
208         }
209         if (callsign != null) {
210             title = callsign + " - " + title;
211         }
212
213         renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
214         JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT,
215                                 plot, true);
216         ChartUtilities.applyCurrentTheme(chart);
217
218         plot.setDomainPannable(true);
219         plot.setRangePannable(true);
220    
221         for (Element e : elements) {
222             e.addToPlot(this, plot);
223         }
224
225         return chart;
226     }
227
228     public void toPNG() throws java.io.IOException {
229         if (axes.size() > 1) {
230             toPNG(800, 500);
231         } else {
232             toPNG(300, 500);
233         }
234     }
235 }