2 // Copyright (c) 2010 Anthony Towns
9 import java.util.concurrent.*;
12 import java.awt.Color;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import org.altusmetrum.AltosLib.*;
17 import org.jfree.chart.ChartUtilities;
18 import org.jfree.chart.JFreeChart;
19 import org.jfree.chart.axis.AxisLocation;
20 import org.jfree.chart.axis.NumberAxis;
21 import org.jfree.chart.labels.StandardXYToolTipGenerator;
22 import org.jfree.chart.plot.PlotOrientation;
23 import org.jfree.chart.plot.XYPlot;
24 import org.jfree.chart.plot.ValueMarker;
25 import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
26 import org.jfree.chart.renderer.xy.XYItemRenderer;
27 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
28 import org.jfree.data.xy.XYSeries;
29 import org.jfree.data.xy.XYSeriesCollection;
30 import org.jfree.ui.RectangleAnchor;
31 import org.jfree.ui.TextAnchor;
33 class AltosGraphTime extends AltosGraph {
34 static interface Element {
35 void attachGraph(AltosGraphTime g);
36 void gotTimeData(double time, AltosDataPoint d);
37 void addToPlot(AltosGraphTime g, XYPlot plot);
40 static class TimeAxis implements Element {
44 private AxisLocation locn;
45 private double min_y = Double.NaN;
47 public TimeAxis(int axis, String label, Color color, AxisLocation locn)
55 public void setLowerBound(double min_y) {
59 public void attachGraph(AltosGraphTime g) { return; }
60 public void gotTimeData(double time, AltosDataPoint d) { return; }
62 public void addToPlot(AltosGraphTime g, XYPlot plot) {
63 NumberAxis numAxis = new NumberAxis(label);
64 if (!Double.isNaN(min_y))
65 numAxis.setLowerBound(min_y);
66 plot.setRangeAxis(axis, numAxis);
67 plot.setRangeAxisLocation(axis, locn);
68 numAxis.setLabelPaint(color);
69 numAxis.setTickLabelPaint(color);
70 numAxis.setAutoRangeIncludesZero(false);
74 abstract static class TimeSeries implements Element {
75 protected XYSeries series;
76 private String axisName;
79 public TimeSeries(String axisName, String label, Color color) {
80 this.series = new XYSeries(label);
81 this.axisName = axisName;
85 public void attachGraph(AltosGraphTime g) {
86 g.setAxis(this, axisName, color);
88 abstract public void gotTimeData(double time, AltosDataPoint d);
90 public void addToPlot(AltosGraphTime g, XYPlot plot) {
91 XYSeriesCollection dataset = new XYSeriesCollection();
92 dataset.addSeries(this.series);
94 XYItemRenderer renderer = new StandardXYItemRenderer();
95 renderer.setSeriesPaint(0, color);
97 int dataNum = g.getDataNum(this);
98 int axisNum = g.getAxisNum(this);
100 plot.setDataset(dataNum, dataset);
101 plot.mapDatasetToRangeAxis(dataNum, axisNum);
102 plot.setRenderer(dataNum, renderer);
106 static class StateMarker implements Element {
107 private LinkedList<Double> times = new LinkedList<Double>();
110 private int prev_state = Altos.ao_flight_startup;
112 StateMarker(int state, String name) {
117 public void attachGraph(AltosGraphTime g) { return; }
118 public void gotTimeData(double time, AltosDataPoint d) {
119 if (prev_state != state && d.state() == state)
121 prev_state = d.state();
124 public void addToPlot(AltosGraphTime g, XYPlot plot) {
125 for (double time : times) {
126 ValueMarker m = new ValueMarker(time);
128 m.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
129 m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
130 plot.addDomainMarker(m);
135 private String callsign = null;
136 private Integer serial = null;
137 private Integer flight = null;
139 private ArrayList<Element> elements;
140 private HashMap<String,Integer> axes;
141 private HashMap<Element,Integer> datasets;
142 private ArrayList<Integer> datasetAxis;
144 public AltosGraphTime(String title) {
145 this.filename = title.toLowerCase().replaceAll("[^a-z0-9]","_")+".png";
147 this.elements = new ArrayList<Element>();
148 this.axes = new HashMap<String,Integer>();
149 this.datasets = new HashMap<Element,Integer>();
150 this.datasetAxis = new ArrayList<Integer>();
153 public AltosGraphTime addElement(Element e) {
159 public void setAxis(Element ds, String axisName, Color color) {
160 Integer axisNum = axes.get(axisName);
161 int dsNum = datasetAxis.size();
162 if (axisNum == null) {
163 axisNum = newAxis(axisName, color);
165 datasets.put(ds, dsNum);
166 datasetAxis.add(axisNum);
169 public int getAxisNum(Element ds) {
170 return datasetAxis.get( datasets.get(ds) ).intValue();
172 public int getDataNum(Element ds) {
173 return datasets.get(ds).intValue();
176 private Integer newAxis(String name, Color color) {
177 int cnt = axes.size();
178 AxisLocation locn = AxisLocation.BOTTOM_OR_LEFT;
180 locn = AxisLocation.TOP_OR_RIGHT;
182 Integer res = new Integer(cnt);
184 this.addElement(new TimeAxis(cnt, name, color, locn));
188 public void addData(AltosDataPoint d) {
189 double time = d.time();
190 for (Element e : elements) {
191 e.gotTimeData(time, d);
193 if (callsign == null) callsign = d.callsign();
194 if (serial == null) serial = new Integer(d.serial());
195 if (flight == null) flight = new Integer(d.flight());
198 public JFreeChart createChart() {
199 NumberAxis xAxis = new NumberAxis("Time (s)");
200 xAxis.setAutoRangeIncludesZero(false);
201 XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
202 XYPlot plot = new XYPlot();
203 plot.setDomainAxis(xAxis);
204 plot.setRenderer(renderer);
205 plot.setOrientation(PlotOrientation.VERTICAL);
207 if (serial != null && flight != null) {
208 title = serial + "/" + flight + ": " + title;
210 if (callsign != null) {
211 title = callsign + " - " + title;
214 renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
215 JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT,
217 ChartUtilities.applyCurrentTheme(chart);
219 plot.setDomainPannable(true);
220 plot.setRangePannable(true);
222 for (Element e : elements) {
223 e.addToPlot(this, plot);
229 public void toPNG() throws java.io.IOException {
230 if (axes.size() > 1) {