From db08e99361d82de63058d3388823f486e5fc9839 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 27 May 2014 10:58:53 -0700 Subject: [PATCH] altosuilib: Add multiple zoom levels and content types to map Also changes the file format for hybrid, satellite and terrain maps to jpg to save disk space. Signed-off-by: Keith Packard --- altosuilib/AltosSiteMap.java | 395 ++++++++++++++++++++++------ altosuilib/AltosSiteMapCache.java | 73 +++-- altosuilib/AltosSiteMapPreload.java | 8 +- altosuilib/AltosSiteMapTile.java | 74 +++++- 4 files changed, 436 insertions(+), 114 deletions(-) diff --git a/altosuilib/AltosSiteMap.java b/altosuilib/AltosSiteMap.java index 1cfbc8b5..f4143fe2 100644 --- a/altosuilib/AltosSiteMap.java +++ b/altosuilib/AltosSiteMap.java @@ -18,14 +18,39 @@ package org.altusmetrum.altosuilib_2; import java.awt.*; +import java.awt.event.*; import javax.swing.*; import java.io.*; import java.lang.Math; import java.awt.geom.Point2D; +import java.util.*; import java.util.concurrent.*; import org.altusmetrum.altoslib_4.*; -public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { +class MapPoint { + double lat, lon; + int state; + + public MapPoint(double lat, double lon, int state) { + this.lat = lat; + this.lon = lon; + this.state = state; + } + + public boolean equals(MapPoint other) { + if (other == null) + return false; + if (other.lat != lat) + return false; + if (other.lon != lon) + return false; + if (other.state != state) + return false; + return true; + } +} + +public class AltosSiteMap extends JComponent implements AltosFlightDisplay { // preferred vertical step in a tile in naut. miles // will actually choose a step size between x and 2x, where this // is 1.5x @@ -35,6 +60,29 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { static final int MAX_TILE_DELTA = 100; + static final int maptype_hybrid = 0; + static final int maptype_roadmap = 1; + static final int maptype_satellite = 2; + static final int maptype_terrain = 3; + + int maptype = maptype_hybrid; + + static final String[] maptype_names = { + "hybrid", + "roadmap", + "satellite", + "terrain" + }; + + static final String[] maptype_labels = { + "Hybrid", + "Roadmap", + "Satellite", + "Terrain" + }; + + LinkedList points = new LinkedList(); + private static Point2D.Double translatePoint(Point2D.Double p, Point2D.Double d) { @@ -89,7 +137,12 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { return new LatLng(lat,lng); } - int zoom; + static final int default_zoom = 15; + static final int min_zoom = 3; + static final int max_zoom = 21; + + int zoom = default_zoom; + double scale_x, scale_y; int radius; /* half width/height of tiles to load */ @@ -112,7 +165,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { private Point2D.Double tileCoordOffset(Point p) { return new Point2D.Double(centre.x - p.x*px_size, - centre.y - p.y * px_size); + centre.y - p.y*px_size); } private Point tileOffset(Point2D.Double p) { @@ -121,19 +174,11 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } 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*4/3/60.0, lng); - if (locn.y - north_step.y > px_size) - break; - } while (zoom < 22); + Point2D.Double locn = pt(0,0), north_step; + + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + locn = pt(lat, lng); locn.x = -px_size * Math.floor(locn.x/px_size); locn.y = -px_size * Math.floor(locn.y/px_size); return locn; @@ -144,23 +189,38 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } public void set_font() { - // nothing + for (AltosSiteMapTile tile : mapTiles.values()) + tile.set_font(AltosUILib.value_font); } - private void loadMap(final AltosSiteMapTile tile, - final File pngfile, String pngurl) + static final int load_mode_cached = 1; + static final int load_mode_uncached = 2; + + private boolean load_map(final AltosSiteMapTile tile, + final File pngfile, String pngurl, + int load_mode) { - boolean loaded = AltosSiteMapCache.fetchMap(pngfile, pngurl); - if (loaded) { + boolean has_map = AltosSiteMapCache.has_map(pngfile, pngurl); + if ((load_mode & load_mode_uncached) == 0 && !has_map) + return false; + if ((load_mode & load_mode_cached) == 0 && has_map) + return false; + + tile.set_status(AltosSiteMapCache.loading); + int status = AltosSiteMapCache.fetch_map(pngfile, pngurl); + if (status == AltosSiteMapCache.success) { SwingUtilities.invokeLater(new Runnable() { public void run() { - tile.loadMap(pngfile); + tile.load_map(pngfile); } }); } else { - System.out.printf("# Failed to fetch file %s\n", pngfile); + tile.set_status(status); + System.out.printf("# Failed to fetch file %s (status %d)\n", pngfile, status); System.out.printf(" wget -O '%s' '%s'\n", pngfile, pngurl); + System.out.printf(" sleep 1\n"); } + return true; } @@ -177,11 +237,11 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { LatLng map_latlng = latlng( -centre.x + x*px_size + px_size/2, -centre.y + y*px_size + px_size/2); - prefetch.pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom); - prefetch.pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom); - if (prefetch.pngfile.exists()) { + prefetch.pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom, maptype_hybrid); + prefetch.pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom, maptype_hybrid); + if (AltosSiteMapCache.has_map(prefetch.pngfile, prefetch.pngurl)) { prefetch.result = 1; - } else if (AltosSiteMapCache.fetchMap(prefetch.pngfile, prefetch.pngurl)) { + } else if (AltosSiteMapCache.fetch_map(prefetch.pngfile, prefetch.pngurl) == AltosSiteMapCache.success) { prefetch.result = 0; } else { prefetch.result = -1; @@ -218,33 +278,45 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } } - public String initMap(Point offset) { + public File init_map(Point offset, int load_mode) { 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, zoom); - String pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom); - loadMap(tile, pngfile, pngurl); - return pngfile.toString(); + File pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom, maptype); + String pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom, maptype); + load_map(tile, pngfile, pngurl, load_mode); + return pngfile; } public void initAndFinishMapAsync (final AltosSiteMapTile tile, final Point offset) { Thread thread = new Thread() { public void run() { - initMap(offset); + init_map(offset, load_mode_cached|load_mode_uncached); finishTileLater(tile, offset); } }; thread.start(); } + double lat, lon; + boolean base_location_set = false; + + public void clear_base_location() { + base_location_set = false; + circle_set = false; + points = new LinkedList(); + for (AltosSiteMapTile tile : mapTiles.values()) + tile.clearMap(); + } + public void setBaseLocation(double lat, double lng) { - for (Point k : mapTiles.keySet()) { - AltosSiteMapTile tile = mapTiles.get(k); + this.lat = lat; + this.lon = lng; + base_location_set = true; + for (AltosSiteMapTile tile : mapTiles.values()) tile.clearMap(); - } centre = getBaseLocation(lat, lng); scrollRocketToVisible(pt(lat,lng)); @@ -256,28 +328,42 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { Thread thread = new Thread() { public void run() { for (Point k : mapTiles.keySet()) - initMap(k); + init_map(k, load_mode_cached); + for (Point k : mapTiles.keySet()) + init_map(k, load_mode_uncached); } }; thread.start(); } - private static File MapFile(double lat, double lng, int zoom) { + private static File MapFile(double lat, double lng, int zoom, int maptype) { char chlat = lat < 0 ? 'S' : 'N'; char chlng = lng < 0 ? 'W' : 'E'; if (lat < 0) lat = -lat; if (lng < 0) lng = -lng; + String maptype_string = String.format("%s-", maptype_names[maptype]); + String format_string; + if (maptype == maptype_hybrid || maptype == maptype_satellite || maptype == maptype_terrain) + format_string = "jpg"; + else + format_string = "png"; return new File(AltosUIPreferences.mapdir(), - String.format("map-%c%.6f,%c%.6f-%d.png", - chlat, lat, chlng, lng, zoom)); + String.format("map-%c%.6f,%c%.6f-%s%d.%s", + chlat, lat, chlng, lng, maptype_string, zoom, format_string)); } - 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); + private static String MapURL(double lat, double lng, int zoom, int maptype) { + String format_string; + if (maptype == maptype_hybrid || maptype == maptype_satellite || maptype == maptype_terrain) + format_string = "jpg"; + else + format_string = "png32"; + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s", + lat, lng, zoom, px_size, px_size, maptype_names[maptype], format_string); } boolean initialised = false; - Point2D.Double last_pt = null; + MapPoint last_point = null; int last_state = -1; public void show(double lat, double lon) { @@ -286,42 +372,44 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { // initMaps(lat, lon); // scrollRocketToVisible(pt(lat, lon)); } - public void show(final AltosState state, final AltosListenerState listener_state) { - // if insufficient gps data, nothing to update - AltosGPS gps = state.gps; - if (gps == null) - return; + JLabel zoom_label; - if (!gps.locked && gps.nsat < 4) - return; + public void set_zoom_label() { + zoom_label.setText(String.format("- %d -", zoom - default_zoom)); + } - if (!initialised) { - if (state.pad_lat != AltosLib.MISSING && state.pad_lon != AltosLib.MISSING) { - initMaps(state.pad_lat, state.pad_lon); - initialised = true; - } else if (gps.lat != AltosLib.MISSING && gps.lon != AltosLib.MISSING) { - initMaps(gps.lat, gps.lon); - initialised = true; - } else { - return; - } + public void set_zoom(int zoom) { + if (min_zoom <= zoom && zoom <= max_zoom) { + this.zoom = zoom; + if (base_location_set) + initMaps(lat, lon); + redraw(); + set_zoom_label(); } + } - final Point2D.Double pt = pt(gps.lat, gps.lon); - if (last_pt == pt && last_state == state.state) - return; + public int get_zoom() { + return zoom; + } - if (last_pt == null) { - last_pt = pt; + public void draw(MapPoint last_point, MapPoint point) { + boolean force_ensure = false; + if (last_point == null) { + force_ensure = true; + last_point = point; } + + Point2D.Double pt = pt(point.lat, point.lon); + Point2D.Double last_pt = pt(last_point.lat, last_point.lon); + boolean in_any = false; for (Point offset : mapTiles.keySet()) { AltosSiteMapTile tile = mapTiles.get(offset); Point2D.Double ref, lref; ref = translatePoint(pt, tileCoordOffset(offset)); lref = translatePoint(last_pt, tileCoordOffset(offset)); - tile.show(state, listener_state, lref, ref); + tile.show(point.state, lref, ref); if (0 <= ref.x && ref.x < px_size) if (0 <= ref.y && ref.y < px_size) in_any = true; @@ -334,18 +422,60 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { lref = translatePoint(last_pt, tileCoordOffset(offset)); AltosSiteMapTile tile = createTile(offset); - tile.show(state, listener_state, lref, ref); + tile.show(point.state, lref, ref); initAndFinishMapAsync(tile, offset); } scrollRocketToVisible(pt); - if (offset != tileOffset(last_pt)) { + if (force_ensure || offset != tileOffset(last_pt)) { ensureTilesAround(offset); } + } + + public void redraw() { + MapPoint last_point = null; + + for (MapPoint point : points) { + draw(last_point, point); + last_point = point; + } + if (circle_set) + draw_circle(circle_lat, circle_lon); + } + + public void show(final AltosState state, final AltosListenerState listener_state) { + // if insufficient gps data, nothing to update + AltosGPS gps = state.gps; + + if (gps == null) + return; + + if (!gps.locked && gps.nsat < 4) + return; + + if (!initialised) { + if (state.pad_lat != AltosLib.MISSING && state.pad_lon != AltosLib.MISSING) { + initMaps(state.pad_lat, state.pad_lon); + initialised = true; + } else if (gps.lat != AltosLib.MISSING && gps.lon != AltosLib.MISSING) { + initMaps(gps.lat, gps.lon); + initialised = true; + } else { + return; + } + } - last_pt = pt; - last_state = state.state; + MapPoint point = new MapPoint(gps.lat, gps.lon, state.state); + + if (point.equals(last_point)) + return; + + points.add(point); + + draw(last_point, point); + + last_point = point; } public void centre(Point2D.Double pt) { @@ -355,6 +485,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { int dy = (int)copt.y - r.height/2 - r.y; r.x += dx; r.y += dy; + r.width = 1; + r.height = 1; comp.scrollRectToVisible(r); } @@ -364,8 +496,15 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { centre(pt(state.gps.lat, state.gps.lon)); } + private double circle_lat, circle_lon; + private boolean circle_set = false; + public void draw_circle(double lat, double lon) { - final Point2D.Double pt = pt(lat, lon); + circle_lat = lat; + circle_lon = lon; + circle_set = true; + + Point2D.Double pt = pt(lat, lon); for (Point offset : mapTiles.keySet()) { AltosSiteMapTile tile = mapTiles.get(offset); @@ -376,6 +515,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { private AltosSiteMapTile createTile(Point offset) { AltosSiteMapTile tile = new AltosSiteMapTile(px_size); + tile.set_font(AltosUILib.value_font); mapTiles.put(offset, tile); return tile; } @@ -405,6 +545,7 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { private void scrollRocketToVisible(Point2D.Double pt) { Rectangle r = comp.getVisibleRect(); Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft)); + int dx = (int)copt.x - r.width/2 - r.x; int dy = (int)copt.y - r.height/2 - r.y; if (Math.abs(dx) > r.width/4 || Math.abs(dy) > r.height/4) { @@ -446,9 +587,9 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { layout.setConstraints(tile, c); comp.add(tile); - if (review) { - comp.scrollRectToVisible(r); - } +// if (review) { +// comp.scrollRectToVisible(r); +// } } private AltosSiteMap(boolean knowWhatYouAreDoing) { @@ -460,6 +601,8 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { JComponent comp = new JComponent() { }; private GridBagLayout layout = new GridBagLayout(); + JScrollPane pane = new JScrollPane(); + public AltosSiteMap(int in_radius) { radius = in_radius; @@ -474,8 +617,108 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { addTileAt(t, offset); } } - setViewportView(comp); - setPreferredSize(new Dimension(500,500)); + pane.setViewportView(comp); + pane.setPreferredSize(new Dimension(500,500)); + pane.setVisible(true); + pane.setEnabled(true); + + GridBagLayout my_layout = new GridBagLayout(); + + setLayout(my_layout); + + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 10; + c.weightx = 1; + c.weighty = 1; + add(pane, c); + + int y = 0; + + zoom_label = new JLabel("", JLabel.CENTER); + set_zoom_label(); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_label, c); + + JButton zoom_reset = new JButton("0"); + zoom_reset.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + set_zoom(default_zoom); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_reset, c); + + JButton zoom_in = new JButton("+"); + zoom_in.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + set_zoom(get_zoom() + 1); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_in, c); + + JButton zoom_out = new JButton("-"); + zoom_out.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + set_zoom(get_zoom() - 1); + } + }); + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_out, c); + + final JComboBox maptype_combo = new JComboBox(maptype_labels); + + maptype_combo.setEditable(false); + maptype_combo.setMaximumRowCount(maptype_combo.getItemCount()); + maptype_combo.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + maptype = maptype_combo.getSelectedIndex(); + if (base_location_set) + initMaps(lat, lon); + redraw(); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(maptype_combo, c); } public AltosSiteMap() { diff --git a/altosuilib/AltosSiteMapCache.java b/altosuilib/AltosSiteMapCache.java index cf93016a..acc84ab0 100644 --- a/altosuilib/AltosSiteMapCache.java +++ b/altosuilib/AltosSiteMapCache.java @@ -22,9 +22,7 @@ import javax.imageio.ImageIO; import java.awt.image.*; import java.awt.*; import java.io.*; -import java.net.URL; -import java.net.URLConnection; - +import java.net.*; class AltosCacheImage { Component component; @@ -36,9 +34,12 @@ class AltosCacheImage { public void load_image() throws IOException { BufferedImage bimg = ImageIO.read(file); + if (bimg == null) + throw new IOException("Can't load image file"); Graphics2D g = image.createGraphics(); g.drawImage(bimg, 0, 0, null); bimg.flush(); + bimg = null; } public Image validate() { @@ -79,14 +80,31 @@ class AltosCacheImage { } } -public class AltosSiteMapCache extends JLabel { +public class AltosSiteMapCache { static final long google_maps_ratelimit_ms = 1200; // Google limits static map queries to 50 per minute per IP, so // each query should take at least 1.2 seconds. - public static boolean fetchMap(File file, String url) { + static final int success = 0; + static final int loading = 1; + static final int failed = 2; + static final int bad_request = 3; + static final int forbidden = 4; + + public static synchronized boolean has_map(File file, String url) { + return file.exists(); + } + + static long forbidden_time; + static boolean forbidden_set = false; + static final long forbidden_interval = 60l * 1000l * 1000l * 1000l; + + public static synchronized int fetch_map(File file, String url) { if (file.exists()) - return true; + return success; + + if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) + return forbidden; URL u; long startTime = System.nanoTime(); @@ -94,13 +112,26 @@ public class AltosSiteMapCache extends JLabel { try { u = new URL(url); } catch (java.net.MalformedURLException e) { - return false; + return bad_request; } byte[] data; + URLConnection uc = null; try { - URLConnection uc = u.openConnection(); + uc = u.openConnection(); + String type = uc.getContentType(); int contentLength = uc.getContentLength(); + if (uc instanceof HttpURLConnection) { + int response = ((HttpURLConnection) uc).getResponseCode(); + switch (response) { + case HttpURLConnection.HTTP_FORBIDDEN: + case HttpURLConnection.HTTP_PAYMENT_REQUIRED: + case HttpURLConnection.HTTP_UNAUTHORIZED: + forbidden_time = System.nanoTime(); + forbidden_set = true; + return forbidden; + } + } InputStream in = new BufferedInputStream(uc.getInputStream()); int bytesRead = 0; int offset = 0; @@ -113,11 +144,11 @@ public class AltosSiteMapCache extends JLabel { } in.close(); - if (offset != contentLength) { - return false; - } + if (offset != contentLength) + return failed; + } catch (IOException e) { - return false; + return failed; } try { @@ -126,12 +157,11 @@ public class AltosSiteMapCache extends JLabel { out.flush(); out.close(); } catch (FileNotFoundException e) { - return false; + return bad_request; } catch (IOException e) { - if (file.exists()) { + if (file.exists()) file.delete(); - } - return false; + return bad_request; } long duration_ms = (System.nanoTime() - startTime) / 1000000; @@ -143,21 +173,16 @@ public class AltosSiteMapCache extends JLabel { } } - return true; + return success; } - static int cache_size = 9; + static final int cache_size = 12; static AltosCacheImage[] images; static long used; - public static void set_cache_size(int cache_size) { - AltosSiteMapCache.cache_size = cache_size; - images = null; - } - - public static Image get_image(Component component, File file, int width, int height) { + public static synchronized Image get_image(Component component, File file, int width, int height) { int oldest = -1; long age = used; AltosCacheImage image; diff --git a/altosuilib/AltosSiteMapPreload.java b/altosuilib/AltosSiteMapPreload.java index baa7fc37..107b09f8 100644 --- a/altosuilib/AltosSiteMapPreload.java +++ b/altosuilib/AltosSiteMapPreload.java @@ -265,9 +265,10 @@ public class AltosSiteMapPreload extends AltosUIDialog implements ActionListener public void run() { 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)); + File pngfile; + pngfile = map.init_map(new Point(x,y), + AltosSiteMap.load_mode_cached|AltosSiteMap.load_mode_uncached); + SwingUtilities.invokeLater(new updatePbar(x, y, pngfile.toString())); } } } @@ -305,6 +306,7 @@ public class AltosSiteMapPreload extends AltosUIDialog implements ActionListener try { final double latitude = lat.get_value(); final double longitude = lon.get_value(); + map.clear_base_location(); map.setBaseLocation(latitude,longitude); map.draw_circle(latitude,longitude); loading = true; diff --git a/altosuilib/AltosSiteMapTile.java b/altosuilib/AltosSiteMapTile.java index 1046d6bd..9610b248 100644 --- a/altosuilib/AltosSiteMapTile.java +++ b/altosuilib/AltosSiteMapTile.java @@ -21,10 +21,10 @@ import java.awt.*; import java.awt.image.*; import javax.swing.*; import javax.imageio.*; -import java.awt.geom.Point2D; -import java.awt.geom.Line2D; +import java.awt.geom.*; import java.io.*; import java.util.*; +import java.awt.RenderingHints.*; import org.altusmetrum.altoslib_4.*; class AltosPoint { @@ -40,15 +40,38 @@ class AltosPoint { public class AltosSiteMapTile extends JComponent { int px_size; File file; + int status; Point2D.Double boost; Point2D.Double landed; LinkedList points; - public void loadMap(File pngFile) { + public synchronized void queue_repaint() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + repaint(); + } + }); + } + + public void load_map(File pngFile) { file = pngFile; - repaint(); + queue_repaint(); + } + + private Font font = null; + + public void set_font(Font font) { + this.font = font; + this.status = AltosSiteMapCache.success; + queue_repaint(); + } + + public void set_status(int status) { + file = null; + this.status = status; + queue_repaint(); } public void clearMap() { @@ -56,6 +79,8 @@ public class AltosSiteMapTile extends JComponent { landed = null; points = null; file = null; + status = AltosSiteMapCache.success; + queue_repaint(); } static Color stateColors[] = { @@ -78,7 +103,7 @@ public class AltosSiteMapTile extends JComponent { public void set_boost(Point2D.Double boost) { this.boost = boost; - repaint(); + queue_repaint(); } public void paint(Graphics g) { @@ -94,6 +119,34 @@ public class AltosSiteMapTile extends JComponent { } else { g2d.setColor(Color.GRAY); g2d.fillRect(0, 0, getWidth(), getHeight()); + String message = null; + switch (status) { + case AltosSiteMapCache.loading: + message = "Loading..."; + break; + case AltosSiteMapCache.bad_request: + message = "Internal error"; + break; + case AltosSiteMapCache.failed: + message = "Network error, check connection"; + break; + case AltosSiteMapCache.forbidden: + message = "Too many requests, try later"; + break; + } + if (message != null && font != null) { + g2d.setFont(font); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + Rectangle2D bounds; + bounds = font.getStringBounds(message, g2d.getFontRenderContext()); + + float x = getWidth() / 2.0f; + float y = getHeight() / 2.0f; + x = x - (float) bounds.getWidth() / 2.0f; + y = y - (float) bounds.getHeight() / 2.0f; + g2d.setColor(Color.BLACK); + g2d.drawString(message, x, y); + } } g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, @@ -120,19 +173,18 @@ public class AltosSiteMapTile extends JComponent { } } - public synchronized void show(AltosState state, AltosListenerState listener_state, - Point2D.Double last_pt, Point2D.Double pt) + public synchronized void show(int state, Point2D.Double last_pt, Point2D.Double pt) { if (points == null) points = new LinkedList(); - points.add(new AltosPoint(pt, state.state)); + points.add(new AltosPoint(pt, state)); - if (state.state == AltosLib.ao_flight_boost && boost == null) + if (state == AltosLib.ao_flight_boost && boost == null) boost = pt; - if (state.state == AltosLib.ao_flight_landed && landed == null) + if (state == AltosLib.ao_flight_landed && landed == null) landed = pt; - repaint(); + queue_repaint(); } public AltosSiteMapTile(int in_px_size) { -- 2.30.2