2 * Copyright © 2010 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 package org.altusmetrum.altoslib_12;
24 class KMLWriter extends PrintWriter {
25 public PrintWriter printf(String format, Object ... arguments) {
26 return printf(Locale.ROOT, format, arguments);
29 public KMLWriter(File name) throws FileNotFoundException {
34 public class AltosKML implements AltosWriter {
38 int flight_state = -1;
40 double gps_start_altitude = AltosLib.MISSING;
41 AltosFlightSeries series;
42 AltosFlightStats stats;
43 AltosCalData cal_data;
45 static final String[] kml_state_colors = {
46 "FF000000", // startup
55 "FFFFFFFF", // invalid
56 "FFFF0000", // stateless
59 static String state_color(int state) {
60 if (state < 0 || kml_state_colors.length <= state)
61 return kml_state_colors[AltosLib.ao_flight_invalid];
62 return kml_state_colors[state];
65 static final String[] kml_style_colors = {
70 static String style_color(int style) {
71 if (style < 0 || kml_style_colors.length <= style)
72 return kml_style_colors[0];
73 return kml_style_colors[style];
76 static final String kml_header_start =
77 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
78 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" +
80 " <name>AO Flight#%d S/N: %03d</name>\n" +
81 " <Snippet maxLines=\"8\">\n";
83 static final String kml_header_end =
87 static final String kml_folder_start =
91 static final String kml_path_style_start =
92 " <Style id=\"ao-style-%s\">\n" +
93 " <LineStyle><color>%s</color><width>8</width></LineStyle>\n" +
97 static final String kml_path_style_end =
99 " </BalloonStyle>\n" +
102 static final String kml_point_style_start =
103 " <Style id=\"ao-style-%s\">\n" +
104 " <LabelStyle><color>%s</color></LabelStyle>\n" +
105 " <IconStyle><color>%s</color></IconStyle>\n" +
106 " <BalloonStyle>\n" +
109 static final String kml_point_style_end =
111 " </BalloonStyle>\n" +
114 static final String kml_path_start =
116 " <name>%s</name>\n" +
117 " <styleUrl>#ao-style-%s</styleUrl>\n" +
119 " <tessellate>1</tessellate>\n" +
120 " <altitudeMode>absolute</altitudeMode>\n" +
123 static final String kml_coord_fmt =
124 " %.7f,%.7f,%.7f <!-- alt %12.7f time %12.7f sats %d -->\n";
126 static final String kml_path_end =
127 " </coordinates>\n" +
131 static final String kml_point_start =
133 " <name>%s</name>\n" +
134 " <styleUrl>#ao-style-%s</styleUrl>\n" +
136 " <tessellate>1</tessellate>\n" +
137 " <altitudeMode>absolute</altitudeMode>\n" +
140 static final String kml_point_end =
141 " </coordinates>\n" +
145 static final String kml_folder_end =
148 static final String kml_footer =
153 AltosGPS gps = cal_data.gps_pad;
155 gps_start_altitude = cal_data.gps_pad_altitude;
156 out.printf(kml_header_start, cal_data.flight, cal_data.serial);
157 out.printf("Product: %s\n", stats.product);
158 out.printf("Firmware: %s\n", stats.firmware_version);
159 out.printf("Date: %04d-%02d-%02d\n",
160 gps.year, gps.month, gps.day);
161 out.printf("Time: %2d:%02d:%02d\n",
162 gps.hour, gps.minute, gps.second);
163 if (stats.max_height != AltosLib.MISSING)
164 out.printf("Max baro height: %s\n", AltosConvert.height.show(6, stats.max_height));
165 if (stats.max_gps_height != AltosLib.MISSING)
166 out.printf("Max GPS Height: %s\n", AltosConvert.height.show(6, stats.max_gps_height));
167 if (stats.max_speed != AltosLib.MISSING)
168 out.printf("Max speed: %s\n", AltosConvert.speed.show(6, stats.max_speed));
169 if (stats.max_acceleration != AltosLib.MISSING)
170 out.printf("Max accel: %s\n", AltosConvert.accel.show(6, stats.max_acceleration));
171 out.printf("%s", kml_header_end);
174 void folder_start(String folder_name) {
175 out.printf(kml_folder_start, folder_name);
179 out.printf(kml_folder_end);
182 void path_style_start(String style, String color) {
183 out.printf(kml_path_style_start, style, color);
186 void path_style_end() {
187 out.printf(kml_path_style_end);
190 void point_style_start(String style, String color) {
191 out.printf(kml_point_style_start, style, color, color);
194 void point_style_end() {
195 out.printf(kml_point_style_end);
198 void path_start(String name, String style) {
199 out.printf(kml_path_start, name, style);
203 out.printf(kml_path_end);
206 void point_start(String name, String style) {
207 out.printf(kml_point_start, name, style);
211 out.printf(kml_point_end);
214 boolean started = false;
216 private double baro_altitude(AltosFlightSeries series, double time) {
217 double height = series.value(AltosFlightSeries.height_name, time);
219 if (height == AltosLib.MISSING)
220 return AltosLib.MISSING;
221 if (cal_data.gps_pad_altitude == AltosLib.MISSING)
222 return AltosLib.MISSING;
224 return height + cal_data.gps_pad_altitude;
227 void coord(double time, AltosGPS gps, double altitude) {
228 out.printf(kml_coord_fmt,
230 altitude, (double) gps.alt,
235 out.printf("%s", kml_footer);
238 public void close() {
245 public void write(AltosGPS gps, double alt)
249 if (gps.lat == AltosLib.MISSING)
251 if (gps.lon == AltosLib.MISSING)
253 if (alt == AltosLib.MISSING) {
254 alt = cal_data.gps_pad_altitude;
255 if (alt == AltosLib.MISSING)
262 public void write_point(AltosTimeValue tv, boolean is_gps) {
263 int state = (int) tv.value;
264 String style_prefix = is_gps ? "gps-" : "baro-";
265 String state_name = AltosLib.state_name(state);
266 String state_label = AltosLib.state_name_capital(state);
267 String style_name = style_prefix + state_name;
268 String folder_name = is_gps ? "GPS" : "Baro";
269 String full_name = state_label + " (" + folder_name + ")";
270 AltosGPS gps = series.gps_before(tv.time);
271 double altitude = is_gps ? gps.alt : baro_altitude(series, tv.time);
273 point_style_start(style_name, state_color(state));
274 out.printf("%s\n", full_name);
276 case AltosLib.ao_flight_boost:
277 out.printf("Max accel %s\n", AltosConvert.accel.show(6, stats.max_acceleration));
278 out.printf("Max speed %s\n", AltosConvert.speed.show(6, stats.max_speed));
280 case AltosLib.ao_flight_coast:
281 case AltosLib.ao_flight_fast:
282 out.printf("Entry speed %s\n", AltosConvert.speed.show(6, stats.state_enter_speed[state]));
283 out.printf("Entry height %s\n", AltosConvert.height.show(6, altitude - cal_data.gps_pad_altitude));
285 case AltosLib.ao_flight_drogue:
286 out.printf("Max height %s\n", AltosConvert.height.show(6, is_gps ? stats.max_gps_height : stats.max_height));
287 out.printf("Average descent rate %s\n", AltosConvert.speed.show(6, -stats.state_speed[state]));
289 case AltosLib.ao_flight_main:
290 out.printf("Entry speed %s\n", AltosConvert.speed.show(6, -stats.state_enter_speed[state]));
291 out.printf("Entry height %s\n", AltosConvert.height.show(6, altitude - cal_data.gps_pad_altitude));
292 out.printf("Average descent rate %s\n", AltosConvert.speed.show(6, -stats.state_speed[state]));
294 case AltosLib.ao_flight_landed:
295 out.printf("Landing speed %s\n", AltosConvert.speed.show(6, -stats.state_enter_speed[state]));
299 point_start(full_name, style_name);
300 gps = series.gps_before(tv.time);
301 write(gps, altitude);
305 public void write(AltosFlightSeries series) {
306 this.series = series;
308 stats = new AltosFlightStats(series);
309 cal_data = series.cal_data();
311 if (series.height_series != null) {
312 folder_start("Barometric Altitude");
313 path_style_start("baro", style_color(0));
314 out.printf("Barometric Altitude\n");
315 out.printf("Max height: %s\n", AltosConvert.height.show(6, stats.max_height));
317 path_start("Barometric Altitude", "baro");
318 for (AltosGPSTimeValue gtv : series.gps_series)
319 write(gtv.gps, baro_altitude(series, gtv.time));
321 if (series.state_series != null) {
322 for (AltosTimeValue tv : series.state_series) {
323 write_point(tv, false);
328 folder_start("GPS Altitude");
329 path_style_start("gps", style_color(1));
330 out.printf("GPS Altitude");
331 out.printf("Max height: %s\n", AltosConvert.height.show(6, stats.max_gps_height));
333 path_start("GPS Altitude", "gps");
334 for (AltosGPSTimeValue gtv : series.gps_series)
335 write(gtv.gps, gtv.gps.alt);
337 if (series.state_series != null) {
338 for (AltosTimeValue tv : series.state_series) {
339 write_point(tv, true);
346 public AltosKML(File in_name) throws FileNotFoundException {
348 out = new KMLWriter(name);
351 public AltosKML(String in_string) throws FileNotFoundException {
352 this(new File(in_string));