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