From 0a6c76fc0525d6588a1d88127f0085f13a02f1af Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 25 May 2014 20:55:11 -0700 Subject: [PATCH 1/1] altosui/altosuilib/altoslib: Move more stuff out of autosui. Reduce site map memory Prepare to share with TeleGPS application. This also has the changes to the site map tile which cache only a few images and regenerate the flight path on the fly, saving piles of memory Signed-off-by: Keith Packard --- {altosui => altoslib}/AltosFlightStats.java | 63 ++++---- altoslib/Makefile.am | 3 +- altosui/AltosSiteMapTile.java | 122 --------------- altosui/Makefile.am | 12 +- .../AltosDisplayThread.java | 28 ++-- .../AltosFlightDisplay.java | 4 +- {altosui => altosuilib}/AltosFreqList.java | 7 +- {altosui => altosuilib}/AltosSiteMap.java | 56 ++++--- .../AltosSiteMapCache.java | 115 +++++++++++--- .../AltosSiteMapPreload.java | 22 +-- altosuilib/AltosSiteMapTile.java | 142 ++++++++++++++++++ {altosui => altosuilib}/AltosVoice.java | 3 +- {altosui => altosuilib}/GrabNDrag.java | 2 +- altosuilib/Makefile.am | 13 +- 14 files changed, 346 insertions(+), 246 deletions(-) rename {altosui => altoslib}/AltosFlightStats.java (76%) delete mode 100644 altosui/AltosSiteMapTile.java rename {altosui => altosuilib}/AltosDisplayThread.java (89%) rename {altosui => altosuilib}/AltosFlightDisplay.java (92%) rename {altosui => altosuilib}/AltosFreqList.java (93%) rename {altosui => altosuilib}/AltosSiteMap.java (90%) rename {altosui => altosuilib}/AltosSiteMapCache.java (54%) rename {altosui => altosuilib}/AltosSiteMapPreload.java (95%) create mode 100644 altosuilib/AltosSiteMapTile.java rename {altosui => altosuilib}/AltosVoice.java (97%) rename {altosui => altosuilib}/GrabNDrag.java (97%) diff --git a/altosui/AltosFlightStats.java b/altoslib/AltosFlightStats.java similarity index 76% rename from altosui/AltosFlightStats.java rename to altoslib/AltosFlightStats.java index d02a518d..87e04293 100644 --- a/altosui/AltosFlightStats.java +++ b/altoslib/AltosFlightStats.java @@ -15,41 +15,40 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altoslib_4; import java.io.*; -import org.altusmetrum.altoslib_3.*; public class AltosFlightStats { - double max_height; - double max_gps_height; - double max_speed; - double max_acceleration; - double[] state_speed = new double[Altos.ao_flight_invalid + 1]; - double[] state_accel = new double[Altos.ao_flight_invalid + 1]; - int[] state_count = new int[Altos.ao_flight_invalid + 1]; - double[] state_start = new double[Altos.ao_flight_invalid + 1]; - double[] state_end = new double[Altos.ao_flight_invalid + 1]; - int serial; - int flight; - int year, month, day; - int hour, minute, second; - double lat, lon; - double pad_lat, pad_lon; - boolean has_gps; - boolean has_other_adc; - boolean has_rssi; - boolean has_imu; - boolean has_mag; - boolean has_orient; - int num_ignitor; + public double max_height; + public double max_gps_height; + public double max_speed; + public double max_acceleration; + public double[] state_speed = new double[AltosLib.ao_flight_invalid + 1]; + public double[] state_accel = new double[AltosLib.ao_flight_invalid + 1]; + public int[] state_count = new int[AltosLib.ao_flight_invalid + 1]; + public double[] state_start = new double[AltosLib.ao_flight_invalid + 1]; + public double[] state_end = new double[AltosLib.ao_flight_invalid + 1]; + public int serial; + public int flight; + public int year, month, day; + public int hour, minute, second; + public double lat, lon; + public double pad_lat, pad_lon; + public boolean has_gps; + public boolean has_other_adc; + public boolean has_rssi; + public boolean has_imu; + public boolean has_mag; + public boolean has_orient; + public int num_ignitor; double landed_time(AltosStateIterable states) { AltosState state = null; for (AltosState s : states) { state = s; - if (state.state == Altos.ao_flight_landed) + if (state.state == AltosLib.ao_flight_landed) break; } @@ -128,10 +127,10 @@ public class AltosFlightStats { end_time = state.time; int state_id = state.state; - if (state.time >= boost_time && state_id < Altos.ao_flight_boost) - state_id = Altos.ao_flight_boost; - if (state.time >= landed_time && state_id < Altos.ao_flight_landed) - state_id = Altos.ao_flight_landed; + if (state.time >= boost_time && state_id < AltosLib.ao_flight_boost) + state_id = AltosLib.ao_flight_boost; + if (state.time >= landed_time && state_id < AltosLib.ao_flight_landed) + state_id = AltosLib.ao_flight_landed; if (state.gps != null && state.gps.locked) { year = state.gps.year; month = state.gps.month; @@ -140,7 +139,7 @@ public class AltosFlightStats { minute = state.gps.minute; second = state.gps.second; } - if (0 <= state_id && state_id < Altos.ao_flight_invalid) { + if (0 <= state_id && state_id < AltosLib.ao_flight_invalid) { double acceleration = state.acceleration(); double speed = state.speed(); if (acceleration != AltosLib.MISSING && speed != AltosLib.MISSING) { @@ -158,7 +157,7 @@ public class AltosFlightStats { max_gps_height = state.max_gps_height(); } if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) { - if (state_id <= Altos.ao_flight_pad) { + if (state_id <= AltosLib.ao_flight_pad) { pad_lat = state.gps.lat; pad_lon = state.gps.lon; } @@ -175,7 +174,7 @@ public class AltosFlightStats { if (state.ignitor_voltage != null && state.ignitor_voltage.length > num_ignitor) num_ignitor = state.ignitor_voltage.length; } - for (int s = Altos.ao_flight_startup; s <= Altos.ao_flight_landed; s++) { + for (int s = AltosLib.ao_flight_startup; s <= AltosLib.ao_flight_landed; s++) { if (state_count[s] > 0) { state_speed[s] /= state_count[s]; state_accel[s] /= state_count[s]; diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index 67cc38ff..bff09704 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -1,4 +1,4 @@ -AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -source 6 +AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6 JAVAROOT=bin @@ -51,6 +51,7 @@ altoslib_JAVA = \ AltosFlash.java \ AltosFlashListener.java \ AltosFlightReader.java \ + AltosFlightStats.java \ AltosFrequency.java \ AltosGPS.java \ AltosGPSSat.java \ diff --git a/altosui/AltosSiteMapTile.java b/altosui/AltosSiteMapTile.java deleted file mode 100644 index 7d5b65e1..00000000 --- a/altosui/AltosSiteMapTile.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package altosui; - -import java.awt.*; -import java.awt.image.*; -import javax.swing.*; -import java.awt.geom.Point2D; -import java.awt.geom.Line2D; -import org.altusmetrum.altoslib_3.*; - -public class AltosSiteMapTile extends JLayeredPane { - JLabel mapLabel; - JLabel draw; - Graphics2D g2d; - int px_size; - - public void loadMap(ImageIcon icn) { - mapLabel.setIcon(icn); - } - - public void clearMap() { - fillLabel(mapLabel, Color.GRAY, px_size); - g2d = fillLabel(draw, new Color(127,127,127,0), px_size); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); - } - - static Color stateColors[] = { - Color.WHITE, // startup - Color.WHITE, // idle - Color.WHITE, // pad - Color.RED, // boost - Color.PINK, // fast - Color.YELLOW, // coast - Color.CYAN, // drogue - Color.BLUE, // main - Color.BLACK // landed - }; - - private boolean drawn_landed_circle = false; - private boolean drawn_boost_circle = false; - public synchronized void show(AltosState state, AltosListenerState listener_state, - Point2D.Double last_pt, Point2D.Double pt) - { - if (0 <= state.state && state.state < stateColors.length) { - g2d.setColor(stateColors[state.state]); - } - g2d.draw(new Line2D.Double(last_pt, pt)); - - if (state.state == 3 && !drawn_boost_circle) { - drawn_boost_circle = true; - g2d.setColor(Color.RED); - g2d.drawOval((int)last_pt.x-5, (int)last_pt.y-5, 10, 10); - g2d.drawOval((int)last_pt.x-20, (int)last_pt.y-20, 40, 40); - g2d.drawOval((int)last_pt.x-35, (int)last_pt.y-35, 70, 70); - } - if (state.state == 8 && !drawn_landed_circle) { - drawn_landed_circle = true; - g2d.setColor(Color.BLACK); - g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); - g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); - g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); - } - - repaint(); - } - - public void draw_circle(Point2D.Double pt) { - g2d.setColor(Color.RED); - g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); - g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); - g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); - } - - public static Graphics2D fillLabel(JLabel l, Color c, int px_size) { - BufferedImage img = new BufferedImage(px_size, px_size, - BufferedImage.TYPE_INT_ARGB); - Graphics2D g = img.createGraphics(); - g.setColor(c); - g.fillRect(0, 0, px_size, px_size); - l.setIcon(new ImageIcon(img)); - return g; - } - - public AltosSiteMapTile(int in_px_size) { - px_size = in_px_size; - setPreferredSize(new Dimension(px_size, px_size)); - - mapLabel = new JLabel(); - fillLabel(mapLabel, Color.GRAY, px_size); - mapLabel.setOpaque(true); - mapLabel.setBounds(0, 0, px_size, px_size); - add(mapLabel, new Integer(0)); - - draw = new JLabel(); - g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); - draw.setBounds(0, 0, px_size, px_size); - draw.setOpaque(false); - - add(draw, new Integer(1)); - } -} diff --git a/altosui/Makefile.am b/altosui/Makefile.am index 76fe9961..0440b4af 100644 --- a/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -1,6 +1,6 @@ JAVAROOT=classes -AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -source 6 +AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6 man_MANS=altosui.1 @@ -17,7 +17,6 @@ altosui_BT = \ AltosBTKnown.java altosui_JAVA = \ - GrabNDrag.java \ AltosAscent.java \ AltosChannelMenu.java \ AltosCompanionInfo.java \ @@ -31,20 +30,16 @@ altosui_JAVA = \ AltosCSVUI.java \ AltosDescent.java \ AltosDeviceUIDialog.java \ - AltosDisplayThread.java \ AltosEepromDelete.java \ AltosEepromManage.java \ AltosEepromMonitorUI.java \ AltosEepromSelect.java \ AltosFlashUI.java \ - AltosFlightDisplay.java \ AltosFlightInfoTableModel.java \ - AltosFlightStats.java \ AltosFlightStatsTable.java \ AltosFlightStatus.java \ AltosFlightStatusUpdate.java \ AltosFlightUI.java \ - AltosFreqList.java \ Altos.java \ AltosIdleMonitorUI.java \ AltosIgniteUI.java \ @@ -61,17 +56,12 @@ altosui_JAVA = \ AltosScanUI.java \ AltosSerial.java \ AltosSerialInUseException.java \ - AltosSiteMap.java \ - AltosSiteMapPreload.java \ - AltosSiteMapCache.java \ - AltosSiteMapTile.java \ AltosUI.java \ AltosGraph.java \ AltosGraphDataPoint.java \ AltosGraphDataSet.java \ AltosGraphUI.java \ AltosDataChooser.java \ - AltosVoice.java \ $(altosui_BT) JFREECHART_CLASS= \ diff --git a/altosui/AltosDisplayThread.java b/altosuilib/AltosDisplayThread.java similarity index 89% rename from altosui/AltosDisplayThread.java rename to altosuilib/AltosDisplayThread.java index ab85607d..e88a891e 100644 --- a/altosui/AltosDisplayThread.java +++ b/altosuilib/AltosDisplayThread.java @@ -15,13 +15,13 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altosuilib_2; import java.awt.*; import javax.swing.*; import java.io.*; import java.text.*; -import org.altusmetrum.altoslib_3.*; +import org.altusmetrum.altoslib_4.*; public class AltosDisplayThread extends Thread { @@ -78,7 +78,7 @@ public class AltosDisplayThread extends Thread { return; /* reset the landing count once we hear about a new flight */ - if (state.state < Altos.ao_flight_drogue) + if (state.state < AltosLib.ao_flight_drogue) reported_landing = 0; /* Shut up once the rocket is on the ground */ @@ -87,8 +87,8 @@ public class AltosDisplayThread extends Thread { } /* If the rocket isn't on the pad, then report height */ - if (Altos.ao_flight_drogue <= state.state && - state.state < Altos.ao_flight_landed && + if (AltosLib.ao_flight_drogue <= state.state && + state.state < AltosLib.ao_flight_landed && state.from_pad != null && state.range >= 0) { @@ -99,7 +99,7 @@ public class AltosDisplayThread extends Thread { (int) (state.from_pad.bearing + 0.5), (int) (state.elevation + 0.5), AltosConvert.distance.say(state.range)); - } else if (state.state > Altos.ao_flight_pad) { + } else if (state.state > AltosLib.ao_flight_pad) { voice.speak(AltosConvert.height.say_units(state.height())); } else { reported_landing = 0; @@ -109,10 +109,10 @@ public class AltosDisplayThread extends Thread { * either we've got a landed report or we haven't heard from it in * a long time */ - if (state.state >= Altos.ao_flight_drogue && + if (state.state >= AltosLib.ao_flight_drogue && (last || System.currentTimeMillis() - state.received_time >= 15000 || - state.state == Altos.ao_flight_landed)) + state.state == AltosLib.ao_flight_landed)) { if (Math.abs(state.speed()) < 20 && state.height() < 100) voice.speak("rocket landed safely"); @@ -123,8 +123,8 @@ public class AltosDisplayThread extends Thread { (int) (state.from_pad.bearing + 0.5), AltosConvert.distance.say_units(state.from_pad.distance)); ++reported_landing; - if (state.state != Altos.ao_flight_landed) { - state.state = Altos.ao_flight_landed; + if (state.state != AltosLib.ao_flight_landed) { + state.state = AltosLib.ao_flight_landed; show_safely(); } } @@ -183,13 +183,13 @@ public class AltosDisplayThread extends Thread { boolean ret = false; if (old_state == null || old_state.state != state.state) { voice.speak(state.state_name()); - if ((old_state == null || old_state.state <= Altos.ao_flight_boost) && - state.state > Altos.ao_flight_boost) { + if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) && + state.state > AltosLib.ao_flight_boost) { voice.speak("max speed: %s.", AltosConvert.speed.say_units(state.max_speed() + 0.5)); ret = true; - } else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) && - state.state >= Altos.ao_flight_drogue) { + } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) && + state.state >= AltosLib.ao_flight_drogue) { voice.speak("max height: %s.", AltosConvert.height.say_units(state.max_height() + 0.5)); ret = true; diff --git a/altosui/AltosFlightDisplay.java b/altosuilib/AltosFlightDisplay.java similarity index 92% rename from altosui/AltosFlightDisplay.java rename to altosuilib/AltosFlightDisplay.java index c1264259..5695a015 100644 --- a/altosui/AltosFlightDisplay.java +++ b/altosuilib/AltosFlightDisplay.java @@ -15,9 +15,9 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altosuilib_2; -import org.altusmetrum.altoslib_3.*; +import org.altusmetrum.altoslib_4.*; public interface AltosFlightDisplay { void reset(); diff --git a/altosui/AltosFreqList.java b/altosuilib/AltosFreqList.java similarity index 93% rename from altosui/AltosFreqList.java rename to altosuilib/AltosFreqList.java index 525e5ce5..e1299aae 100644 --- a/altosui/AltosFreqList.java +++ b/altosuilib/AltosFreqList.java @@ -15,13 +15,12 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altosuilib_2; import javax.swing.*; -import org.altusmetrum.altoslib_3.*; -import org.altusmetrum.altosuilib_1.*; +import org.altusmetrum.altoslib_4.*; -public class AltosFreqList extends JComboBox { +public class AltosFreqList extends JComboBox { String product; int serial; diff --git a/altosui/AltosSiteMap.java b/altosuilib/AltosSiteMap.java similarity index 90% rename from altosui/AltosSiteMap.java rename to altosuilib/AltosSiteMap.java index 643417b5..1cfbc8b5 100644 --- a/altosui/AltosSiteMap.java +++ b/altosuilib/AltosSiteMap.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altosuilib_2; import java.awt.*; import javax.swing.*; @@ -23,8 +23,7 @@ import java.io.*; import java.lang.Math; import java.awt.geom.Point2D; import java.util.concurrent.*; -import org.altusmetrum.altoslib_3.*; -import org.altusmetrum.altosuilib_1.*; +import org.altusmetrum.altoslib_4.*; public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { // preferred vertical step in a tile in naut. miles @@ -149,13 +148,13 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } private void loadMap(final AltosSiteMapTile tile, - File pngfile, String pngurl) + final File pngfile, String pngurl) { - final ImageIcon res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); - if (res != null) { + boolean loaded = AltosSiteMapCache.fetchMap(pngfile, pngurl); + if (loaded) { SwingUtilities.invokeLater(new Runnable() { public void run() { - tile.loadMap(res); + tile.loadMap(pngfile); } }); } else { @@ -164,22 +163,30 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } } - File pngfile; - String pngurl; - public int prefetchMap(int x, int y) { + class AltosSiteMapPrefetch { + int x; + int y; + int result; + File pngfile; + String pngurl; + } + + public AltosSiteMapPrefetch prefetchMap(int x, int y) { + AltosSiteMapPrefetch prefetch = new AltosSiteMapPrefetch(); LatLng map_latlng = latlng( -centre.x + x*px_size + px_size/2, -centre.y + y*px_size + px_size/2); - pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom); - pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom); - if (pngfile.exists()) { - return 1; - } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { - return 0; + prefetch.pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom); + prefetch.pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom); + if (prefetch.pngfile.exists()) { + prefetch.result = 1; + } else if (AltosSiteMapCache.fetchMap(prefetch.pngfile, prefetch.pngurl)) { + prefetch.result = 0; } else { - return -1; + prefetch.result = -1; } + return prefetch; } public static void prefetchMaps(double lat, double lng) { @@ -193,17 +200,18 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { int dx = -w/2, dy = -h/2; for (int y = dy; y < h+dy; y++) { for (int x = dx; x < w+dx; x++) { - int r = asm.prefetchMap(x, y); - switch (r) { + AltosSiteMapPrefetch prefetch = asm.prefetchMap(x, y); + switch (prefetch.result) { case 1: - System.out.printf("Already have %s\n", asm.pngfile); + System.out.printf("Already have %s\n", prefetch.pngfile); break; case 0: - System.out.printf("Fetched map %s\n", asm.pngfile); + System.out.printf("Fetched map %s\n", prefetch.pngfile); break; case -1: - System.out.printf("# Failed to fetch file %s\n", asm.pngfile); - System.out.printf(" wget -O '%s' ''\n", asm.pngfile, asm.pngurl); + System.out.printf("# Failed to fetch file %s\n", prefetch.pngfile); + System.out.printf(" wget -O '%s' ''\n", + prefetch.pngfile, prefetch.pngurl); break; } } @@ -362,7 +370,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { for (Point offset : mapTiles.keySet()) { AltosSiteMapTile tile = mapTiles.get(offset); Point2D.Double ref = translatePoint(pt, tileCoordOffset(offset)); - tile.draw_circle(ref); + tile.set_boost(ref); } } diff --git a/altosui/AltosSiteMapCache.java b/altosuilib/AltosSiteMapCache.java similarity index 54% rename from altosui/AltosSiteMapCache.java rename to altosuilib/AltosSiteMapCache.java index 03dc3cf5..cf93016a 100644 --- a/altosui/AltosSiteMapCache.java +++ b/altosuilib/AltosSiteMapCache.java @@ -15,21 +15,79 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altosuilib_2; import javax.swing.*; import javax.imageio.ImageIO; import java.awt.image.*; +import java.awt.*; import java.io.*; import java.net.URL; import java.net.URLConnection; + +class AltosCacheImage { + Component component; + File file; + VolatileImage image; + int width; + int height; + long used; + + public void load_image() throws IOException { + BufferedImage bimg = ImageIO.read(file); + Graphics2D g = image.createGraphics(); + g.drawImage(bimg, 0, 0, null); + bimg.flush(); + } + + public Image validate() { + int returnCode; + + if (image != null) + returnCode = image.validate(component.getGraphicsConfiguration()); + else + returnCode = VolatileImage.IMAGE_INCOMPATIBLE; + if (returnCode == VolatileImage.IMAGE_RESTORED) { + try { + load_image(); + } catch (IOException e) { + return null; + } + } else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) { + image = component.createVolatileImage(width, height); + try { + load_image(); + } catch (IOException e) { + return null; + } + } + return image; + } + + public void flush() { + image.flush(); + } + + public AltosCacheImage(Component component, File file, int w, int h) throws IOException { + this.component = component; + this.file = file; + width = w; + height = h; + image = component.createVolatileImage(w, h); + used = 0; + } +} + public class AltosSiteMapCache extends JLabel { static final long google_maps_ratelimit_ms = 1200; // Google limits static map queries to 50 per minute per IP, so // each query should take at least 1.2 seconds. public static boolean fetchMap(File file, String url) { + if (file.exists()) + return true; + URL u; long startTime = System.nanoTime(); @@ -88,31 +146,48 @@ public class AltosSiteMapCache extends JLabel { return true; } - public static ImageIcon fetchAndLoadMap(File pngfile, String url) { - if (!pngfile.exists()) { - if (!fetchMap(pngfile, url)) { - return null; - } - } - return loadMap(pngfile, url); + static int cache_size = 9; + + static AltosCacheImage[] images; + + static long used; + + public static void set_cache_size(int cache_size) { + AltosSiteMapCache.cache_size = cache_size; + images = null; } - public static ImageIcon loadMap(File pngfile, String url) { - if (!pngfile.exists()) { - return null; + public static Image get_image(Component component, File file, int width, int height) { + int oldest = -1; + long age = used; + AltosCacheImage image; + if (images == null) + images = new AltosCacheImage[cache_size]; + for (int i = 0; i < cache_size; i++) { + image = images[i]; + + if (image == null) { + oldest = i; + break; + } + if (image.component == component && file.equals(image.file)) { + image.used = used++; + return image.validate(); + } + if (image.used < age) { + oldest = i; + age = image.used; + } } try { - BufferedImage img; - - img = ImageIO.read(pngfile); - if (img == null) { - System.out.printf("# Can't read pngfile %s\n", pngfile); - return null; - } - return new ImageIcon(img); + image = new AltosCacheImage(component, file, width, height); + image.used = used++; + if (images[oldest] != null) + images[oldest].flush(); + images[oldest] = image; + return image.validate(); } catch (IOException e) { - System.out.printf("# IO error trying to load %s\n", pngfile); return null; } } diff --git a/altosui/AltosSiteMapPreload.java b/altosuilib/AltosSiteMapPreload.java similarity index 95% rename from altosui/AltosSiteMapPreload.java rename to altosuilib/AltosSiteMapPreload.java index 8380b967..baa7fc37 100644 --- a/altosui/AltosSiteMapPreload.java +++ b/altosuilib/AltosSiteMapPreload.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altosuilib_2; import java.awt.*; import java.awt.event.*; @@ -26,10 +26,10 @@ import java.text.*; import java.lang.Math; import java.net.URL; import java.net.URLConnection; -import org.altusmetrum.altosuilib_1.*; +import org.altusmetrum.altoslib_4.*; class AltosMapPos extends Box { - AltosUI owner; + AltosUIFrame owner; JLabel label; JComboBox hemi; JTextField deg; @@ -87,14 +87,14 @@ class AltosMapPos extends Box { return v; } - public AltosMapPos(AltosUI in_owner, + public AltosMapPos(AltosUIFrame in_owner, String label_value, String[] hemi_names, double default_value) { super(BoxLayout.X_AXIS); owner = in_owner; label = new JLabel(label_value); - hemi = new JComboBox(hemi_names); + hemi = new JComboBox(hemi_names); hemi.setEditable(false); deg = new JTextField(5); deg.setMinimumSize(deg.getPreferredSize()); @@ -179,7 +179,7 @@ class AltosSites extends Thread { URLConnection uc = url.openConnection(); //int length = uc.getContentLength(); - InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), Altos.unicode_set); + InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set); BufferedReader in = new BufferedReader(in_stream); for (;;) { @@ -198,7 +198,7 @@ class AltosSites extends Thread { sites = new LinkedList(); preload = in_preload; try { - url = new URL(Altos.launch_sites_url); + url = new URL(AltosLib.launch_sites_url); } catch (java.net.MalformedURLException e) { notify_complete(); } @@ -207,7 +207,7 @@ class AltosSites extends Thread { } public class AltosSiteMapPreload extends AltosUIDialog implements ActionListener, ItemListener { - AltosUI owner; + AltosUIFrame owner; AltosSiteMap map; AltosMapPos lat; @@ -221,7 +221,7 @@ public class AltosSiteMapPreload extends AltosUIDialog implements ActionListener AltosSites sites; JLabel site_list_label; - JComboBox site_list; + JComboBox site_list; JToggleButton load_button; boolean loading; @@ -317,7 +317,7 @@ public class AltosSiteMapPreload extends AltosUIDialog implements ActionListener } } - public AltosSiteMapPreload(AltosUI in_owner) { + public AltosSiteMapPreload(AltosUIFrame in_owner) { owner = in_owner; Container pane = getContentPane(); @@ -374,7 +374,7 @@ public class AltosSiteMapPreload extends AltosUIDialog implements ActionListener pane.add(site_list_label, c); - site_list = new JComboBox(new String[] { "Site List" }); + site_list = new JComboBox(new AltosSite[] { new AltosSite("Site List", 0, 0) }); site_list.addItemListener(this); sites = new AltosSites(this); diff --git a/altosuilib/AltosSiteMapTile.java b/altosuilib/AltosSiteMapTile.java new file mode 100644 index 00000000..1046d6bd --- /dev/null +++ b/altosuilib/AltosSiteMapTile.java @@ -0,0 +1,142 @@ +/* + * Copyright © 2010 Anthony Towns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.image.*; +import javax.swing.*; +import javax.imageio.*; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; +import java.io.*; +import java.util.*; +import org.altusmetrum.altoslib_4.*; + +class AltosPoint { + Point2D.Double pt; + int state; + + AltosPoint(Point2D.Double pt, int state) { + this.pt = pt; + this.state = state; + } +} + +public class AltosSiteMapTile extends JComponent { + int px_size; + File file; + + Point2D.Double boost; + Point2D.Double landed; + + LinkedList points; + + public void loadMap(File pngFile) { + file = pngFile; + repaint(); + } + + public void clearMap() { + boost = null; + landed = null; + points = null; + file = null; + } + + static Color stateColors[] = { + Color.WHITE, // startup + Color.WHITE, // idle + Color.WHITE, // pad + Color.RED, // boost + Color.PINK, // fast + Color.YELLOW, // coast + Color.CYAN, // drogue + Color.BLUE, // main + Color.BLACK // landed + }; + + private void draw_circle(Graphics g, Point2D.Double pt) { + g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); + g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); + g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); + } + + public void set_boost(Point2D.Double boost) { + this.boost = boost; + repaint(); + } + + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + AltosPoint prev = null; + Image img = null; + + if (file != null) + img = AltosSiteMapCache.get_image(this, file, px_size, px_size); + + if (img != null) { + g2d.drawImage(img, 0, 0, null); + } else { + g2d.setColor(Color.GRAY); + g2d.fillRect(0, 0, getWidth(), getHeight()); + } + + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + + if (points != null) { + for (AltosPoint point : points) { + if (prev != null) { + if (0 <= point.state && point.state < stateColors.length) + g2d.setColor(stateColors[point.state]); + g2d.draw(new Line2D.Double(prev.pt, point.pt)); + } + prev = point; + } + } + if (boost != null) { + g2d.setColor(Color.RED); + draw_circle(g2d, boost); + } + if (landed != null) { + g2d.setColor(Color.BLACK); + draw_circle(g2d, landed); + } + } + + public synchronized void show(AltosState state, AltosListenerState listener_state, + Point2D.Double last_pt, Point2D.Double pt) + { + if (points == null) + points = new LinkedList(); + + points.add(new AltosPoint(pt, state.state)); + + if (state.state == AltosLib.ao_flight_boost && boost == null) + boost = pt; + if (state.state == AltosLib.ao_flight_landed && landed == null) + landed = pt; + repaint(); + } + + public AltosSiteMapTile(int in_px_size) { + px_size = in_px_size; + setPreferredSize(new Dimension(px_size, px_size)); + } +} diff --git a/altosui/AltosVoice.java b/altosuilib/AltosVoice.java similarity index 97% rename from altosui/AltosVoice.java rename to altosuilib/AltosVoice.java index 2ed6a8c2..a3995f68 100644 --- a/altosui/AltosVoice.java +++ b/altosuilib/AltosVoice.java @@ -15,12 +15,11 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altosuilib_2; import com.sun.speech.freetts.Voice; import com.sun.speech.freetts.VoiceManager; import java.util.concurrent.LinkedBlockingQueue; -import org.altusmetrum.altosuilib_1.*; public class AltosVoice implements Runnable { VoiceManager voice_manager; diff --git a/altosui/GrabNDrag.java b/altosuilib/GrabNDrag.java similarity index 97% rename from altosui/GrabNDrag.java rename to altosuilib/GrabNDrag.java index 2fd6c4da..5e5fdd52 100644 --- a/altosui/GrabNDrag.java +++ b/altosuilib/GrabNDrag.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package altosui; +package org.altusmetrum.altosuilib_2; import java.awt.*; import java.awt.event.*; diff --git a/altosuilib/Makefile.am b/altosuilib/Makefile.am index 4b22af1f..b7d624e2 100644 --- a/altosuilib/Makefile.am +++ b/altosuilib/Makefile.am @@ -1,4 +1,4 @@ -AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -source 6 +AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6 JAVAROOT=bin @@ -9,8 +9,10 @@ SRC=. altosuilibdir = $(datadir)/java altosuilib_JAVA = \ + GrabNDrag.java \ AltosDevice.java \ AltosDeviceDialog.java \ + AltosFlightDisplay.java \ AltosFontListener.java \ AltosPositionListener.java \ AltosUIConfigure.java \ @@ -30,7 +32,14 @@ altosuilib_JAVA = \ AltosUIPreferences.java \ AltosUISeries.java \ AltosUIVersion.java \ - AltosUSBDevice.java + AltosUSBDevice.java \ + AltosSiteMap.java \ + AltosSiteMapCache.java \ + AltosSiteMapPreload.java \ + AltosSiteMapTile.java \ + AltosVoice.java \ + AltosDisplayThread.java \ + AltosFreqList.java JAR=altosuilib_$(ALTOSUILIB_VERSION).jar -- 2.30.2