X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=src%2Ftim%2Fprune%2Fgui%2Fmap%2FMapTileManager.java;fp=src%2Ftim%2Fprune%2Fgui%2Fmap%2FMapTileManager.java;h=f7370c5c90c833e7f053fc786eafdae37ab7f843;hp=0000000000000000000000000000000000000000;hb=ce6f2161b8596f7018d6a76bff79bc9e571f35fd;hpb=2d8cb72e84d5cc1089ce77baf1e34ea3ea2f8465 diff --git a/src/tim/prune/gui/map/MapTileManager.java b/src/tim/prune/gui/map/MapTileManager.java new file mode 100644 index 0000000..f7370c5 --- /dev/null +++ b/src/tim/prune/gui/map/MapTileManager.java @@ -0,0 +1,261 @@ +package tim.prune.gui.map; + +import java.awt.Image; +import java.awt.image.ImageObserver; +import java.net.MalformedURLException; +import java.net.URL; + +import tim.prune.config.Config; + + +/** + * Class responsible for managing the map tiles, + * including invoking the correct memory cacher(s) and/or disk cacher(s) + */ +public class MapTileManager implements ImageObserver +{ + /** Consumer object to inform when tiles received */ + private TileConsumer _consumer = null; + /** Current map source */ + private MapSource _mapSource = null; + /** Array of tile caches, one per layer */ + private MemTileCacher[] _tempCaches = null; + /** Flag for whether to download any tiles or just pull from disk */ + private boolean _downloadTiles = true; + /** Flag for whether to return incomplete images or just pass to tile cache until they're done */ + private boolean _returnIncompleteImages = false; + /** Number of layers */ + private int _numLayers = -1; + /** Current zoom level */ + private int _zoom = 0; + /** Number of tiles in each direction for this zoom level */ + private int _numTileIndices = 1; + + + /** + * Constructor + * @param inConsumer consumer object to be notified + */ + public MapTileManager(TileConsumer inConsumer) + { + _consumer = inConsumer; + } + + /** + * Recentre the map + * @param inZoom zoom level + * @param inTileX x coord of central tile + * @param inTileY y coord of central tile + */ + public void centreMap(int inZoom, int inTileX, int inTileY) + { + setZoom(inZoom); + // Pass params onto all memory cachers + if (_tempCaches != null) { + for (int i=0; i<_tempCaches.length; i++) { + _tempCaches[i].centreMap(inZoom, inTileX, inTileY); + } + } + } + + /** @param inZoom zoom level to set */ + public void setZoom(int inZoom) + { + _zoom = inZoom; + // Calculate number of tiles = 2^^zoom + _numTileIndices = 1 << _zoom; + } + + /** + * @return true if zoom is too high for tiles + */ + public boolean isOverzoomed() + { + // Ask current map source what maximum zoom is + int maxZoom = (_mapSource == null?0:_mapSource.getMaxZoomLevel()); + return (_zoom > maxZoom); + } + + /** + * Enable or disable tile downloading + * @param inEnabled true to enable downloading, false to just get tiles from disk + */ + public void enableTileDownloading(boolean inEnabled) + { + _downloadTiles = inEnabled; + } + + /** Configure to return incomplete images instead of going via caches (and another call) */ + public void setReturnIncompleteImages() + { + _returnIncompleteImages = true; + } + + /** + * Clear all the memory caches due to changed config / zoom + */ + public void clearMemoryCaches() + { + int numLayers = _mapSource.getNumLayers(); + if (_tempCaches == null || _tempCaches.length != numLayers) + { + // Cachers don't match, so need to create the right number of them + _tempCaches = new MemTileCacher[numLayers]; + for (int i=0; i= _numTileIndices) return null; + // Wrap tile indices which are too big or too small + inX = ((inX % _numTileIndices) + _numTileIndices) % _numTileIndices; + + // Check first in memory cache for tile + Image tileImage = null; + MemTileCacher tempCache = null; + if (_tempCaches != null) + { + tempCache = _tempCaches[inLayer]; // Should probably guard array indexes here + tileImage = tempCache.getTile(inX, inY); + if (tileImage != null) { + return tileImage; + } + } + + // Tile wasn't in memory, but maybe it's in disk cache (if there is one) + String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE); + boolean useDisk = (diskCachePath != null); + boolean onlineMode = Config.getConfigBoolean(Config.KEY_ONLINE_MODE); + MapTile mapTile = null; + if (useDisk) + { + // Get the map tile from cache + mapTile = DiskTileCacher.getTile(diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY)); + if (mapTile != null && mapTile.getImage() != null) + { + tileImage = mapTile.getImage(); + if (_returnIncompleteImages) {return tileImage;} + // Pass tile to memory cache + if (tempCache != null) { + tempCache.setTile(tileImage, inX, inY, _zoom); + } + tileImage.getWidth(this); // trigger the load from file + } + } + // Maybe we've got an image now, maybe it's expired + final boolean shouldDownload = (tileImage == null || mapTile == null || mapTile.isExpired()); + + // If we're online then try to download the tile + if (onlineMode && _downloadTiles && inDownloadIfNecessary && shouldDownload) + { + try + { + URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY)); + //System.out.println("Trying to fetch: " + tileUrl); + if (useDisk) + { + DiskTileCacher.saveTile(tileUrl, diskCachePath, + _mapSource.makeFilePath(inLayer, _zoom, inX, inY), this); + // Image will now be copied directly from URL stream to disk cache + } + else + { + // Load image asynchronously, using observer + // In order to set the http user agent, need to use a TileDownloader instead + TileDownloader.triggerLoad(this, tileUrl, inLayer, inX, inY, _zoom); + } + } + catch (MalformedURLException urle) {} // ignore + } + return tileImage; + } + + /** + * Method called by image loader to inform of updates to the tiles + * @param img the image + * @param infoflags flags describing how much of the image is known + * @param x ignored + * @param y ignored + * @param width ignored + * @param height ignored + * @return false to carry on loading, true to stop + */ + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) + { + boolean loaded = (infoflags & ImageObserver.ALLBITS) > 0; + boolean error = (infoflags & ImageObserver.ERROR) > 0; + if (loaded || error) { + _consumer.tilesUpdated(loaded); + } + return !loaded; + } + + /** + * Callback method from TileDownloader to let us know that an image has been loaded + * @param inTile Loaded Image object + * @param inLayer layer index from 0 + * @param inX x coordinate of tile + * @param inY y coordinate of tile + * @param inZoom zoom level of loaded image + */ + public void notifyImageLoaded(Image inTile, int inLayer, int inX, int inY, int inZoom) + { + if (inTile != null && _tempCaches != null) + { + MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here + if (tempCache.getTile(inX, inY) == null) + { + // Check with cache that the zoom level is still valid + tempCache.setTile(inTile, inX, inY, inZoom); + inTile.getWidth(this); // trigger imageUpdate when image is ready + } + } + else if (inTile != null) { + inTile.getWidth(this); + } + } +}