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