diff options
| -rw-r--r-- | altosuilib/AltosSiteMap.java | 395 | ||||
| -rw-r--r-- | altosuilib/AltosSiteMapCache.java | 73 | ||||
| -rw-r--r-- | altosuilib/AltosSiteMapPreload.java | 8 | ||||
| -rw-r--r-- | 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<MapPoint> points = new LinkedList<MapPoint>(); +  	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<MapPoint>(); +		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<String>	maptype_combo = new JComboBox<String>(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<AltosPoint>	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<AltosPoint>(); -		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) { | 
