From 81e7b43ecad666e2e2310c7c94184f888bc86585 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 02:07:41 +1000 Subject: add site map tab, at least for QRS launches --- ao-tools/altosui/AltosFlightUI.java | 6 ++ ao-tools/altosui/AltosSiteMap.java | 137 ++++++++++++++++++++++++++++++++++++ ao-tools/altosui/Makefile.am | 1 + 3 files changed, 144 insertions(+) create mode 100644 ao-tools/altosui/AltosSiteMap.java diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 3581c54c..816ffa23 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -44,6 +44,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { AltosAscent ascent; AltosDescent descent; AltosLanded landed; + AltosSiteMap sitemap; private AltosStatusTable flightStatus; private AltosInfoTable flightInfo; @@ -93,6 +94,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { descent.reset(); landed.reset(); flightInfo.clear(); + sitemap.reset(); } public void show(AltosState state, int crc_errors) { @@ -119,6 +121,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } flightStatus.set(state); flightInfo.show(state, crc_errors); + sitemap.show(state, crc_errors); } public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { @@ -155,6 +158,9 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { flightInfo = new AltosInfoTable(); pane.add("Table", flightInfo.box()); + sitemap = new AltosSiteMap(); + pane.add("Site Map", sitemap); + vbox.add(pane); this.add(vbox); diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java new file mode 100644 index 00000000..22b9aebe --- /dev/null +++ b/ao-tools/altosui/AltosSiteMap.java @@ -0,0 +1,137 @@ +/* + * 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 java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.lang.Math; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +public class AltosSiteMap extends JComponent implements AltosFlightDisplay { + double lat, lng; + int zoom; + double scale_x, scale_y; + Point2D.Double coord_pt; + Point2D.Double last_pt; + + Graphics2D g2d; + + private void setLocation(double new_lat, double new_lng, int new_zoom) { + lat = new_lat; + lng = new_lng; + zoom = new_zoom; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + coord_pt = pt(lat, lng, new Point2D.Double(0,0)); + coord_pt.x = 320-coord_pt.x; + coord_pt.y = 320-coord_pt.y; + last_pt = null; + } + + private static double limit(double v, double lo, double hi) { + if (v < lo) + return lo; + if (hi < v) + return hi; + return v; + } + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private Point2D.Double pt(double lat, double lng) { + return pt(lat, lng, coord_pt); + } + + private Point2D.Double pt(double lat, double lng, Point2D.Double centre) { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = centre.x + lng*scale_x; + e = limit(Math.sin(Math.toRadians(lat)),-(1-1.0E-15),1-1.0E-15); + res.y = centre.y + 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + + public void reset() { + // ? + } + + 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 + }; + + public void show(AltosState state, int crc_errors) { + if (!state.gps_ready) + return; + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + if (last_pt != null && pt != last_pt) { + if (0 <= state.state && state.state < stateColors.length) { + g2d.setColor(stateColors[state.state]); + } + g2d.draw(new Line2D.Double(last_pt, pt)); + } + last_pt = pt; + repaint(); + } + + public AltosSiteMap() { + GridBagLayout layout = new GridBagLayout(); + setLayout(layout); + + GridBagConstraints c = new GridBagConstraints(); + + setLocation(-27.850, 152.960, 15); + String pngfile = "/home/aj/qrs-S27.850,W152.960-15.png"; + + c.gridx = 0; c.gridy = 0; + c.weightx = 1; c.weighty = 1; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + + try { + BufferedImage myPicture = ImageIO.read(new File(pngfile)); + g2d = myPicture.createGraphics(); + JLabel picLabel = new JLabel(new ImageIcon( myPicture )); + JScrollPane scrollPane = new JScrollPane(picLabel); + layout.setConstraints(scrollPane, c); + add(scrollPane); + } catch (Exception e) { + throw new RuntimeException(e); + }; + } +} + diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 267bae63..fc532863 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -59,6 +59,7 @@ altosui_JAVA = \ AltosRomconfigUI.java \ AltosSerial.java \ AltosSerialMonitor.java \ + AltosSiteMap.java \ AltosState.java \ AltosStatusTable.java \ AltosTelemetry.java \ -- cgit v1.2.3 From 0327c1da01a3f6ede01f05c1d775651a57fd0c68 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 02:08:58 +1000 Subject: tabs -> spaces --- ao-tools/altosui/AltosSiteMap.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 22b9aebe..6f33aabd 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -78,9 +78,9 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { } - public void reset() { - // ? - } + public void reset() { + // ? + } static Color stateColors[] = { Color.WHITE, // startup @@ -94,7 +94,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Color.BLACK // landed }; - public void show(AltosState state, int crc_errors) { + public void show(AltosState state, int crc_errors) { if (!state.gps_ready) return; Point2D.Double pt = pt(state.gps.lat, state.gps.lon); @@ -106,7 +106,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { } last_pt = pt; repaint(); - } + } public AltosSiteMap() { GridBagLayout layout = new GridBagLayout(); -- cgit v1.2.3 From beb6c881ec006241c7d2820c64e5381131d41180 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 03:24:26 +1000 Subject: make infotable scrollable, revert its fontsize to 14 --- ao-tools/altosui/AltosFlightUI.java | 2 +- ao-tools/altosui/AltosInfoTable.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 816ffa23..f56b3d1b 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -156,7 +156,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pane.add("Landed", landed); flightInfo = new AltosInfoTable(); - pane.add("Table", flightInfo.box()); + pane.add("Table", new JScrollPane(flightInfo.box())); sitemap = new AltosSiteMap(); pane.add("Site Map", sitemap); diff --git a/ao-tools/altosui/AltosInfoTable.java b/ao-tools/altosui/AltosInfoTable.java index 2f326e07..9964ab10 100644 --- a/ao-tools/altosui/AltosInfoTable.java +++ b/ao-tools/altosui/AltosInfoTable.java @@ -37,8 +37,8 @@ public class AltosInfoTable { private AltosFlightInfoTableModel model[]; private Box ibox[]; - private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 12); - private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 12); + private Font infoLabelFont = new Font("SansSerif", Font.PLAIN, 14); + private Font infoValueFont = new Font("Monospaced", Font.PLAIN, 14); static final int info_columns = 3; static final int info_rows = 17; -- cgit v1.2.3 From 1bcfa22de7821984149db10cb79913efed36b41e Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 23:29:40 +1000 Subject: pull up maps for arbitrary locations --- ao-tools/altosui/AltosSiteMap.java | 54 +++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 6f33aabd..420bfc81 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -40,7 +40,8 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Graphics2D g2d; - private void setLocation(double new_lat, double new_lng, int new_zoom) { + private void setLocation(double new_lat, double new_lng) { + int new_zoom = 15; lat = new_lat; lng = new_lng; zoom = new_zoom; @@ -50,6 +51,17 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { coord_pt.x = 320-coord_pt.x; coord_pt.y = 320-coord_pt.y; last_pt = null; + + try { + File pngfile = new File(AltosPreferences.logdir(), + FileCoord(lat, lng, zoom)); + System.out.printf("Trying file %s\n", pngfile); + BufferedImage myPicture = ImageIO.read(pngfile); + picLabel.setIcon(new ImageIcon( myPicture )); + g2d = myPicture.createGraphics(); + } catch (Exception e) { + throw new RuntimeException(e); + }; } private static double limit(double v, double lo, double hi) { @@ -60,6 +72,16 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { return v; } + private static String FileCoord(double lat, double lng, int zoom) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return String.format("map-%c%.3f,%c%.3f-%d.png", + chlat, lat, chlng, lng, zoom); + } + + // based on google js // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js // search for fromLatLngToPoint and fromPointToLatLng @@ -95,8 +117,15 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { }; public void show(AltosState state, int crc_errors) { - if (!state.gps_ready) + if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) return; + double plat = (int)(state.pad_lat*200)/200.0; + double plon = (int)(state.pad_lon*200)/200.0; + + if (last_pt == null) { + setLocation(plat, plon); + } + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); if (last_pt != null && pt != last_pt) { if (0 <= state.state && state.state < stateColors.length) { @@ -107,31 +136,24 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { last_pt = pt; repaint(); } - + + JLabel picLabel = new JLabel(); + public AltosSiteMap() { GridBagLayout layout = new GridBagLayout(); setLayout(layout); GridBagConstraints c = new GridBagConstraints(); - setLocation(-27.850, 152.960, 15); - String pngfile = "/home/aj/qrs-S27.850,W152.960-15.png"; - c.gridx = 0; c.gridy = 0; c.weightx = 1; c.weighty = 1; c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; + picLabel = new JLabel(); + JScrollPane scrollPane = new JScrollPane(picLabel); + layout.setConstraints(scrollPane, c); + add(scrollPane); - try { - BufferedImage myPicture = ImageIO.read(new File(pngfile)); - g2d = myPicture.createGraphics(); - JLabel picLabel = new JLabel(new ImageIcon( myPicture )); - JScrollPane scrollPane = new JScrollPane(picLabel); - layout.setConstraints(scrollPane, c); - add(scrollPane); - } catch (Exception e) { - throw new RuntimeException(e); - }; } } -- cgit v1.2.3 From 991541f57f065f429c6ec425efd6ac731280b2c1 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2010 23:42:42 +1000 Subject: better error behaviour if no map --- ao-tools/altosui/AltosSiteMap.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 420bfc81..1fb70b35 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -40,7 +40,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Graphics2D g2d; - private void setLocation(double new_lat, double new_lng) { + private boolean setLocation(double new_lat, double new_lng) { int new_zoom = 15; lat = new_lat; lng = new_lng; @@ -60,8 +60,10 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { picLabel.setIcon(new ImageIcon( myPicture )); g2d = myPicture.createGraphics(); } catch (Exception e) { - throw new RuntimeException(e); - }; + // throw new RuntimeException(e); + return false; + } + return true; } private static double limit(double v, double lo, double hi) { @@ -116,14 +118,20 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Color.BLACK // landed }; + boolean nomaps = false; public void show(AltosState state, int crc_errors) { + if (nomaps) + return; if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) return; double plat = (int)(state.pad_lat*200)/200.0; double plon = (int)(state.pad_lon*200)/200.0; if (last_pt == null) { - setLocation(plat, plon); + if (!setLocation(plat, plon)) { + nomaps = true; + return; + } } Point2D.Double pt = pt(state.gps.lat, state.gps.lon); -- cgit v1.2.3 From 1e7e02987276847274493312202d22222c953149 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 14 Nov 2010 00:57:45 +1000 Subject: AltosTelemetryReader: actually open serial port --- ao-tools/altosui/AltosTelemetryReader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java index 0b5509eb..03e694f8 100644 --- a/ao-tools/altosui/AltosTelemetryReader.java +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -58,5 +58,6 @@ class AltosTelemetryReader extends AltosFlightReader { telem = new LinkedBlockingQueue(); serial.add_monitor(telem); + serial.open(device); } } -- cgit v1.2.3 From e68fe9454352087889c560d95797922493117acb Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 14 Nov 2010 00:59:01 +1000 Subject: AltosSiteMap: add targeting circles around landing site --- ao-tools/altosui/AltosSiteMap.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 1fb70b35..25b77792 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -118,6 +118,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { Color.BLACK // landed }; + boolean drawn_landed_circle = false; boolean nomaps = false; public void show(AltosState state, int crc_errors) { if (nomaps) @@ -141,6 +142,15 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { } g2d.draw(new Line2D.Double(last_pt, pt)); } + + if (state.state == 8 && !drawn_landed_circle) { + drawn_landed_circle = true; + 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); + } + last_pt = pt; repaint(); } -- cgit v1.2.3 From 483346a03c94b200692f5e6d59f3feee4dcf2ace Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 19 Nov 2010 12:09:46 +1000 Subject: altosui: tile site maps --- ao-tools/altosui/AltosFlightUI.java | 4 +- ao-tools/altosui/AltosSiteMap.java | 138 +++--------------------- ao-tools/altosui/AltosSiteMapTile.java | 190 +++++++++++++++++++++++++++++++++ ao-tools/altosui/Makefile.am | 1 + 4 files changed, 206 insertions(+), 127 deletions(-) create mode 100644 ao-tools/altosui/AltosSiteMapTile.java diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 1372cc00..658d6f6f 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -48,6 +48,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { private AltosFlightStatus flightStatus; private JScrollPane flightInfoPane; + private JScrollPane sitemapPane; private AltosInfoTable flightInfo; static final int tab_pad = 1; @@ -167,7 +168,8 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pane.add("Table", flightInfoPane); sitemap = new AltosSiteMap(); - pane.add("Site Map", sitemap); + sitemapPane = new JScrollPane(sitemap); + pane.add("Site Map", sitemapPane); vbox.add(pane); diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 25b77792..df1cc246 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -32,146 +32,32 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMap extends JComponent implements AltosFlightDisplay { - double lat, lng; - int zoom; - double scale_x, scale_y; - Point2D.Double coord_pt; - Point2D.Double last_pt; - - Graphics2D g2d; - - private boolean setLocation(double new_lat, double new_lng) { - int new_zoom = 15; - lat = new_lat; - lng = new_lng; - zoom = new_zoom; - scale_x = 256/360.0 * Math.pow(2, zoom); - scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - coord_pt = pt(lat, lng, new Point2D.Double(0,0)); - coord_pt.x = 320-coord_pt.x; - coord_pt.y = 320-coord_pt.y; - last_pt = null; - - try { - File pngfile = new File(AltosPreferences.logdir(), - FileCoord(lat, lng, zoom)); - System.out.printf("Trying file %s\n", pngfile); - BufferedImage myPicture = ImageIO.read(pngfile); - picLabel.setIcon(new ImageIcon( myPicture )); - g2d = myPicture.createGraphics(); - } catch (Exception e) { - // throw new RuntimeException(e); - return false; - } - return true; - } - - private static double limit(double v, double lo, double hi) { - if (v < lo) - return lo; - if (hi < v) - return hi; - return v; - } - - private static String FileCoord(double lat, double lng, int zoom) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return String.format("map-%c%.3f,%c%.3f-%d.png", - chlat, lat, chlng, lng, zoom); - } - - - // based on google js - // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js - // search for fromLatLngToPoint and fromPointToLatLng - private Point2D.Double pt(double lat, double lng) { - return pt(lat, lng, coord_pt); - } - - private Point2D.Double pt(double lat, double lng, Point2D.Double centre) { - Point2D.Double res = new Point2D.Double(); - double e; - - res.x = centre.x + lng*scale_x; - e = limit(Math.sin(Math.toRadians(lat)),-(1-1.0E-15),1-1.0E-15); - res.y = centre.y + 0.5*Math.log((1+e)/(1-e))*-scale_y; - return res; - } - - public void reset() { - // ? + // nothing } - - 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 - }; - - boolean drawn_landed_circle = false; - boolean nomaps = false; public void show(AltosState state, int crc_errors) { - if (nomaps) - return; - if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) - return; - double plat = (int)(state.pad_lat*200)/200.0; - double plon = (int)(state.pad_lon*200)/200.0; - - if (last_pt == null) { - if (!setLocation(plat, plon)) { - nomaps = true; - return; - } - } - - Point2D.Double pt = pt(state.gps.lat, state.gps.lon); - if (last_pt != null && pt != last_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 == 8 && !drawn_landed_circle) { - drawn_landed_circle = true; - 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); + for (int x = 0; x < mapTiles.length; x++) { + mapTiles[x].show(state, crc_errors); } - - last_pt = pt; - repaint(); } - - JLabel picLabel = new JLabel(); + + AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; public AltosSiteMap() { + GridBagLayout layout = new GridBagLayout(); setLayout(layout); GridBagConstraints c = new GridBagConstraints(); - - c.gridx = 0; c.gridy = 0; - c.weightx = 1; c.weighty = 1; c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; - picLabel = new JLabel(); - JScrollPane scrollPane = new JScrollPane(picLabel); - layout.setConstraints(scrollPane, c); - add(scrollPane); + for (int x = 0; x < 9; x++) { + c.gridx = x % 3; c.gridy = x / 3; + mapTiles[x] = new AltosSiteMapTile((x%3)-1, (x/3)-1); + layout.setConstraints(mapTiles[x], c); + add(mapTiles[x]); + } } } diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java new file mode 100644 index 00000000..d84941ae --- /dev/null +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -0,0 +1,190 @@ +/* + * 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 java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.lang.Math; +import java.awt.geom.Point2D; +import java.awt.geom.Line2D; + +public class AltosSiteMapTile extends JLabel { + double lat, lng; + int zoom; + double scale_x, scale_y; + Point2D.Double coord_pt; + Point2D.Double last_pt; + + Graphics2D g2d; + + int off_x; + int off_y; + + int px_size = 512; + + private boolean setLocation(double new_lat, double new_lng) { + int new_zoom = 16; + lat = new_lat; + lng = new_lng; + zoom = new_zoom; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + coord_pt = pt(lat, lng, new Point2D.Double(0,0)); + coord_pt.x = px_size/2-coord_pt.x - off_x * px_size; + coord_pt.y = px_size/2-coord_pt.y - off_y * px_size; + last_pt = null; + + Point2D.Double map_latlng; + map_latlng = latlng(new Point2D.Double(px_size/2, px_size/2)); + + BufferedImage myPicture; + File pngfile = new File(AltosPreferences.logdir(), + FileCoord(map_latlng, zoom)); + try { + myPicture = ImageIO.read(pngfile); + System.out.printf("# Found file %s\n", pngfile); + } catch (Exception e) { + // throw new RuntimeException(e); + System.out.printf("# Failed to find file %s\n", pngfile); + System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); + myPicture = new BufferedImage(px_size, px_size, + BufferedImage.TYPE_INT_RGB); + } + setIcon(new ImageIcon( myPicture )); + g2d = myPicture.createGraphics(); + return true; + } + + private static double limit(double v, double lo, double hi) { + if (v < lo) + return lo; + if (hi < v) + return hi; + return v; + } + + private static String FileCoord(Point2D.Double latlng, int zoom) { + double lat, lng; + lat = latlng.x; + lng = latlng.y; + return FileCoord(lat, lng, zoom); + } + private static String FileCoord(double lat, double lng, int zoom) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return String.format("map-%c%.3f,%c%.3f-%d.png", + chlat, lat, chlng, lng, zoom); + } + + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private Point2D.Double pt(double lat, double lng) { + return pt(lat, lng, coord_pt); + } + + private Point2D.Double pt(double lat, double lng, Point2D.Double centre) { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = centre.x + lng*scale_x; + e = limit(Math.sin(Math.toRadians(lat)),-(1-1.0E-15),1-1.0E-15); + res.y = centre.y + 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + private Point2D.Double latlng(Point2D.Double pt) { + return latlng(pt, coord_pt); + } + private Point2D.Double latlng(Point2D.Double pt, Point2D.Double centre) { + double lat, lng; + double rads; + + lng = (pt.x - centre.x)/scale_x; + rads = 2 * Math.atan(Math.exp((pt.y-centre.y)/-scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new Point2D.Double(lat,lng); + } + + 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 + }; + + boolean drawn_landed_circle = false; + boolean nomaps = false; + public void show(AltosState state, int crc_errors) { + if (nomaps) + return; + if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) + return; + double plat = (int)(state.pad_lat*200)/200.0; + double plon = (int)(state.pad_lon*200)/200.0; + + if (last_pt == null) { + if (!setLocation(plat, plon)) { + nomaps = true; + return; + } + } + + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + if (last_pt != null && pt != last_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 == 8 && !drawn_landed_circle) { + drawn_landed_circle = true; + 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); + } + + last_pt = pt; + repaint(); + } + + public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { + off_x = x_tile_offset; + off_y = y_tile_offset; + } +} + diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 1c24ce13..b6b2e572 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -62,6 +62,7 @@ altosui_JAVA = \ AltosSerialInUseException.java \ AltosSerialMonitor.java \ AltosSiteMap.java \ + AltosSiteMapTile.java \ AltosState.java \ AltosTelemetry.java \ AltosTelemetryIterable.java \ -- cgit v1.2.3 From fda93afcd8aa4133b0e5f008b824d072e338d0ed Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 19 Nov 2010 13:02:05 +1000 Subject: AltosSiteMapTile: autoscale to about 2 nmi per tile --- ao-tools/altosui/AltosSiteMapTile.java | 37 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index d84941ae..919de825 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -32,7 +32,6 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMapTile extends JLabel { - double lat, lng; int zoom; double scale_x, scale_y; Point2D.Double coord_pt; @@ -45,16 +44,20 @@ public class AltosSiteMapTile extends JLabel { int px_size = 512; - private boolean setLocation(double new_lat, double new_lng) { - int new_zoom = 16; - lat = new_lat; - lng = new_lng; - zoom = new_zoom; + private boolean setLocation(double lat, double lng) { + Point2D.Double north_1nm; + for (zoom = 3; zoom < 22; zoom++) { + coord_pt = pt(lat, lng, new Point2D.Double(0,0), zoom); + north_1nm = pt(lat+1/60.0, lng, new Point2D.Double(0,0), zoom); + if (coord_pt.y - north_1nm.y > px_size/2) + break; + } + coord_pt.x = px_size/2 - ((long)coord_pt.x/px_size + off_x) * px_size; + coord_pt.y = px_size/2 - ((long)coord_pt.y/px_size + off_y) * px_size; + scale_x = 256/360.0 * Math.pow(2, zoom); scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - coord_pt = pt(lat, lng, new Point2D.Double(0,0)); - coord_pt.x = px_size/2-coord_pt.x - off_x * px_size; - coord_pt.y = px_size/2-coord_pt.y - off_y * px_size; + last_pt = null; Point2D.Double map_latlng; @@ -97,7 +100,7 @@ public class AltosSiteMapTile extends JLabel { char chlng = lng < 0 ? 'E' : 'W'; if (lat < 0) lat = -lat; if (lng < 0) lng = -lng; - return String.format("map-%c%.3f,%c%.3f-%d.png", + return String.format("map-%c%.6f,%c%.6f-%d.png", chlat, lat, chlng, lng, zoom); } @@ -106,10 +109,20 @@ public class AltosSiteMapTile extends JLabel { // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js // search for fromLatLngToPoint and fromPointToLatLng private Point2D.Double pt(double lat, double lng) { - return pt(lat, lng, coord_pt); + return pt(lat, lng, coord_pt, scale_x, scale_y); + } + + private static Point2D.Double pt(double lat, double lng, + Point2D.Double centre, int zoom) + { + double scale_x = 256/360.0 * Math.pow(2, zoom); + double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + return pt(lat, lng, centre, scale_x, scale_y); } - private Point2D.Double pt(double lat, double lng, Point2D.Double centre) { + private static Point2D.Double pt(double lat, double lng, + Point2D.Double centre, double scale_x, double scale_y) + { Point2D.Double res = new Point2D.Double(); double e; -- cgit v1.2.3 From fa45336062523838ba8abb08427cdc4d9c7de7a8 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 19 Nov 2010 13:17:29 +1000 Subject: AltosSiteMapTile: adjust centering calculation --- ao-tools/altosui/AltosSiteMapTile.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 919de825..6f5ddede 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -52,8 +52,8 @@ public class AltosSiteMapTile extends JLabel { if (coord_pt.y - north_1nm.y > px_size/2) break; } - coord_pt.x = px_size/2 - ((long)coord_pt.x/px_size + off_x) * px_size; - coord_pt.y = px_size/2 - ((long)coord_pt.y/px_size + off_y) * px_size; + coord_pt.x = -px_size * Math.floor(coord_pt.x/px_size + off_x); + coord_pt.y = -px_size * Math.floor(coord_pt.y/px_size + off_y); scale_x = 256/360.0 * Math.pow(2, zoom); scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); -- cgit v1.2.3 From 90b9bc4475011bead7117ed72fa5efa0f77b2813 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 19 Nov 2010 13:30:00 +1000 Subject: AltosSiteMapTile: adjust scale to 1 nmi per tile --- ao-tools/altosui/AltosSiteMapTile.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 6f5ddede..c14fb93f 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -45,11 +45,13 @@ public class AltosSiteMapTile extends JLabel { int px_size = 512; private boolean setLocation(double lat, double lng) { - Point2D.Double north_1nm; + Point2D.Double north_step; + double step_nm = 0.5; for (zoom = 3; zoom < 22; zoom++) { coord_pt = pt(lat, lng, new Point2D.Double(0,0), zoom); - north_1nm = pt(lat+1/60.0, lng, new Point2D.Double(0,0), zoom); - if (coord_pt.y - north_1nm.y > px_size/2) + north_step = pt(lat+step_nm/60.0, lng, + new Point2D.Double(0,0), zoom); + if (coord_pt.y - north_step.y > px_size/2) break; } coord_pt.x = -px_size * Math.floor(coord_pt.x/px_size + off_x); -- cgit v1.2.3 From 58f8d069ce9488e2987b8e92caa69fe68cda7569 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sat, 20 Nov 2010 21:06:37 +1000 Subject: AltosSiteMap: add autoscroll and grabndrag scroll --- ao-tools/altosui/AltosFlightUI.java | 4 +--- ao-tools/altosui/AltosSiteMap.java | 38 +++++++++++++++++++++++++++++++--- ao-tools/altosui/AltosSiteMapTile.java | 32 +++++++++++++++++++++------- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 6db6c67b..c85fc977 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -46,7 +46,6 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { private AltosFlightStatus flightStatus; private JScrollPane flightInfoPane; - private JScrollPane sitemapPane; private AltosInfoTable flightInfo; static final int tab_pad = 1; @@ -192,8 +191,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { pane.add("Table", flightInfoPane); sitemap = new AltosSiteMap(); - sitemapPane = new JScrollPane(sitemap); - pane.add("Site Map", sitemapPane); + pane.add("Site Map", sitemap); c.gridx = 0; c.gridy = 2; diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index df1cc246..1db83959 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -21,6 +21,7 @@ import java.awt.*; import java.awt.image.*; import java.awt.event.*; import javax.swing.*; +import javax.swing.event.MouseInputAdapter; import javax.imageio.ImageIO; import javax.swing.table.*; import java.io.*; @@ -31,7 +32,7 @@ import java.lang.Math; import java.awt.geom.Point2D; import java.awt.geom.Line2D; -public class AltosSiteMap extends JComponent implements AltosFlightDisplay { +public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { public void reset() { // nothing } @@ -43,10 +44,40 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; + class GrabNDrag extends MouseInputAdapter { + private JComponent scroll; + private Point startPt = new Point(); + + public GrabNDrag(JComponent parent) { + scroll = parent; + } + + public void mousePressed(MouseEvent e) { + startPt.setLocation(e.getPoint()); + } + public void mouseDragged(MouseEvent e) { + int xd = e.getX() - startPt.x; + int yd = e.getY() - startPt.y; + + Rectangle r = scroll.getVisibleRect(); + r.x -= xd; + r.y -= yd; + scroll.scrollRectToVisible(r); + } + } + public AltosSiteMap() { + JComponent comp = new JComponent() { + GrabNDrag scroller = new GrabNDrag(this); + { + addMouseMotionListener(scroller); + addMouseListener(scroller); + setAutoscrolls(true); + } + }; GridBagLayout layout = new GridBagLayout(); - setLayout(layout); + comp.setLayout(layout); GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.CENTER; @@ -56,8 +87,9 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay { c.gridx = x % 3; c.gridy = x / 3; mapTiles[x] = new AltosSiteMapTile((x%3)-1, (x/3)-1); layout.setConstraints(mapTiles[x], c); - add(mapTiles[x]); + comp.add(mapTiles[x]); } + setViewportView(comp); } } diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index c14fb93f..df57aa7d 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -65,21 +65,19 @@ public class AltosSiteMapTile extends JLabel { Point2D.Double map_latlng; map_latlng = latlng(new Point2D.Double(px_size/2, px_size/2)); - BufferedImage myPicture; File pngfile = new File(AltosPreferences.logdir(), FileCoord(map_latlng, zoom)); try { + BufferedImage myPicture; myPicture = ImageIO.read(pngfile); + setIcon(new ImageIcon( myPicture )); System.out.printf("# Found file %s\n", pngfile); + g2d = myPicture.createGraphics(); } catch (Exception e) { // throw new RuntimeException(e); System.out.printf("# Failed to find file %s\n", pngfile); System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); - myPicture = new BufferedImage(px_size, px_size, - BufferedImage.TYPE_INT_RGB); } - setIcon(new ImageIcon( myPicture )); - g2d = myPicture.createGraphics(); return true; } @@ -167,8 +165,8 @@ public class AltosSiteMapTile extends JLabel { return; if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) return; - double plat = (int)(state.pad_lat*200)/200.0; - double plon = (int)(state.pad_lon*200)/200.0; + double plat = state.pad_lat; + double plon = state.pad_lon; if (last_pt == null) { if (!setLocation(plat, plon)) { @@ -185,6 +183,19 @@ public class AltosSiteMapTile extends JLabel { g2d.draw(new Line2D.Double(last_pt, pt)); } + if (0 <= pt.x && pt.x < px_size) { + if (0 <= pt.y && pt.y < px_size) { + int dx = 500, dy = 250; + if (last_pt != null && state.state > 2) { + dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); + dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); + } + Rectangle r = new Rectangle((int)pt.x-dx, (int)pt.y-dy, + dx*2, dy*2); + scrollRectToVisible(r); + } + } + if (state.state == 8 && !drawn_landed_circle) { drawn_landed_circle = true; g2d.setColor(Color.RED); @@ -198,6 +209,13 @@ public class AltosSiteMapTile extends JLabel { } public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { + BufferedImage myPicture = new BufferedImage(px_size, px_size, + BufferedImage.TYPE_INT_RGB); + setIcon(new ImageIcon( myPicture )); + g2d = myPicture.createGraphics(); + g2d.setColor(Color.GRAY); + g2d.fillRect(0, 0, px_size, px_size); + off_x = x_tile_offset; off_y = y_tile_offset; } -- cgit v1.2.3 From 20f714bbe3137de8fb7491b39985021fd1774930 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sat, 20 Nov 2010 22:49:51 +1000 Subject: AltosSiteMapTile: seperate map and drawing layers --- ao-tools/altosui/AltosSiteMapTile.java | 94 ++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index df57aa7d..ca68412a 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -31,18 +31,33 @@ import java.lang.Math; import java.awt.geom.Point2D; import java.awt.geom.Line2D; -public class AltosSiteMapTile extends JLabel { +public class AltosSiteMapTile extends JLayeredPane { int zoom; double scale_x, scale_y; Point2D.Double coord_pt; Point2D.Double last_pt; + JLabel mapLabel; + JLabel draw; Graphics2D g2d; int off_x; int off_y; - int px_size = 512; + static final int px_size = 512; + + private void loadMap() { + Point2D.Double map_latlng = latlng(px_size/2, px_size/2); + File pngfile = new File(AltosPreferences.logdir(), + FileCoord(map_latlng, zoom)); + try { + mapLabel.setIcon(new ImageIcon(ImageIO.read(pngfile))); + } catch (Exception e) { + // throw new RuntimeException(e); + System.out.printf("# Failed to find file %s\n", pngfile); + System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); + } + } private boolean setLocation(double lat, double lng) { Point2D.Double north_step; @@ -62,22 +77,6 @@ public class AltosSiteMapTile extends JLabel { last_pt = null; - Point2D.Double map_latlng; - map_latlng = latlng(new Point2D.Double(px_size/2, px_size/2)); - - File pngfile = new File(AltosPreferences.logdir(), - FileCoord(map_latlng, zoom)); - try { - BufferedImage myPicture; - myPicture = ImageIO.read(pngfile); - setIcon(new ImageIcon( myPicture )); - System.out.printf("# Found file %s\n", pngfile); - g2d = myPicture.createGraphics(); - } catch (Exception e) { - // throw new RuntimeException(e); - System.out.printf("# Failed to find file %s\n", pngfile); - System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); - } return true; } @@ -132,6 +131,9 @@ public class AltosSiteMapTile extends JLabel { return res; } + private Point2D.Double latlng(double x, double y) { + return latlng(new Point2D.Double(x,y), coord_pt); + } private Point2D.Double latlng(Point2D.Double pt) { return latlng(pt, coord_pt); } @@ -159,24 +161,22 @@ public class AltosSiteMapTile extends JLabel { }; boolean drawn_landed_circle = false; - boolean nomaps = false; public void show(AltosState state, int crc_errors) { - if (nomaps) - return; - if (!state.gps_ready && state.pad_lat == 0 && state.pad_lon == 0) - return; - double plat = state.pad_lat; - double plon = state.pad_lon; + if (!state.gps_ready) { + if (state.pad_lat == 0 && state.pad_lon == 0) + return; + if (state.ngps < 3) + return; + } if (last_pt == null) { - if (!setLocation(plat, plon)) { - nomaps = true; - return; - } + setLocation(state.pad_lat, state.pad_lon); + loadMap(); + last_pt = pt; } Point2D.Double pt = pt(state.gps.lat, state.gps.lon); - if (last_pt != null && pt != last_pt) { + if (pt != last_pt) { if (0 <= state.state && state.state < stateColors.length) { g2d.setColor(stateColors[state.state]); } @@ -186,7 +186,7 @@ public class AltosSiteMapTile extends JLabel { if (0 <= pt.x && pt.x < px_size) { if (0 <= pt.y && pt.y < px_size) { int dx = 500, dy = 250; - if (last_pt != null && state.state > 2) { + if (state.state > 2) { dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); } @@ -207,14 +207,32 @@ public class AltosSiteMapTile extends JLabel { last_pt = pt; repaint(); } - + + public static Graphics2D fillLabel(JLabel l, Color c) { + 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 x_tile_offset, int y_tile_offset) { - BufferedImage myPicture = new BufferedImage(px_size, px_size, - BufferedImage.TYPE_INT_RGB); - setIcon(new ImageIcon( myPicture )); - g2d = myPicture.createGraphics(); - g2d.setColor(Color.GRAY); - g2d.fillRect(0, 0, px_size, px_size); + setPreferredSize(new Dimension(px_size, px_size)); + + mapLabel = new JLabel(); + fillLabel(mapLabel, Color.GRAY); + 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)); + draw.setBounds(0, 0, px_size, px_size); + draw.setOpaque(false); + + add(draw, new Integer(1)); off_x = x_tile_offset; off_y = y_tile_offset; -- cgit v1.2.3 From 25ffe1cc7823895886b4777f310b4bda1c80133b Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 00:07:16 +1000 Subject: AltosSiteMap: automatic fetching of map data --- ao-tools/altosui/AltosSiteMapLabel.java | 142 ++++++++++++++++++++++++++++++++ ao-tools/altosui/AltosSiteMapTile.java | 32 +------ ao-tools/altosui/Makefile.am | 1 + 3 files changed, 147 insertions(+), 28 deletions(-) create mode 100644 ao-tools/altosui/AltosSiteMapLabel.java diff --git a/ao-tools/altosui/AltosSiteMapLabel.java b/ao-tools/altosui/AltosSiteMapLabel.java new file mode 100644 index 00000000..1a371c5b --- /dev/null +++ b/ao-tools/altosui/AltosSiteMapLabel.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 altosui; + +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.net.URL; +import java.net.URLConnection; + +public class AltosSiteMapLabel extends JLabel { + public static boolean fetchMap(File file, String url) { + URL u; + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return false; + } + + byte[] data; + try { + URLConnection uc = u.openConnection(); + int contentLength = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) { + return false; + } + } catch (IOException e) { + return false; + } + + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + if (file.exists()) { + file.delete(); + } + return false; + } + return true; + } + + public void fetchAndLoadMap(final File pngfile, final String url) { + System.out.printf("# Trying to fetch %s...\n", pngfile); + + Thread thread = new Thread() { + public void run() { + try { + if (fetchMap(pngfile, url)) { + setIcon(new ImageIcon(ImageIO.read(pngfile))); + } + } catch (Exception e) { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, url); + } + } + }; + thread.start(); + } + + public void fetchMap(double lat, double lng, int zoom, int px_size) { + File pngfile = MapFile(lat, lng, zoom); + String url = MapURL(lat, lng, zoom, px_size); + + if (!pngfile.exists()) { + fetchMap(pngfile, url); + } + } + + public void loadMap(double lat, double lng, int zoom, int px_size) { + File pngfile = MapFile(lat, lng, zoom); + String url = MapURL(lat, lng, zoom, px_size); + + if (!pngfile.exists()) { + fetchAndLoadMap(pngfile, url); + return; + } + + try { + setIcon(new ImageIcon(ImageIO.read(pngfile))); + return; + } catch (IOException e) { + System.out.printf("# IO error trying to load %s\n", pngfile); + return; + } + } + + private static File MapFile(double lat, double lng, int zoom) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return new File(AltosPreferences.logdir(), + String.format("map-%c%.6f,%c%.6f-%d.png", + chlat, lat, chlng, lng, zoom)); + } + + private static String MapURL(double lat, double lng, + int zoom, int px_size) + { + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); + } +} + diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index ca68412a..56bc98af 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -37,7 +37,7 @@ public class AltosSiteMapTile extends JLayeredPane { Point2D.Double coord_pt; Point2D.Double last_pt; - JLabel mapLabel; + AltosSiteMapLabel mapLabel; JLabel draw; Graphics2D g2d; @@ -48,15 +48,7 @@ public class AltosSiteMapTile extends JLayeredPane { private void loadMap() { Point2D.Double map_latlng = latlng(px_size/2, px_size/2); - File pngfile = new File(AltosPreferences.logdir(), - FileCoord(map_latlng, zoom)); - try { - mapLabel.setIcon(new ImageIcon(ImageIO.read(pngfile))); - } catch (Exception e) { - // throw new RuntimeException(e); - System.out.printf("# Failed to find file %s\n", pngfile); - System.out.printf(" wget -O '%s' 'http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32'\n", pngfile, map_latlng.x, map_latlng.y, zoom, px_size, px_size); - } + mapLabel.loadMap(map_latlng.x, map_latlng.y, zoom, px_size); } private boolean setLocation(double lat, double lng) { @@ -88,22 +80,6 @@ public class AltosSiteMapTile extends JLayeredPane { return v; } - private static String FileCoord(Point2D.Double latlng, int zoom) { - double lat, lng; - lat = latlng.x; - lng = latlng.y; - return FileCoord(lat, lng, zoom); - } - private static String FileCoord(double lat, double lng, int zoom) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return String.format("map-%c%.6f,%c%.6f-%d.png", - chlat, lat, chlng, lng, zoom); - } - - // based on google js // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js // search for fromLatLngToPoint and fromPointToLatLng @@ -172,7 +148,7 @@ public class AltosSiteMapTile extends JLayeredPane { if (last_pt == null) { setLocation(state.pad_lat, state.pad_lon); loadMap(); - last_pt = pt; + last_pt = pt(state.pad_lat, state.pad_lon); } Point2D.Double pt = pt(state.gps.lat, state.gps.lon); @@ -221,7 +197,7 @@ public class AltosSiteMapTile extends JLayeredPane { public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { setPreferredSize(new Dimension(px_size, px_size)); - mapLabel = new JLabel(); + mapLabel = new AltosSiteMapLabel(); fillLabel(mapLabel, Color.GRAY); mapLabel.setOpaque(true); mapLabel.setBounds(0, 0, px_size, px_size); diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 41afdf27..334608f6 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -63,6 +63,7 @@ altosui_JAVA = \ AltosSerialInUseException.java \ AltosSerialMonitor.java \ AltosSiteMap.java \ + AltosSiteMapLabel.java \ AltosSiteMapTile.java \ AltosState.java \ AltosTelemetry.java \ -- cgit v1.2.3 From 51e403145d28ac913e36d205077a613845596be2 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 00:17:51 +1000 Subject: AltosSiteMapTile: draw boost circle as well as landed --- ao-tools/altosui/AltosSiteMapTile.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 56bc98af..6035a794 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -137,6 +137,7 @@ public class AltosSiteMapTile extends JLayeredPane { }; boolean drawn_landed_circle = false; + boolean drawn_boost_circle = false; public void show(AltosState state, int crc_errors) { if (!state.gps_ready) { if (state.pad_lat == 0 && state.pad_lon == 0) @@ -172,9 +173,16 @@ public class AltosSiteMapTile extends JLayeredPane { } } + 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.RED); + 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); -- cgit v1.2.3 From 72f5e05f9f0055f2cef8b840812f090556c94338 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 08:18:39 +1000 Subject: AltosSiteMap: major refactoring --- ao-tools/altosui/AltosSiteMap.java | 214 ++++++++++++++++++++++++++++---- ao-tools/altosui/AltosSiteMapCache.java | 103 +++++++++++++++ ao-tools/altosui/AltosSiteMapLabel.java | 142 --------------------- ao-tools/altosui/AltosSiteMapTile.java | 117 ++--------------- ao-tools/altosui/AltosUI.java | 6 +- ao-tools/altosui/Makefile.am | 3 +- 6 files changed, 316 insertions(+), 269 deletions(-) create mode 100644 ao-tools/altosui/AltosSiteMapCache.java delete mode 100644 ao-tools/altosui/AltosSiteMapLabel.java diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index 1db83959..a8b66dac 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -33,36 +33,206 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { + // max vertical step in a tile in naut. miles + static final double tile_size_nmi = 1.0; + + static final int px_size = 512; + + private static Point2D.Double translatePoint(Point2D.Double p, + Point2D.Double d) + { + return new Point2D.Double(p.x + d.x, p.y + d.y); + } + + static class LatLng { + public double lat, lng; + public LatLng(double lat, double lng) { + this.lat = lat; + this.lng = lng; + } + } + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private static Point2D.Double pt(LatLng latlng, int zoom) { + double scale_x = 256/360.0 * Math.pow(2, zoom); + double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + return pt(latlng, scale_x, scale_y); + } + + private static Point2D.Double pt(LatLng latlng, + double scale_x, double scale_y) + { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = latlng.lng * scale_x; + + e = Math.sin(Math.toRadians(latlng.lat)); + e = Math.max(e,-(1-1.0E-15)); + e = Math.min(e, 1-1.0E-15 ); + + res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + static private LatLng latlng(Point2D.Double pt, + double scale_x, double scale_y) + { + double lat, lng; + double rads; + + lng = pt.x/scale_x; + rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new LatLng(lat,lng); + } + + int zoom; + double scale_x, scale_y; + + private Point2D.Double pt(double lat, double lng) { + return pt(new LatLng(lat, lng), scale_x, scale_y); + } + + private LatLng latlng(double x, double y) { + return latlng(new Point2D.Double(x,y), scale_x, scale_y); + } + private LatLng latlng(Point2D.Double pt) { + return latlng(pt, scale_x, scale_y); + } + + AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; + Point2D.Double [] tileOffset = new Point2D.Double[9]; + + private Point2D.Double getBaseLocation(double lat, double lng) { + Point2D.Double locn, north_step; + + zoom = 2; + // stupid loop structure to please Java's control flow analysis + do { + zoom++; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + locn = pt(lat, lng); + north_step = pt(lat+tile_size_nmi/60.0, lng); + if (locn.y - north_step.y > px_size) + break; + } while (zoom < 22); + locn.x = -px_size * Math.floor(locn.x/px_size); + locn.y = -px_size * Math.floor(locn.y/px_size); + return locn; + } + public void reset() { // nothing } - public void show(AltosState state, int crc_errors) { - for (int x = 0; x < mapTiles.length; x++) { - mapTiles[x].show(state, crc_errors); + + private void bgLoadMap(final int i, + final File pngfile, final String pngurl) + { + Thread thread = new Thread() { + public void run() { + ImageIcon res; + res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); + if (res != null) { + mapTiles[i].loadMap(res); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + }; + thread.start(); + } + + public static void prefetchMaps(double lat, double lng, int w, int h) { + AltosPreferences.init(null); + + AltosSiteMap asm = new AltosSiteMap(true); + Point2D.Double c = asm.getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); + Point2D.Double p2; + int dx = -w/2, dy = -h/2; + for (int y = dy; y < h+dy; y++) { + for (int x = dx; x < w+dx; x++) { + LatLng map_latlng = asm.latlng( + -c.x + x*px_size + px_size/2, + -c.y + y*px_size + px_size/2); + File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); + if (pngfile.exists()) { + System.out.printf("Already have %s\n", pngfile); + } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { + System.out.printf("Fetched map %s\n", pngfile); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } } } - - AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; - class GrabNDrag extends MouseInputAdapter { - private JComponent scroll; - private Point startPt = new Point(); + private void initMaps(double lat, double lng) { + Point2D.Double c = getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); + + for (int i = 0; i < 9; i++) { + int x = i%3 - 1, y = i/3 - 1; - public GrabNDrag(JComponent parent) { - scroll = parent; + tileOffset[i] = new Point2D.Double( + c.x - x*px_size, p.y = c.y - y*px_size); + LatLng map_latlng = latlng( + -tileOffset[i].x+px_size/2, + -tileOffset[i].y+px_size/2); + + File pngfile = MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = MapURL(map_latlng.lat, map_latlng.lng); + bgLoadMap(i, pngfile, pngurl); + } + } + + private File MapFile(double lat, double lng) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return new File(AltosPreferences.logdir(), + String.format("map-%c%.6f,%c%.6f-%d.png", + chlat, lat, chlng, lng, zoom)); + } + + private String MapURL(double lat, double lng) { + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); + } + + boolean initialised = false; + public void show(AltosState state, int crc_errors) { + // if insufficient gps data, nothing to update + if (!state.gps_ready) { + if (state.pad_lat == 0 && state.pad_lon == 0) + return; + if (state.ngps < 3) + return; + } + + if (!initialised) { + initMaps(state.pad_lat, state.pad_lon); + initialised = true; } - public void mousePressed(MouseEvent e) { - startPt.setLocation(e.getPoint()); + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + for (int x = 0; x < mapTiles.length; x++) { + mapTiles[x].show(state, crc_errors, + translatePoint(pt, tileOffset[x])); } - public void mouseDragged(MouseEvent e) { - int xd = e.getX() - startPt.x; - int yd = e.getY() - startPt.y; - - Rectangle r = scroll.getVisibleRect(); - r.x -= xd; - r.y -= yd; - scroll.scrollRectToVisible(r); + } + + private AltosSiteMap(boolean knowWhatYouAreDoing) { + if (!knowWhatYouAreDoing) { + throw new RuntimeException("Arggh."); } } @@ -83,9 +253,11 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.BOTH; + // put some space between the map tiles, debugging only + // c.insets = new Insets(5, 5, 5, 5); for (int x = 0; x < 9; x++) { c.gridx = x % 3; c.gridy = x / 3; - mapTiles[x] = new AltosSiteMapTile((x%3)-1, (x/3)-1); + mapTiles[x] = new AltosSiteMapTile(px_size); layout.setConstraints(mapTiles[x], c); comp.add(mapTiles[x]); } diff --git a/ao-tools/altosui/AltosSiteMapCache.java b/ao-tools/altosui/AltosSiteMapCache.java new file mode 100644 index 00000000..dbdcbf65 --- /dev/null +++ b/ao-tools/altosui/AltosSiteMapCache.java @@ -0,0 +1,103 @@ +/* + * 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 java.awt.event.*; +import javax.swing.*; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.net.URL; +import java.net.URLConnection; + +public class AltosSiteMapCache extends JLabel { + public static boolean fetchMap(File file, String url) { + URL u; + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return false; + } + + byte[] data; + try { + URLConnection uc = u.openConnection(); + int contentLength = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) { + return false; + } + } catch (IOException e) { + return false; + } + + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + if (file.exists()) { + file.delete(); + } + return false; + } + return true; + } + + public static ImageIcon fetchAndLoadMap(File pngfile, String url) { + if (!pngfile.exists()) { + if (!fetchMap(pngfile, url)) { + return null; + } + } + return loadMap(pngfile, url); + } + + public static ImageIcon loadMap(File pngfile, String url) { + if (!pngfile.exists()) { + return null; + } + + try { + return new ImageIcon(ImageIO.read(pngfile)); + } catch (IOException e) { + System.out.printf("# IO error trying to load %s\n", pngfile); + return null; + } + } +} + diff --git a/ao-tools/altosui/AltosSiteMapLabel.java b/ao-tools/altosui/AltosSiteMapLabel.java deleted file mode 100644 index 1a371c5b..00000000 --- a/ao-tools/altosui/AltosSiteMapLabel.java +++ /dev/null @@ -1,142 +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 java.awt.event.*; -import javax.swing.*; -import javax.imageio.ImageIO; -import javax.swing.table.*; -import java.io.*; -import java.util.*; -import java.text.*; -import java.util.prefs.*; -import java.net.URL; -import java.net.URLConnection; - -public class AltosSiteMapLabel extends JLabel { - public static boolean fetchMap(File file, String url) { - URL u; - try { - u = new URL(url); - } catch (java.net.MalformedURLException e) { - return false; - } - - byte[] data; - try { - URLConnection uc = u.openConnection(); - int contentLength = uc.getContentLength(); - InputStream in = new BufferedInputStream(uc.getInputStream()); - int bytesRead = 0; - int offset = 0; - data = new byte[contentLength]; - while (offset < contentLength) { - bytesRead = in.read(data, offset, data.length - offset); - if (bytesRead == -1) - break; - offset += bytesRead; - } - in.close(); - - if (offset != contentLength) { - return false; - } - } catch (IOException e) { - return false; - } - - try { - FileOutputStream out = new FileOutputStream(file); - out.write(data); - out.flush(); - out.close(); - } catch (FileNotFoundException e) { - return false; - } catch (IOException e) { - if (file.exists()) { - file.delete(); - } - return false; - } - return true; - } - - public void fetchAndLoadMap(final File pngfile, final String url) { - System.out.printf("# Trying to fetch %s...\n", pngfile); - - Thread thread = new Thread() { - public void run() { - try { - if (fetchMap(pngfile, url)) { - setIcon(new ImageIcon(ImageIO.read(pngfile))); - } - } catch (Exception e) { - System.out.printf("# Failed to fetch file %s\n", pngfile); - System.out.printf(" wget -O '%s' ''\n", pngfile, url); - } - } - }; - thread.start(); - } - - public void fetchMap(double lat, double lng, int zoom, int px_size) { - File pngfile = MapFile(lat, lng, zoom); - String url = MapURL(lat, lng, zoom, px_size); - - if (!pngfile.exists()) { - fetchMap(pngfile, url); - } - } - - public void loadMap(double lat, double lng, int zoom, int px_size) { - File pngfile = MapFile(lat, lng, zoom); - String url = MapURL(lat, lng, zoom, px_size); - - if (!pngfile.exists()) { - fetchAndLoadMap(pngfile, url); - return; - } - - try { - setIcon(new ImageIcon(ImageIO.read(pngfile))); - return; - } catch (IOException e) { - System.out.printf("# IO error trying to load %s\n", pngfile); - return; - } - } - - private static File MapFile(double lat, double lng, int zoom) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return new File(AltosPreferences.logdir(), - String.format("map-%c%.6f,%c%.6f-%d.png", - chlat, lat, chlng, lng, zoom)); - } - - private static String MapURL(double lat, double lng, - int zoom, int px_size) - { - return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); - } -} - diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index 6035a794..de28fc8b 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -32,96 +32,15 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMapTile extends JLayeredPane { - int zoom; - double scale_x, scale_y; Point2D.Double coord_pt; Point2D.Double last_pt; - AltosSiteMapLabel mapLabel; + JLabel mapLabel; JLabel draw; Graphics2D g2d; - int off_x; - int off_y; - - static final int px_size = 512; - - private void loadMap() { - Point2D.Double map_latlng = latlng(px_size/2, px_size/2); - mapLabel.loadMap(map_latlng.x, map_latlng.y, zoom, px_size); - } - - private boolean setLocation(double lat, double lng) { - Point2D.Double north_step; - double step_nm = 0.5; - for (zoom = 3; zoom < 22; zoom++) { - coord_pt = pt(lat, lng, new Point2D.Double(0,0), zoom); - north_step = pt(lat+step_nm/60.0, lng, - new Point2D.Double(0,0), zoom); - if (coord_pt.y - north_step.y > px_size/2) - break; - } - coord_pt.x = -px_size * Math.floor(coord_pt.x/px_size + off_x); - coord_pt.y = -px_size * Math.floor(coord_pt.y/px_size + off_y); - - scale_x = 256/360.0 * Math.pow(2, zoom); - scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - - last_pt = null; - - return true; - } - - private static double limit(double v, double lo, double hi) { - if (v < lo) - return lo; - if (hi < v) - return hi; - return v; - } - - // based on google js - // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js - // search for fromLatLngToPoint and fromPointToLatLng - private Point2D.Double pt(double lat, double lng) { - return pt(lat, lng, coord_pt, scale_x, scale_y); - } - - private static Point2D.Double pt(double lat, double lng, - Point2D.Double centre, int zoom) - { - double scale_x = 256/360.0 * Math.pow(2, zoom); - double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - return pt(lat, lng, centre, scale_x, scale_y); - } - - private static Point2D.Double pt(double lat, double lng, - Point2D.Double centre, double scale_x, double scale_y) - { - Point2D.Double res = new Point2D.Double(); - double e; - - res.x = centre.x + lng*scale_x; - e = limit(Math.sin(Math.toRadians(lat)),-(1-1.0E-15),1-1.0E-15); - res.y = centre.y + 0.5*Math.log((1+e)/(1-e))*-scale_y; - return res; - } - - private Point2D.Double latlng(double x, double y) { - return latlng(new Point2D.Double(x,y), coord_pt); - } - private Point2D.Double latlng(Point2D.Double pt) { - return latlng(pt, coord_pt); - } - private Point2D.Double latlng(Point2D.Double pt, Point2D.Double centre) { - double lat, lng; - double rads; - - lng = (pt.x - centre.x)/scale_x; - rads = 2 * Math.atan(Math.exp((pt.y-centre.y)/-scale_y)); - lat = Math.toDegrees(rads - Math.PI/2); - - return new Point2D.Double(lat,lng); + public void loadMap(ImageIcon icn) { + mapLabel.setIcon(icn); } static Color stateColors[] = { @@ -138,21 +57,13 @@ public class AltosSiteMapTile extends JLayeredPane { boolean drawn_landed_circle = false; boolean drawn_boost_circle = false; - public void show(AltosState state, int crc_errors) { - if (!state.gps_ready) { - if (state.pad_lat == 0 && state.pad_lon == 0) - return; - if (state.ngps < 3) - return; - } - + public void show(AltosState state, int crc_errors, Point2D.Double pt) { if (last_pt == null) { - setLocation(state.pad_lat, state.pad_lon); - loadMap(); - last_pt = pt(state.pad_lat, state.pad_lon); + // setLocation(state.pad_lat, state.pad_lon); + // loadMap(); + last_pt = pt; } - Point2D.Double pt = pt(state.gps.lat, state.gps.lon); if (pt != last_pt) { if (0 <= state.state && state.state < stateColors.length) { g2d.setColor(stateColors[state.state]); @@ -160,6 +71,7 @@ public class AltosSiteMapTile extends JLayeredPane { g2d.draw(new Line2D.Double(last_pt, pt)); } + int px_size = getWidth(); if (0 <= pt.x && pt.x < px_size) { if (0 <= pt.y && pt.y < px_size) { int dx = 500, dy = 250; @@ -192,7 +104,7 @@ public class AltosSiteMapTile extends JLayeredPane { repaint(); } - public static Graphics2D fillLabel(JLabel l, Color c) { + 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(); @@ -202,24 +114,21 @@ public class AltosSiteMapTile extends JLayeredPane { return g; } - public AltosSiteMapTile(int x_tile_offset, int y_tile_offset) { + public AltosSiteMapTile(int px_size) { setPreferredSize(new Dimension(px_size, px_size)); - mapLabel = new AltosSiteMapLabel(); - fillLabel(mapLabel, Color.GRAY); + 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)); + g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size); draw.setBounds(0, 0, px_size, px_size); draw.setOpaque(false); add(draw, new Integer(1)); - - off_x = x_tile_offset; - off_y = y_tile_offset; } } diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 6bfde014..93a5e0d8 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -353,7 +353,11 @@ public class AltosUI extends JFrame { public static void main(final String[] args) { int process = 0; /* Handle batch-mode */ - if (args.length == 2 && args[0].equals("--replay")) { + if (args.length == 3 && args[0].equals("--fetchmaps")) { + double lat = Double.parseDouble(args[1]); + double lon = Double.parseDouble(args[2]); + AltosSiteMap.prefetchMaps(lat, lon, 5, 5); + } else if (args.length == 2 && args[0].equals("--replay")) { String filename = args[1]; FileInputStream in; try { diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 334608f6..93a43b12 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -10,6 +10,7 @@ CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../libaltos:$(FREETTS)/ bin_SCRIPTS=altosui altosui_JAVA = \ + GrabNDrag.java \ AltosAscent.java \ AltosChannelMenu.java \ AltosConfig.java \ @@ -63,7 +64,7 @@ altosui_JAVA = \ AltosSerialInUseException.java \ AltosSerialMonitor.java \ AltosSiteMap.java \ - AltosSiteMapLabel.java \ + AltosSiteMapCache.java \ AltosSiteMapTile.java \ AltosState.java \ AltosTelemetry.java \ -- cgit v1.2.3 From 66ebd954d9c9a44a8db0ee713c682e39306fabd8 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 08:28:24 +1000 Subject: Add GrabNDrag.java --- ao-tools/altosui/GrabNDrag.java | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 ao-tools/altosui/GrabNDrag.java diff --git a/ao-tools/altosui/GrabNDrag.java b/ao-tools/altosui/GrabNDrag.java new file mode 100644 index 00000000..b44f3fe2 --- /dev/null +++ b/ao-tools/altosui/GrabNDrag.java @@ -0,0 +1,51 @@ +/* + * 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 java.awt.event.*; +import javax.swing.*; +import javax.swing.event.MouseInputAdapter; +import javax.imageio.ImageIO; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; + +class GrabNDrag extends MouseInputAdapter { + private JComponent scroll; + private Point startPt = new Point(); + + public GrabNDrag(JComponent parent) { + scroll = parent; + } + + public void mousePressed(MouseEvent e) { + startPt.setLocation(e.getPoint()); + } + public void mouseDragged(MouseEvent e) { + int xd = e.getX() - startPt.x; + int yd = e.getY() - startPt.y; + + Rectangle r = scroll.getVisibleRect(); + r.x -= xd; + r.y -= yd; + scroll.scrollRectToVisible(r); + } +} -- cgit v1.2.3 From 82636305021c41d676f5f0f11378724fe0de0079 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 08:44:13 +1000 Subject: AltosSiteMap: be more polite about preferred size --- ao-tools/altosui/AltosSiteMap.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index a8b66dac..a241cbd0 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -262,6 +262,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { comp.add(mapTiles[x]); } setViewportView(comp); + setPreferredSize(new Dimension(500,200)); } } -- cgit v1.2.3 From 1e712647dd6df1e77650db705f3ac32a3c8f6907 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sun, 21 Nov 2010 08:58:44 +1000 Subject: altosui: reindent --- ao-tools/altosui/AltosSiteMap.java | 463 ++++++++++++++++---------------- ao-tools/altosui/AltosSiteMapCache.java | 126 ++++----- ao-tools/altosui/AltosSiteMapTile.java | 196 +++++++------- 3 files changed, 393 insertions(+), 392 deletions(-) diff --git a/ao-tools/altosui/AltosSiteMap.java b/ao-tools/altosui/AltosSiteMap.java index a241cbd0..5f5e30f0 100644 --- a/ao-tools/altosui/AltosSiteMap.java +++ b/ao-tools/altosui/AltosSiteMap.java @@ -33,236 +33,237 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { - // max vertical step in a tile in naut. miles - static final double tile_size_nmi = 1.0; - - static final int px_size = 512; - - private static Point2D.Double translatePoint(Point2D.Double p, - Point2D.Double d) - { - return new Point2D.Double(p.x + d.x, p.y + d.y); - } - - static class LatLng { - public double lat, lng; - public LatLng(double lat, double lng) { - this.lat = lat; - this.lng = lng; - } - } - - // based on google js - // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js - // search for fromLatLngToPoint and fromPointToLatLng - private static Point2D.Double pt(LatLng latlng, int zoom) { - double scale_x = 256/360.0 * Math.pow(2, zoom); - double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - return pt(latlng, scale_x, scale_y); - } - - private static Point2D.Double pt(LatLng latlng, - double scale_x, double scale_y) - { - Point2D.Double res = new Point2D.Double(); - double e; - - res.x = latlng.lng * scale_x; - - e = Math.sin(Math.toRadians(latlng.lat)); - e = Math.max(e,-(1-1.0E-15)); - e = Math.min(e, 1-1.0E-15 ); - - res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; - return res; - } - - static private LatLng latlng(Point2D.Double pt, - double scale_x, double scale_y) - { - double lat, lng; - double rads; - - lng = pt.x/scale_x; - rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); - lat = Math.toDegrees(rads - Math.PI/2); - - return new LatLng(lat,lng); - } - - int zoom; - double scale_x, scale_y; - - private Point2D.Double pt(double lat, double lng) { - return pt(new LatLng(lat, lng), scale_x, scale_y); - } - - private LatLng latlng(double x, double y) { - return latlng(new Point2D.Double(x,y), scale_x, scale_y); - } - private LatLng latlng(Point2D.Double pt) { - return latlng(pt, scale_x, scale_y); - } - - AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; - Point2D.Double [] tileOffset = new Point2D.Double[9]; - - private Point2D.Double getBaseLocation(double lat, double lng) { - Point2D.Double locn, north_step; - - zoom = 2; - // stupid loop structure to please Java's control flow analysis - do { - zoom++; - scale_x = 256/360.0 * Math.pow(2, zoom); - scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - locn = pt(lat, lng); - north_step = pt(lat+tile_size_nmi/60.0, lng); - if (locn.y - north_step.y > px_size) - break; - } while (zoom < 22); - locn.x = -px_size * Math.floor(locn.x/px_size); - locn.y = -px_size * Math.floor(locn.y/px_size); - return locn; - } - - public void reset() { - // nothing - } - - private void bgLoadMap(final int i, - final File pngfile, final String pngurl) - { - Thread thread = new Thread() { - public void run() { - ImageIcon res; - res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); - if (res != null) { - mapTiles[i].loadMap(res); - } else { - System.out.printf("# Failed to fetch file %s\n", pngfile); - System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); - } - } - }; - thread.start(); - } - - public static void prefetchMaps(double lat, double lng, int w, int h) { - AltosPreferences.init(null); - - AltosSiteMap asm = new AltosSiteMap(true); - Point2D.Double c = asm.getBaseLocation(lat, lng); - Point2D.Double p = new Point2D.Double(); - Point2D.Double p2; - int dx = -w/2, dy = -h/2; - for (int y = dy; y < h+dy; y++) { - for (int x = dx; x < w+dx; x++) { - LatLng map_latlng = asm.latlng( - -c.x + x*px_size + px_size/2, - -c.y + y*px_size + px_size/2); - File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); - String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); - if (pngfile.exists()) { - System.out.printf("Already have %s\n", pngfile); - } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { - System.out.printf("Fetched map %s\n", pngfile); - } else { - System.out.printf("# Failed to fetch file %s\n", pngfile); - System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); - } - } - } - } - - private void initMaps(double lat, double lng) { - Point2D.Double c = getBaseLocation(lat, lng); - Point2D.Double p = new Point2D.Double(); - - for (int i = 0; i < 9; i++) { - int x = i%3 - 1, y = i/3 - 1; - - tileOffset[i] = new Point2D.Double( - c.x - x*px_size, p.y = c.y - y*px_size); - LatLng map_latlng = latlng( - -tileOffset[i].x+px_size/2, - -tileOffset[i].y+px_size/2); - - File pngfile = MapFile(map_latlng.lat, map_latlng.lng); - String pngurl = MapURL(map_latlng.lat, map_latlng.lng); - bgLoadMap(i, pngfile, pngurl); - } - } - - private File MapFile(double lat, double lng) { - char chlat = lat < 0 ? 'S' : 'N'; - char chlng = lng < 0 ? 'E' : 'W'; - if (lat < 0) lat = -lat; - if (lng < 0) lng = -lng; - return new File(AltosPreferences.logdir(), - String.format("map-%c%.6f,%c%.6f-%d.png", - chlat, lat, chlng, lng, zoom)); - } - - private String MapURL(double lat, double lng) { - return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); - } - - boolean initialised = false; - public void show(AltosState state, int crc_errors) { - // if insufficient gps data, nothing to update - if (!state.gps_ready) { - if (state.pad_lat == 0 && state.pad_lon == 0) - return; - if (state.ngps < 3) - return; - } - - if (!initialised) { - initMaps(state.pad_lat, state.pad_lon); - initialised = true; - } - - Point2D.Double pt = pt(state.gps.lat, state.gps.lon); - for (int x = 0; x < mapTiles.length; x++) { - mapTiles[x].show(state, crc_errors, - translatePoint(pt, tileOffset[x])); - } - } - - private AltosSiteMap(boolean knowWhatYouAreDoing) { - if (!knowWhatYouAreDoing) { - throw new RuntimeException("Arggh."); - } - } - - public AltosSiteMap() { - JComponent comp = new JComponent() { - GrabNDrag scroller = new GrabNDrag(this); - { - addMouseMotionListener(scroller); - addMouseListener(scroller); - setAutoscrolls(true); - } - }; - - GridBagLayout layout = new GridBagLayout(); - comp.setLayout(layout); - - GridBagConstraints c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.BOTH; - - // put some space between the map tiles, debugging only - // c.insets = new Insets(5, 5, 5, 5); - for (int x = 0; x < 9; x++) { - c.gridx = x % 3; c.gridy = x / 3; - mapTiles[x] = new AltosSiteMapTile(px_size); - layout.setConstraints(mapTiles[x], c); - comp.add(mapTiles[x]); - } - setViewportView(comp); - setPreferredSize(new Dimension(500,200)); - } + // max vertical step in a tile in naut. miles + static final double tile_size_nmi = 1.0; + + static final int px_size = 512; + + private static Point2D.Double translatePoint(Point2D.Double p, + Point2D.Double d) + { + return new Point2D.Double(p.x + d.x, p.y + d.y); + } + + static class LatLng { + public double lat, lng; + public LatLng(double lat, double lng) { + this.lat = lat; + this.lng = lng; + } + } + + // based on google js + // http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js + // search for fromLatLngToPoint and fromPointToLatLng + private static Point2D.Double pt(LatLng latlng, int zoom) { + double scale_x = 256/360.0 * Math.pow(2, zoom); + double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + return pt(latlng, scale_x, scale_y); + } + + private static Point2D.Double pt(LatLng latlng, + double scale_x, double scale_y) + { + Point2D.Double res = new Point2D.Double(); + double e; + + res.x = latlng.lng * scale_x; + + e = Math.sin(Math.toRadians(latlng.lat)); + e = Math.max(e,-(1-1.0E-15)); + e = Math.min(e, 1-1.0E-15 ); + + res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y; + return res; + } + + static private LatLng latlng(Point2D.Double pt, + double scale_x, double scale_y) + { + double lat, lng; + double rads; + + lng = pt.x/scale_x; + rads = 2 * Math.atan(Math.exp(-pt.y/scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new LatLng(lat,lng); + } + + int zoom; + double scale_x, scale_y; + + private Point2D.Double pt(double lat, double lng) { + return pt(new LatLng(lat, lng), scale_x, scale_y); + } + + private LatLng latlng(double x, double y) { + return latlng(new Point2D.Double(x,y), scale_x, scale_y); + } + private LatLng latlng(Point2D.Double pt) { + return latlng(pt, scale_x, scale_y); + } + + AltosSiteMapTile [] mapTiles = new AltosSiteMapTile[9]; + Point2D.Double [] tileOffset = new Point2D.Double[9]; + + private Point2D.Double getBaseLocation(double lat, double lng) { + Point2D.Double locn, north_step; + + zoom = 2; + // stupid loop structure to please Java's control flow analysis + do { + zoom++; + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + locn = pt(lat, lng); + north_step = pt(lat+tile_size_nmi/60.0, lng); + if (locn.y - north_step.y > px_size) + break; + } while (zoom < 22); + locn.x = -px_size * Math.floor(locn.x/px_size); + locn.y = -px_size * Math.floor(locn.y/px_size); + return locn; + } + + public void reset() { + // nothing + } + + private void bgLoadMap(final int i, + final File pngfile, final String pngurl) + { + Thread thread = new Thread() { + public void run() { + ImageIcon res; + res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl); + if (res != null) { + mapTiles[i].loadMap(res); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + }; + thread.start(); + } + + public static void prefetchMaps(double lat, double lng, int w, int h) { + AltosPreferences.init(null); + + AltosSiteMap asm = new AltosSiteMap(true); + Point2D.Double c = asm.getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); + Point2D.Double p2; + int dx = -w/2, dy = -h/2; + for (int y = dy; y < h+dy; y++) { + for (int x = dx; x < w+dx; x++) { + LatLng map_latlng = asm.latlng( + -c.x + x*px_size + px_size/2, + -c.y + y*px_size + px_size/2); + File pngfile = asm.MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = asm.MapURL(map_latlng.lat, map_latlng.lng); + if (pngfile.exists()) { + System.out.printf("Already have %s\n", pngfile); + } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) { + System.out.printf("Fetched map %s\n", pngfile); + } else { + System.out.printf("# Failed to fetch file %s\n", pngfile); + System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl); + } + } + } + } + + private void initMaps(double lat, double lng) { + Point2D.Double c = getBaseLocation(lat, lng); + Point2D.Double p = new Point2D.Double(); + + for (int i = 0; i < 9; i++) { + int x = i%3 - 1, y = i/3 - 1; + + tileOffset[i] = new Point2D.Double( + c.x - x*px_size, p.y = c.y - y*px_size); + LatLng map_latlng = latlng( + -tileOffset[i].x+px_size/2, + -tileOffset[i].y+px_size/2); + + File pngfile = MapFile(map_latlng.lat, map_latlng.lng); + String pngurl = MapURL(map_latlng.lat, map_latlng.lng); + bgLoadMap(i, pngfile, pngurl); + } + } + + private File MapFile(double lat, double lng) { + char chlat = lat < 0 ? 'S' : 'N'; + char chlng = lng < 0 ? 'E' : 'W'; + if (lat < 0) lat = -lat; + if (lng < 0) lng = -lng; + return new File(AltosPreferences.logdir(), + String.format("map-%c%.6f,%c%.6f-%d.png", + chlat, lat, chlng, lng, zoom)); + } + + private String MapURL(double lat, double lng) { + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size); + } + + boolean initialised = false; + public void show(AltosState state, int crc_errors) { + // if insufficient gps data, nothing to update + if (!state.gps_ready) { + if (state.pad_lat == 0 && state.pad_lon == 0) + return; + if (state.ngps < 3) + return; + } + + if (!initialised) { + initMaps(state.pad_lat, state.pad_lon); + initialised = true; + } + + Point2D.Double pt = pt(state.gps.lat, state.gps.lon); + for (int x = 0; x < mapTiles.length; x++) { + mapTiles[x].show(state, crc_errors, + translatePoint(pt, tileOffset[x])); + } + } + + private AltosSiteMap(boolean knowWhatYouAreDoing) { + if (!knowWhatYouAreDoing) { + throw new RuntimeException("Arggh."); + } + } + + public AltosSiteMap() { + JComponent comp = new JComponent() { + GrabNDrag scroller = new GrabNDrag(this); + { + addMouseMotionListener(scroller); + addMouseListener(scroller); + setAutoscrolls(true); + } + }; + + GridBagLayout layout = new GridBagLayout(); + comp.setLayout(layout); + + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + + // put some space between the map tiles, debugging only + // c.insets = new Insets(5, 5, 5, 5); + for (int x = 0; x < 9; x++) { + c.gridx = x % 3; + c.gridy = x / 3; + mapTiles[x] = new AltosSiteMapTile(px_size); + layout.setConstraints(mapTiles[x], c); + comp.add(mapTiles[x]); + } + setViewportView(comp); + setPreferredSize(new Dimension(500,200)); + } } diff --git a/ao-tools/altosui/AltosSiteMapCache.java b/ao-tools/altosui/AltosSiteMapCache.java index dbdcbf65..e9dbf8e6 100644 --- a/ao-tools/altosui/AltosSiteMapCache.java +++ b/ao-tools/altosui/AltosSiteMapCache.java @@ -31,73 +31,73 @@ import java.net.URL; import java.net.URLConnection; public class AltosSiteMapCache extends JLabel { - public static boolean fetchMap(File file, String url) { - URL u; - try { - u = new URL(url); - } catch (java.net.MalformedURLException e) { - return false; - } + public static boolean fetchMap(File file, String url) { + URL u; + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return false; + } - byte[] data; - try { - URLConnection uc = u.openConnection(); - int contentLength = uc.getContentLength(); - InputStream in = new BufferedInputStream(uc.getInputStream()); - int bytesRead = 0; - int offset = 0; - data = new byte[contentLength]; - while (offset < contentLength) { - bytesRead = in.read(data, offset, data.length - offset); - if (bytesRead == -1) - break; - offset += bytesRead; - } - in.close(); + byte[] data; + try { + URLConnection uc = u.openConnection(); + int contentLength = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); - if (offset != contentLength) { - return false; - } - } catch (IOException e) { - return false; - } - - try { - FileOutputStream out = new FileOutputStream(file); - out.write(data); - out.flush(); - out.close(); - } catch (FileNotFoundException e) { - return false; - } catch (IOException e) { - if (file.exists()) { - file.delete(); - } - return false; - } - return true; - } + if (offset != contentLength) { + return false; + } + } catch (IOException e) { + return false; + } - public static ImageIcon fetchAndLoadMap(File pngfile, String url) { - if (!pngfile.exists()) { - if (!fetchMap(pngfile, url)) { - return null; - } - } - return loadMap(pngfile, url); - } + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + if (file.exists()) { + file.delete(); + } + return false; + } + return true; + } - public static ImageIcon loadMap(File pngfile, String url) { - if (!pngfile.exists()) { - return null; - } + public static ImageIcon fetchAndLoadMap(File pngfile, String url) { + if (!pngfile.exists()) { + if (!fetchMap(pngfile, url)) { + return null; + } + } + return loadMap(pngfile, url); + } - try { - return new ImageIcon(ImageIO.read(pngfile)); - } catch (IOException e) { - System.out.printf("# IO error trying to load %s\n", pngfile); - return null; - } - } + public static ImageIcon loadMap(File pngfile, String url) { + if (!pngfile.exists()) { + return null; + } + + try { + return new ImageIcon(ImageIO.read(pngfile)); + } catch (IOException e) { + System.out.printf("# IO error trying to load %s\n", pngfile); + return null; + } + } } diff --git a/ao-tools/altosui/AltosSiteMapTile.java b/ao-tools/altosui/AltosSiteMapTile.java index de28fc8b..8aee86c1 100644 --- a/ao-tools/altosui/AltosSiteMapTile.java +++ b/ao-tools/altosui/AltosSiteMapTile.java @@ -32,103 +32,103 @@ import java.awt.geom.Point2D; import java.awt.geom.Line2D; public class AltosSiteMapTile extends JLayeredPane { - Point2D.Double coord_pt; - Point2D.Double last_pt; - - JLabel mapLabel; - JLabel draw; - Graphics2D g2d; - - public void loadMap(ImageIcon icn) { - mapLabel.setIcon(icn); - } - - 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 - }; - - boolean drawn_landed_circle = false; - boolean drawn_boost_circle = false; - public void show(AltosState state, int crc_errors, Point2D.Double pt) { - if (last_pt == null) { - // setLocation(state.pad_lat, state.pad_lon); - // loadMap(); - last_pt = pt; - } - - if (pt != last_pt) { - if (0 <= state.state && state.state < stateColors.length) { - g2d.setColor(stateColors[state.state]); - } - g2d.draw(new Line2D.Double(last_pt, pt)); - } - - int px_size = getWidth(); - if (0 <= pt.x && pt.x < px_size) { - if (0 <= pt.y && pt.y < px_size) { - int dx = 500, dy = 250; - if (state.state > 2) { - dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); - dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); - } - Rectangle r = new Rectangle((int)pt.x-dx, (int)pt.y-dy, - dx*2, dy*2); - scrollRectToVisible(r); - } - } - - 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); - } - - last_pt = pt; - repaint(); - } - - 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 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); - draw.setBounds(0, 0, px_size, px_size); - draw.setOpaque(false); - - add(draw, new Integer(1)); - } + Point2D.Double coord_pt; + Point2D.Double last_pt; + + JLabel mapLabel; + JLabel draw; + Graphics2D g2d; + + public void loadMap(ImageIcon icn) { + mapLabel.setIcon(icn); + } + + 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 + }; + + boolean drawn_landed_circle = false; + boolean drawn_boost_circle = false; + public void show(AltosState state, int crc_errors, Point2D.Double pt) { + if (last_pt == null) { + // setLocation(state.pad_lat, state.pad_lon); + // loadMap(); + last_pt = pt; + } + + if (pt != last_pt) { + if (0 <= state.state && state.state < stateColors.length) { + g2d.setColor(stateColors[state.state]); + } + g2d.draw(new Line2D.Double(last_pt, pt)); + } + + int px_size = getWidth(); + if (0 <= pt.x && pt.x < px_size) { + if (0 <= pt.y && pt.y < px_size) { + int dx = 500, dy = 250; + if (state.state > 2) { + dx = Math.min(200, 20 + (int) Math.abs(last_pt.x - pt.x)); + dy = Math.min(100, 10 + (int) Math.abs(last_pt.y - pt.y)); + } + Rectangle r = new Rectangle((int)pt.x-dx, (int)pt.y-dy, + dx*2, dy*2); + scrollRectToVisible(r); + } + } + + 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); + } + + last_pt = pt; + repaint(); + } + + 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 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); + draw.setBounds(0, 0, px_size, px_size); + draw.setOpaque(false); + + add(draw, new Integer(1)); + } } -- cgit v1.2.3