diff options
| -rw-r--r-- | altosuilib/AltosSiteMap.java | 64 | ||||
| -rw-r--r-- | altosuilib/AltosSiteMapCache.java | 304 | ||||
| -rw-r--r-- | altosuilib/AltosSiteMapImage.java | 82 | ||||
| -rw-r--r-- | altosuilib/AltosSiteMapTile.java | 70 | ||||
| -rw-r--r-- | altosuilib/Makefile.am | 1 | 
5 files changed, 340 insertions, 181 deletions
| diff --git a/altosuilib/AltosSiteMap.java b/altosuilib/AltosSiteMap.java index f22d9531..ee9b8c05 100644 --- a/altosuilib/AltosSiteMap.java +++ b/altosuilib/AltosSiteMap.java @@ -50,7 +50,7 @@ class MapPoint {  	}  } -public class AltosSiteMap extends JComponent implements AltosFlightDisplay, MouseMotionListener, MouseListener { +public class AltosSiteMap extends JComponent implements AltosFlightDisplay, MouseMotionListener, MouseListener, HierarchyBoundsListener {  	// preferred vertical step in a tile in naut. miles  	// will actually choose a step size between x and 2x, where this  	// is 1.5x @@ -213,11 +213,15 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  		tile.set_status(AltosSiteMapCache.loading);  		int status = AltosSiteMapCache.fetch_map(pngfile, pngurl);  		if (status == AltosSiteMapCache.success) { -			SwingUtilities.invokeLater(new Runnable() { -					public void run() { -						tile.load_map(pngfile); -					} -				}); +			if (SwingUtilities.isEventDispatchThread()) +				tile.load_map(pngfile); +			else { +				SwingUtilities.invokeLater(new Runnable() { +						public void run() { +							tile.load_map(pngfile); +						} +					}); +			}  		} else {  			tile.set_status(status);  			System.out.printf("# Failed to fetch file %s (status %d)\n", pngfile, status); @@ -298,7 +302,11 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  		Thread thread = new Thread() {  				public void run() {  					init_map(offset, load_mode_cached|load_mode_uncached); -					finishTileLater(tile, offset); +					SwingUtilities.invokeLater( new Runnable() { +							public void run() { +								addTileAt(tile, offset); +							} +						} );  				}  			};  		thread.start(); @@ -315,8 +323,6 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  	}  	public void setBaseLocation(double lat, double lng) { -		for (AltosSiteMapTile tile : mapTiles.values()) -			tile.clearMap();  		this.lat = lat;  		this.lon = lng;  		base_location_set = true; @@ -328,6 +334,8 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  	private void initMaps(double lat, double lng) {  		setBaseLocation(lat, lng); +		for (AltosSiteMapTile tile : mapTiles.values()) +			tile.clearMap();  		Thread thread = new Thread() {  				public void run() {  					for (Point k : mapTiles.keySet()) @@ -379,7 +387,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  	JLabel	zoom_label;  	public void set_zoom_label() { -		zoom_label.setText(String.format("- %d -", zoom - default_zoom)); +		zoom_label.setText(String.format("Zoom %d", zoom - default_zoom));  	}  	public void set_zoom(int zoom) { @@ -524,15 +532,6 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  		mapTiles.put(offset, tile);  		return tile;  	} -	private void finishTileLater(final AltosSiteMapTile tile, -				     final Point offset) -	{ -		SwingUtilities.invokeLater( new Runnable() { -			public void run() { -				addTileAt(tile, offset); -			} -		} ); -	}  	private void ensureTilesAround(Point base_offset) {  		for (int x = -radius; x <= radius; x++) { @@ -629,6 +628,14 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  		}  	} +	static void debug_component(Component who, String where) { +//		Rectangle	r = who.getBounds(); +//		int		x = r.x / px_size; +//		int		y = r.y / px_size; +// +//		System.out.printf ("%3d, %3d: %s\n", x, y, where); +	} +  	LatLng latlng(MouseEvent e) {  		if (!base_location_set)  			return null; @@ -672,6 +679,24 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  	public void mouseReleased(MouseEvent e) {  	} +	public void set_cache_size() { +		Rectangle	r = comp.getVisibleRect(); + +		int	width_tiles = (r.width + 2*px_size) / px_size; +		int	height_tiles = (r.height + 2*px_size) / px_size; +		int	tiles = width_tiles * height_tiles; +		AltosSiteMapCache.set_cache_size(tiles); +	} + +	/* HierarchyBoundsListener methods */ +	public void ancestorMoved(HierarchyEvent e) { +		set_cache_size(); +	} + +	public void ancestorResized(HierarchyEvent e) { +		set_cache_size(); +	} +  	JScrollPane	pane = new JScrollPane();  	public AltosSiteMap(int in_radius) { @@ -681,6 +706,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous  		comp.addMouseMotionListener(this);  		comp.addMouseListener(this); +		comp.addHierarchyBoundsListener(this);  		GrabNDrag scroller = new GrabNDrag(comp); diff --git a/altosuilib/AltosSiteMapCache.java b/altosuilib/AltosSiteMapCache.java index 42914deb..6e6046bc 100644 --- a/altosuilib/AltosSiteMapCache.java +++ b/altosuilib/AltosSiteMapCache.java @@ -24,62 +24,6 @@ import java.awt.*;  import java.io.*;  import java.net.*; -class AltosCacheImage { -	Component	component; -	File		file; -	VolatileImage	image; -	int		width; -	int		height; -	long		used; - -	public void load_image() throws IOException { -		BufferedImage	bimg = ImageIO.read(file); -		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() { -		int	returnCode; - -		if (image != null) -			returnCode = image.validate(component.getGraphicsConfiguration()); -		else -			returnCode = VolatileImage.IMAGE_INCOMPATIBLE; -		if (returnCode == VolatileImage.IMAGE_RESTORED) { -			try { -				load_image(); -			} catch (IOException e) { -				return null; -			} -		} else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) { -			image = component.createVolatileImage(width, height); -			try { -				load_image(); -			} catch (IOException e) { -				return null; -			} -		} -		return image; -	} - -	public void flush() { -		image.flush(); -	} - -	public AltosCacheImage(Component component, File file, int w, int h) throws IOException { -		this.component = component; -		this.file = file; -		width = w; -		height = h; -		image = component.createVolatileImage(w, h); -		used = 0; -	} -} -  public class AltosSiteMapCache {  	static final long google_maps_ratelimit_ms = 1200;  	// Google limits static map queries to 50 per minute per IP, so @@ -99,121 +43,197 @@ public class AltosSiteMapCache {  	static boolean	forbidden_set = false;  	static final long	forbidden_interval = 60l * 1000l * 1000l * 1000l; -	public static synchronized int fetch_map(File file, String url) { +	static private Object fetch_lock = new Object(); + +	public static int fetch_map(File file, String url) {  		if (file.exists())  			return success;  		if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval)  			return forbidden; -		URL u; -		long startTime = System.nanoTime(); +		synchronized (fetch_lock) { +			URL u; +			long startTime = System.nanoTime(); -		try { -			u = new URL(url); -		} catch (java.net.MalformedURLException e) { -			return bad_request; -		} +			try { +				u = new URL(url); +			} catch (java.net.MalformedURLException e) { +				return bad_request; +			} -		byte[] data; -		URLConnection uc = null; -		try { -			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; +			byte[] data; +			URLConnection uc = null; +			try { +				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; -			data = new byte[contentLength]; -			while (offset < contentLength) { -				bytesRead = in.read(data, offset, data.length - offset); -				if (bytesRead == -1) -					break; -				offset += bytesRead; -			} -			in.close(); +				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) +				if (offset != contentLength) +					return failed; + +			} catch (IOException e) {  				return failed; +			} -		} catch (IOException e) { -			return failed; -		} +			try { +				FileOutputStream out = new FileOutputStream(file); +				out.write(data); +				out.flush(); +				out.close(); +			} catch (FileNotFoundException e) { +				return bad_request; +			} catch (IOException e) { +				if (file.exists()) +					file.delete(); +				return bad_request; +			} -		try { -			FileOutputStream out = new FileOutputStream(file); -			out.write(data); -			out.flush(); -			out.close(); -		} catch (FileNotFoundException e) { -			return bad_request; -		} catch (IOException e) { -			if (file.exists()) -				file.delete(); -			return bad_request; +			long duration_ms = (System.nanoTime() - startTime) / 1000000; +			if (duration_ms < google_maps_ratelimit_ms) { +				try { +					Thread.sleep(google_maps_ratelimit_ms - duration_ms); +				} catch (InterruptedException e) { +					Thread.currentThread().interrupt(); +				} +			} +			return success;  		} +	} -		long duration_ms = (System.nanoTime() - startTime) / 1000000; -		if (duration_ms < google_maps_ratelimit_ms) { -			try { -				Thread.sleep(google_maps_ratelimit_ms - duration_ms); -			} catch (InterruptedException e) { -				Thread.currentThread().interrupt(); +	static final int		min_cache_size = 9; +	static final int		max_cache_size = 24; + +	static int			cache_size = min_cache_size; + +	static AltosSiteMapImage[]	images = new AltosSiteMapImage[cache_size]; + +	static Object cache_lock = new Object(); + +	public  static void set_cache_size(int new_size) { +		if (new_size < min_cache_size) +			new_size = min_cache_size; +		if (new_size > max_cache_size) +			new_size = max_cache_size; +		if (new_size == cache_size) +			return; + +		synchronized(cache_lock) { +			AltosSiteMapImage[]	new_images = new AltosSiteMapImage[new_size]; + +			for (int i = 0; i < cache_size; i++) { +				if (i < new_size) +					new_images[i] = images[i]; +				else +					images[i].flush();  			} +			images = new_images; +			cache_size = new_size;  		} +	} -		return success; +	static long			used; + +	private static Point tile_loc(AltosSiteMapTile tile) { +		Rectangle	r = tile.getBounds(); +		int		x = r.x / 512; +		int		y = r.y / 512; + +		return new Point (x, y);  	} -	static final int		cache_size = 12; +	private static void dump_cache() { +		int	min_x = 1000, max_x = -1000, min_y = 1000, max_y = -1000; -	static AltosCacheImage[]	images; +		for (int i = 0; i < cache_size; i++) { +			AltosSiteMapImage	image = images[i]; +			if (image != null) { +				Point p = tile_loc(image.tile); +				min_x = min_x < p.x ? min_x : p.x; +				max_x = max_x > p.x ? max_x : p.x; +				min_y = min_y < p.y ? min_y : p.y; +				max_y = max_y > p.y ? max_y : p.y; +				System.out.printf ("entry %d %d,%d used %d\n", i, p.x, p.y, image.used); +			} else { +				System.out.printf ("entry %d empty\n", i); +			} +		} -	static long			used; +		int[][]	map = new int[max_x - min_x + 1][max_y - min_y + 1]; +		for (int i = 0; i < cache_size; i++) { +			AltosSiteMapImage	image = images[i]; +			if (image != null) { +				Point p = tile_loc(image.tile); +				map[p.x - min_x][p.y - min_y]++; +			} +		} -	public static Image get_image(Component component, File file, int width, int height) { +		for (int y = min_y; y <= max_y; y++) { +			for (int x = min_x; x <= max_x; x++) +				System.out.printf (" %2d", map[x - min_x][y - min_y]); +			System.out.printf("\n"); +		} +	} + +	public static AltosSiteMapImage get_image(AltosSiteMapTile tile, File file, int width, int height) {  		int		oldest = -1;  		long		age = used; -		AltosCacheImage	image; -		if (images == null) -			images = new AltosCacheImage[cache_size]; -		for (int i = 0; i < cache_size; i++) { -			image = images[i]; -			if (image == null) { -				oldest = i; -				break; +		synchronized(cache_lock) { +			AltosSiteMapImage	image = null; +			for (int i = 0; i < cache_size; i++) { +				image = images[i]; + +				if (image == null) { +					oldest = i; +					break; +				} +				if (image.tile == tile && file.equals(image.file)) { +					image.used = used++; +					return image; +				} +				if (image.used < age) { +					oldest = i; +					age = image.used; +				}  			} -			if (image.component == component && file.equals(image.file)) { + +			try { +				image = new AltosSiteMapImage(tile, file, width, height);  				image.used = used++; -				return image.validate(); -			} -			if (image.used < age) { -				oldest = i; -				age = image.used; +				if (images[oldest] != null) { +//					dump_cache(); +					AltosSiteMap.debug_component(images[oldest].tile, "replacing cache"); +					AltosSiteMap.debug_component(tile, "replaced cache"); +					images[oldest].flush(); +				} +				images[oldest] = image; +				return image; +			} catch (IOException e) { +				return null;  			}  		} - -		try { -			image = new AltosCacheImage(component, file, width, height); -			image.used = used++; -			if (images[oldest] != null) -				images[oldest].flush(); -			images[oldest] = image; -			return image.validate(); -		} catch (IOException e) { -			return null; -		}  	}  } diff --git a/altosuilib/AltosSiteMapImage.java b/altosuilib/AltosSiteMapImage.java new file mode 100644 index 00000000..f08c0b26 --- /dev/null +++ b/altosuilib/AltosSiteMapImage.java @@ -0,0 +1,82 @@ +/* + * Copyright © 2014 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_2; + +import java.awt.*; +import java.awt.image.*; +import javax.imageio.ImageIO; +import javax.swing.*; +import java.io.*; +import org.altusmetrum.altoslib_4.*; + +public class AltosSiteMapImage { +	AltosSiteMapTile	tile; +	File			file; +	BufferedImage		image; +	int			width; +	int			height; +	long			used; + +	Thread	load_thread; + +	public boolean validate() { +		if (image != null) { +			AltosSiteMap.debug_component(tile, "valid"); +			return true; +		} else { +			AltosSiteMap.debug_component(tile, "loading"); +			load_thread = new Thread() { +					public void run() { +						image = null; +						try { +							image = ImageIO.read(file); +						} catch (Exception e) { +						} +						SwingUtilities.invokeLater( new Runnable() { +								public void run() { +									AltosSiteMap.debug_component(tile, "later"); +									Graphics2D g2d = (Graphics2D) tile.getGraphics(); +									tile.paint_graphics(g2d, image); +									load_thread = null; +								} +							}); +					} +				}; +			load_thread.start(); +			return false; +		} +	} + +	public void flush() { +		if (load_thread == null) { +			AltosSiteMap.debug_component(tile, "flush"); +			image.flush(); +			image = null; +		} +	} + +	public AltosSiteMapImage (AltosSiteMapTile tile, File file, int w, int h) throws IOException { +		this.tile = tile; +		this.file = file; +		width = w; +		height = h; +		image = null; +		used = 0; +	} +} + diff --git a/altosuilib/AltosSiteMapTile.java b/altosuilib/AltosSiteMapTile.java index 09f184a3..136fbd7a 100644 --- a/altosuilib/AltosSiteMapTile.java +++ b/altosuilib/AltosSiteMapTile.java @@ -51,11 +51,15 @@ public class AltosSiteMapTile extends JComponent {  	LinkedList<AltosPoint>	points;  	public synchronized void queue_repaint() { -		SwingUtilities.invokeLater(new Runnable() { -				public void run() { -					repaint(); -				} -			}); +		if (SwingUtilities.isEventDispatchThread()) +			repaint(); +		else { +			SwingUtilities.invokeLater(new Runnable() { +					public void run() { +						repaint(); +					} +				}); +		}  	}  	public void load_map(File pngFile) { @@ -68,13 +72,14 @@ public class AltosSiteMapTile extends JComponent {  	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(); +		if (status != this.status || file != null) { +			file = null; +			this.status = status; +			queue_repaint(); +		}  	}  	public void clearMap() { @@ -83,7 +88,6 @@ public class AltosSiteMapTile extends JComponent {  		points = null;  		file = null;  		status = AltosSiteMapCache.success; -		queue_repaint();  		line = null;  	} @@ -151,17 +155,14 @@ public class AltosSiteMapTile extends JComponent {  		return String.format(format, distance);  	} -	public void paint(Graphics g) { -		Graphics2D	g2d = (Graphics2D) g; -		AltosPoint	prev = null; -		Image		img = null; +	boolean	painting; -		if (file != null) -			img = AltosSiteMapCache.get_image(this, file, px_size, px_size); - -		if (img != null) { -			g2d.drawImage(img, 0, 0, null); +	public void paint_graphics(Graphics2D g2d, Image image) { +		if (image != null) { +			AltosSiteMap.debug_component(this, "paint_graphics"); +			g2d.drawImage(image, 0, 0, null);  		} else { +			AltosSiteMap.debug_component(this, "erase_graphics");  			g2d.setColor(Color.GRAY);  			g2d.fillRect(0, 0, getWidth(), getHeight());  			String	message = null; @@ -199,6 +200,7 @@ public class AltosSiteMapTile extends JComponent {  		g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));  		if (points != null) { +			AltosPoint		prev = null;  			for (AltosPoint point : points) {  				if (prev != null) {  					if (0 <= point.state && point.state < stateColors.length) @@ -237,9 +239,37 @@ public class AltosSiteMapTile extends JComponent {  			}  			g2d.drawString(message, x, y);  		} +		painting = false; +	} + +	public void paint(Graphics g) { +		Graphics2D		g2d = (Graphics2D) g; +		Image			image = null; +		boolean			queued = false; + +		if (painting) { +			AltosSiteMap.debug_component(this, "already painting"); +			return; +		} +		AltosSiteMap.debug_component(this, "paint"); + +		if (file != null) { +			AltosSiteMapImage	aimage; + +			aimage = AltosSiteMapCache.get_image(this, file, px_size, px_size); +			if (aimage != null) { +				if (aimage.validate()) +					image = aimage.image; +				else +					queued = true; +			} +		} +		if (!queued) +			paint_graphics(g2d, image); +		painting = queued;  	} -	public synchronized void show(int state, Point2D.Double last_pt, Point2D.Double pt) +	public void show(int state, Point2D.Double last_pt, Point2D.Double pt)  	{  		if (points == null)  			points = new LinkedList<AltosPoint>(); diff --git a/altosuilib/Makefile.am b/altosuilib/Makefile.am index b7d624e2..10b756b8 100644 --- a/altosuilib/Makefile.am +++ b/altosuilib/Makefile.am @@ -37,6 +37,7 @@ altosuilib_JAVA = \  	AltosSiteMapCache.java \  	AltosSiteMapPreload.java \  	AltosSiteMapTile.java \ +	AltosSiteMapImage.java \  	AltosVoice.java \  	AltosDisplayThread.java \  	AltosFreqList.java | 
