altosui: Display full map preload area in view.
authorKeith Packard <keithp@keithp.com>
Sun, 17 Jul 2011 05:34:44 +0000 (22:34 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 17 Jul 2011 05:34:44 +0000 (22:34 -0700)
This involved fixing the map view to support arbitrary sizes, and then
exposing a synchronous tile loading API so that the progress bar could
be used to show tile loading progress.

Signed-off-by: Keith Packard <keithp@keithp.com>
altosui/AltosSiteMap.java
altosui/AltosSiteMapCache.java
altosui/AltosSiteMapPreload.java
altosui/AltosSiteMapTile.java

index 7306813806e0e6034719c7d202dd5748d3005892..188902e9e535f0ceb3e9fc3dd8852d6f4c8b1569 100644 (file)
@@ -97,6 +97,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
        int zoom;
        double scale_x, scale_y;
 
+       int radius;     /* half width/height of tiles to load */
+
        private Point2D.Double pt(double lat, double lng) {
                return pt(new LatLng(lat, lng), scale_x, scale_y);
        }
@@ -144,37 +146,31 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                // nothing
        }
 
-       private void bgLoadMap(final AltosSiteMapTile tile,
-                              final File pngfile, final String pngurl)
+       private void loadMap(final AltosSiteMapTile tile,
+                            File pngfile, String pngurl)
        {
-               //System.out.printf("Loading/fetching map %s\n", pngfile);
-               Thread thread = new Thread() {
-                       public void run() {
-                               final ImageIcon res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl);
-                               if (res != null) {
-                                       SwingUtilities.invokeLater(new Runnable() {
-                                                       public void run() {
-                                                               tile.loadMap(res);
-                                                       }
-                                               });
-                               } else {
-                                       System.out.printf("# Failed to fetch file %s\n", pngfile);
-                                       System.out.printf(" wget -O '%s' ''\n", pngfile, pngurl);
-                               }
-                       }
-               };
-               thread.start();
+               final ImageIcon res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl);
+               if (res != null) {
+                       SwingUtilities.invokeLater(new Runnable() {
+                                       public void run() {
+                                               tile.loadMap(res);
+                                       }
+                               });
+               } else {
+                       System.out.printf("# Failed to fetch file %s\n", pngfile);
+                       System.out.printf(" wget -O '%s' '%s'\n", pngfile, pngurl);
+               }
        }
 
-       File    pngfile;
-       String  pngurl;
+       File pngfile;
+       String pngurl;
 
        public int prefetchMap(int x, int y) {
                LatLng map_latlng = latlng(
                        -centre.x + x*px_size + px_size/2,
                        -centre.y + y*px_size + px_size/2);
-               pngfile = MapFile(map_latlng.lat, map_latlng.lng);
-               pngurl = MapURL(map_latlng.lat, map_latlng.lng);
+               pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom);
+               pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom);
                if (pngfile.exists()) {
                        return 1;
                } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) {
@@ -210,25 +206,41 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                }
        }
 
-       private void initMap(AltosSiteMapTile tile, Point offset) {
+       public String initMap(Point offset) {
+               AltosSiteMapTile tile = mapTiles.get(offset);
                Point2D.Double coord = tileCoordOffset(offset);
 
                LatLng map_latlng = latlng(px_size/2-coord.x, px_size/2-coord.y);
 
-               File pngfile = MapFile(map_latlng.lat, map_latlng.lng);
-               String pngurl = MapURL(map_latlng.lat, map_latlng.lng);
-               bgLoadMap(tile, pngfile, pngurl);
+               File pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom);
+               String pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom);
+               loadMap(tile, pngfile, pngurl);
+               return pngfile.toString();
        }
 
-       private void initMaps(double lat, double lng) {
-               centre = getBaseLocation(lat, lng);
-
+       public void setBaseLocation(double lat, double lng) {
                for (Point k : mapTiles.keySet()) {
-                       initMap(mapTiles.get(k), k);
+                       AltosSiteMapTile tile = mapTiles.get(k);
+                       tile.clearMap();
                }
+                       
+               centre = getBaseLocation(lat, lng);
+               scrollRocketToVisible(pt(lat,lng));
        }
 
-       private File MapFile(double lat, double lng) {
+       private void initMaps(double lat, double lng) {
+               setBaseLocation(lat, lng);
+
+               Thread thread = new Thread() {
+                               public void run() {
+                                       for (Point k : mapTiles.keySet())
+                                               initMap(k);
+                               }
+                       };
+               thread.start();
+       }
+
+       private static File MapFile(double lat, double lng, int zoom) {
                char chlat = lat < 0 ? 'S' : 'N';
                char chlng = lng < 0 ? 'W' : 'E';
                if (lat < 0) lat = -lat;
@@ -238,7 +250,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                                              chlat, lat, chlng, lng, zoom));
        }
 
-       private String MapURL(double lat, double lng) {
+       private static String MapURL(double lat, double lng, int zoom) {
                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);
        }
 
@@ -248,7 +260,6 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
 
        public void show(double lat, double lon) {
                initMaps(lat, lon);
-               initialised = true;
                scrollRocketToVisible(pt(lat, lon));
        }
        public void show(final AltosState state, final int crc_errors) {
@@ -295,7 +306,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
 
                        AltosSiteMapTile tile = createTile(offset);
                        tile.show(state, crc_errors, lref, ref);
-                       initMap(tile, offset);
+                       initMap(offset);
                        finishTileLater(tile, offset);
                }
 
@@ -325,13 +336,13 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
        }
 
        private void ensureTilesAround(Point base_offset) {
-               for (int x = -1; x <= 1; x++) {
-                       for (int y = -1; y <= 1; y++) {
+               for (int x = -radius; x <= radius; x++) {
+                       for (int y = -radius; y <= radius; y++) {
                                Point offset = new Point(base_offset.x + x, base_offset.y + y);
                                if (mapTiles.containsKey(offset))
                                        continue;
                                AltosSiteMapTile tile = createTile(offset);
-                               initMap(tile, offset);
+                               initMap(offset);
                                finishTileLater(tile, offset);
                        }
                }
@@ -396,13 +407,15 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
        JComponent comp = new JComponent() { };
        private GridBagLayout layout = new GridBagLayout();
 
-       public AltosSiteMap() {
+       public AltosSiteMap(int in_radius) {
+               radius = in_radius;
+
                GrabNDrag scroller = new GrabNDrag(comp);
 
                comp.setLayout(layout);
 
-               for (int x = -1; x <= 1; x++) {
-                       for (int y = -1; y <= 1; y++) {
+               for (int x = -radius; x <= radius; x++) {
+                       for (int y = -radius; y <= radius; y++) {
                                Point offset = new Point(x, y);
                                AltosSiteMapTile t = createTile(offset);
                                addTileAt(t, offset);
@@ -411,4 +424,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
                setViewportView(comp);
                setPreferredSize(new Dimension(500,500));
        }
+
+       public AltosSiteMap() {
+               this(1);
+       }
 }
index 2e62cc454f71fcfa27ef782bd926395e64a5dddf..cfad52a9d5e3cbe7ba631c77725192afb126ed9a 100644 (file)
@@ -37,6 +37,7 @@ public class AltosSiteMapCache extends JLabel {
                try {
                        u = new URL(url);
                } catch (java.net.MalformedURLException e) {
+                       System.out.printf("Malformed URL '%s'\n", url);
                        return false;
                }
 
@@ -57,9 +58,12 @@ public class AltosSiteMapCache extends JLabel {
                        in.close();
 
                        if (offset != contentLength) {
+                               System.out.printf("Bad length %d != %d\n",
+                                                 offset, contentLength);
                                return false;
                        }
                } catch (IOException e) {
+                       System.out.printf("IO exception reading URL\n");
                        return false;
                }
 
@@ -69,11 +73,13 @@ public class AltosSiteMapCache extends JLabel {
                        out.flush();
                        out.close();
                } catch (FileNotFoundException e) {
+                       System.out.printf("Can't create file\n");
                        return false;
                } catch (IOException e) {
                        if (file.exists()) {
                                file.delete();
                        }
+                       System.out.printf("IO exception writing file\n");
                        return false;
                }
                return true;
index f939e9d6f71c3e0396b397a2beab46eb1c435e7d..25133435ca1dbc695086c43ec93629f24cd01401 100644 (file)
@@ -33,6 +33,7 @@ import java.awt.geom.Point2D;
 import java.awt.geom.Line2D;
 
 class AltosMapPos extends Box {
+       AltosUI         owner;
        JLabel          label;
        JComboBox       hemi;
        JTextField      deg;
@@ -58,25 +59,51 @@ class AltosMapPos extends Box {
 
        public double get_value() throws NumberFormatException {
                int     h = hemi.getSelectedIndex();
-               double d = Double.parseDouble(deg.getText());
-               double m = Double.parseDouble(min.getText());
-               double v = d + m/60.0;
+               String  d_t = deg.getText();
+               String  m_t = min.getText();
+               double  d, m, v;
+               try {
+                       d = Double.parseDouble(d_t);
+               } catch (NumberFormatException ne) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Invalid degrees \"%s\"",
+                                                                   d_t),
+                                                     "Invalid number",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       throw ne;
+               }
+               try {
+                       if (m_t.equals(""))
+                               m = 0;
+                       else
+                               m = Double.parseDouble(m_t);
+               } catch (NumberFormatException ne) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Invalid minutes \"%s\"",
+                                                                   m_t),
+                                                     "Invalid number",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       throw ne;
+               }
+               v = d + m/60.0;
                if (h == 1)
                        v = -v;
                return v;
        }
 
-       public AltosMapPos(String label_value,
+       public AltosMapPos(AltosUI in_owner,
+                          String label_value,
                           String[] hemi_names,
                           double default_value) {
                super(BoxLayout.X_AXIS);
+               owner = in_owner;
                label = new JLabel(label_value);
                hemi = new JComboBox(hemi_names);
                hemi.setEditable(false);
                deg = new JTextField("000");
-               deg_label = new JLabel("degrees");
+               deg_label = new JLabel("°");
                min = new JTextField("00.0000");
-               min_label = new JLabel("minutes");
+               min_label = new JLabel("'");
                set_value(default_value);
                add(label);
                add(Box.createRigidArea(new Dimension(5, 0)));
@@ -101,9 +128,9 @@ public class AltosSiteMapPreload extends JDialog implements ActionListener {
 
        JProgressBar    pbar;
 
-       final static int        width = 9;
-       final static int        height = 9;
-       final static int        tiles = width * height;
+       final static int        radius = 4;
+       final static int        width = (radius * 2 + 1);
+       final static int        height = (radius * 2 + 1);
 
        JToggleButton   load_button;
        boolean         loading;
@@ -116,15 +143,21 @@ public class AltosSiteMapPreload extends JDialog implements ActionListener {
                int             n;
                String          s;
 
-               public updatePbar(int in_n, String in_s) {
-                       n = in_n;
+               public updatePbar(int x, int y, String in_s) {
+                       n = (x + radius) + (y + radius) * width + 1;
+                       System.out.printf("update pbar %d\n", n);
                        s = in_s;
                }
 
                public void run() {
                        pbar.setValue(n);
                        pbar.setString(s);
-                       if (n == width * height) {
+                       if (n < width * height) {
+                               pbar.setValue(n);
+                               pbar.setString(s);
+                       } else {
+                               pbar.setValue(0);
+                               pbar.setString("");
                                load_button.setSelected(false);
                                loading = false;
                        }
@@ -140,11 +173,11 @@ public class AltosSiteMapPreload extends JDialog implements ActionListener {
                }
 
                public void run() {
-                       for (int y = 0; y < height; y++) {
-                               for (int x = 0; x < width; x++) {
-                                       map.prefetchMap(y - height/2, x - width/2);
-                                       SwingUtilities.invokeLater(new updatePbar(y * height + x + 1,
-                                                                                 map.pngfile.toString()));
+                       for (int y = -map.radius; y <= map.radius; y++) {
+                               for (int x = -map.radius; x <= map.radius; x++) {
+                                       String  pngfile;
+                                       pngfile = map.initMap(new Point(x,y));
+                                       SwingUtilities.invokeLater(new updatePbar(x, y, pngfile));
                                }
                        }
                }
@@ -158,12 +191,16 @@ public class AltosSiteMapPreload extends JDialog implements ActionListener {
 
                if (cmd.equals("load")) {
                        if (!loading) {
-                               loading = true;
-                               final double    latitude = lat.get_value();
-                               final double    longitude = lon.get_value();
-                               map.show(latitude,longitude);
-                               bgLoad thread = new bgLoad(map);
-                               thread.start();
+                               try {
+                                       final double    latitude = lat.get_value();
+                                       final double    longitude = lon.get_value();
+                                       map.setBaseLocation(latitude,longitude);
+                                       loading = true;
+                                       bgLoad thread = new bgLoad(map);
+                                       thread.start();
+                               } catch (NumberFormatException ne) {
+                                       load_button.setSelected(false);
+                               }
                        }
                }
        }
@@ -177,7 +214,7 @@ public class AltosSiteMapPreload extends JDialog implements ActionListener {
 
                pane.setLayout(new GridBagLayout());
 
-               map = new AltosSiteMap();
+               map = new AltosSiteMap(4);
 
                c.fill = GridBagConstraints.BOTH;
                c.anchor = GridBagConstraints.CENTER;
@@ -211,7 +248,8 @@ public class AltosSiteMapPreload extends JDialog implements ActionListener {
 
                pane.add(pbar, c);
 
-               lat = new AltosMapPos("Latitude:",
+               lat = new AltosMapPos(owner,
+                                     "Latitude:",
                                      lat_hemi_names,
                                      37.167833333);
                c.fill = GridBagConstraints.NONE;
@@ -227,7 +265,8 @@ public class AltosSiteMapPreload extends JDialog implements ActionListener {
 
                pane.add(lat, c);
                
-               lon = new AltosMapPos("Longitude:",
+               lon = new AltosMapPos(owner,
+                                     "Longitude:",
                                      lon_hemi_names,
                                      -97.73975);
 
index 8301f42b007e7fa9907991c3920543ce033e4946..66da7c54f01db2362941610adaf68a71ad91a91d 100644 (file)
@@ -35,11 +35,16 @@ public class AltosSiteMapTile extends JLayeredPane {
        JLabel mapLabel;
        JLabel draw;
        Graphics2D g2d;
+       int px_size;
 
        public void loadMap(ImageIcon icn) {
                mapLabel.setIcon(icn);
        }
 
+       public void clearMap() {
+               fillLabel(mapLabel, Color.GRAY, px_size);
+       }
+
        static Color stateColors[] = {
                Color.WHITE,  // startup
                Color.WHITE,  // idle
@@ -90,7 +95,8 @@ public class AltosSiteMapTile extends JLayeredPane {
                return g;
        }
 
-       public AltosSiteMapTile(int px_size) {
+       public AltosSiteMapTile(int in_px_size) {
+               px_size = in_px_size;
                setPreferredSize(new Dimension(px_size, px_size));
 
                mapLabel = new JLabel();