1 package tim.prune.gui.map;
4 import java.awt.image.ImageObserver;
5 import java.net.MalformedURLException;
8 import tim.prune.config.Config;
12 * Class responsible for managing the map tiles,
13 * including invoking the correct memory cacher(s) and/or disk cacher(s)
15 public class MapTileManager implements ImageObserver
17 /** Consumer object to inform when tiles received */
18 private TileConsumer _consumer = null;
19 /** Current map source */
20 private MapSource _mapSource = null;
21 /** Array of tile caches, one per layer */
22 private MemTileCacher[] _tempCaches = null;
23 /** Flag for whether to download any tiles or just pull from disk */
24 private boolean _downloadTiles = true;
25 /** Flag for whether to return incomplete images or just pass to tile cache until they're done */
26 private boolean _returnIncompleteImages = false;
27 /** Number of layers */
28 private int _numLayers = -1;
29 /** Current zoom level */
30 private int _zoom = 0;
31 /** Number of tiles in each direction for this zoom level */
32 private int _numTileIndices = 1;
37 * @param inConsumer consumer object to be notified
39 public MapTileManager(TileConsumer inConsumer)
41 _consumer = inConsumer;
46 * @param inZoom zoom level
47 * @param inTileX x coord of central tile
48 * @param inTileY y coord of central tile
50 public void centreMap(int inZoom, int inTileX, int inTileY)
53 // Pass params onto all memory cachers
54 if (_tempCaches != null) {
55 for (int i=0; i<_tempCaches.length; i++) {
56 _tempCaches[i].centreMap(inZoom, inTileX, inTileY);
61 /** @param inZoom zoom level to set */
62 public void setZoom(int inZoom)
65 // Calculate number of tiles = 2^^zoom
66 _numTileIndices = 1 << _zoom;
70 * @return true if zoom is too high for tiles
72 public boolean isOverzoomed()
74 return _zoom > getMaxZoomLevel();
78 * @return the maximum useable zoom level for tiles
80 public int getMaxZoomLevel()
82 // Ask current map source what maximum zoom is
83 int maxZoom = (_mapSource == null?0:_mapSource.getMaxZoomLevel());
89 * Enable or disable tile downloading
90 * @param inEnabled true to enable downloading, false to just get tiles from disk
92 public void enableTileDownloading(boolean inEnabled)
94 _downloadTiles = inEnabled;
97 /** Configure to return incomplete images instead of going via caches (and another call) */
98 public void setReturnIncompleteImages()
100 _returnIncompleteImages = true;
104 * Clear all the memory caches due to changed config / zoom
106 public void clearMemoryCaches()
108 int numLayers = _mapSource.getNumLayers();
109 if (_tempCaches == null || _tempCaches.length != numLayers)
111 // Cachers don't match, so need to create the right number of them
112 _tempCaches = new MemTileCacher[numLayers];
113 for (int i=0; i<numLayers; i++) {
114 _tempCaches[i] = new MemTileCacher();
118 // Cachers already there, just need to be cleared
119 for (int i=0; i<numLayers; i++) {
120 _tempCaches[i].clearAll();
126 * @param inSourceNum selected map source index
128 public void setMapSource(int inSourceNum)
130 setMapSource(MapSourceLibrary.getSource(inSourceNum));
134 * @param inMapSource selected map source
136 public void setMapSource(MapSource inMapSource)
138 _mapSource = inMapSource;
139 if (_mapSource == null) {_mapSource = MapSourceLibrary.getSource(0);}
141 _numLayers = _mapSource.getNumLayers();
145 * @return the number of layers in the map
147 public int getNumLayers()
153 * Get a tile from the currently selected map source
154 * @param inLayer layer number, starting from 0
155 * @param inX x index of tile
156 * @param inY y index of tile
157 * @param inDownloadIfNecessary true to download the file if it's not available
158 * @return selected tile if already loaded, or null otherwise
160 public Image getTile(int inLayer, int inX, int inY, boolean inDownloadIfNecessary)
162 if (inY < 0 || inY >= _numTileIndices) return null;
163 // Wrap tile indices which are too big or too small
164 inX = ((inX % _numTileIndices) + _numTileIndices) % _numTileIndices;
166 // Check first in memory cache for tile
167 Image tileImage = null;
168 MemTileCacher tempCache = null;
169 if (_tempCaches != null)
171 tempCache = _tempCaches[inLayer]; // Should probably guard array indexes here
172 tileImage = tempCache.getTile(inX, inY);
173 if (tileImage != null) {
174 //System.out.println("Got tile from memory: " + inX + ", " + inY);
179 // Tile wasn't in memory, but maybe it's in disk cache (if there is one)
180 String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
181 boolean useDisk = (diskCachePath != null);
182 boolean onlineMode = Config.getConfigBoolean(Config.KEY_ONLINE_MODE);
183 MapTile mapTile = null;
186 // Get the map tile from cache
187 mapTile = DiskTileCacher.getTile(diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY));
188 if (mapTile != null && mapTile.getImage() != null)
190 tileImage = mapTile.getImage();
191 if (_returnIncompleteImages) {return tileImage;}
192 // Pass tile to memory cache
193 if (tempCache != null) {
194 tempCache.setTile(tileImage, inX, inY, _zoom);
196 tileImage.getWidth(this); // trigger the load from file
199 // Maybe we've got an image now, maybe it's expired
200 final boolean shouldDownload = (tileImage == null || mapTile == null || mapTile.isExpired());
202 // If we're online then try to download the tile
203 if (onlineMode && _downloadTiles && inDownloadIfNecessary && shouldDownload)
207 URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY));
210 DiskTileCacher.saveTile(tileUrl, diskCachePath,
211 _mapSource.makeFilePath(inLayer, _zoom, inX, inY), this);
212 // Image will now be copied directly from URL stream to disk cache
216 // Load image asynchronously, using observer
217 // In order to set the http user agent, need to use a TileDownloader instead
218 TileDownloader.triggerLoad(this, tileUrl, inLayer, inX, inY, _zoom);
221 catch (MalformedURLException urle) {} // ignore
227 * Method called by image loader to inform of updates to the tiles
228 * @param img the image
229 * @param infoflags flags describing how much of the image is known
232 * @param width ignored
233 * @param height ignored
234 * @return false to carry on loading, true to stop
236 public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
238 boolean loaded = (infoflags & ImageObserver.ALLBITS) > 0;
239 boolean error = (infoflags & ImageObserver.ERROR) > 0;
240 if (loaded || error) {
241 _consumer.tilesUpdated(loaded);
247 * Callback method from TileDownloader to let us know that an image has been loaded
248 * @param inTile Loaded Image object
249 * @param inLayer layer index from 0
250 * @param inX x coordinate of tile
251 * @param inY y coordinate of tile
252 * @param inZoom zoom level of loaded image
254 public void notifyImageLoaded(Image inTile, int inLayer, int inX, int inY, int inZoom)
256 if (inTile != null && _tempCaches != null)
258 MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
259 if (tempCache.getTile(inX, inY) == null)
261 // Check with cache that the zoom level is still valid
262 tempCache.setTile(inTile, inX, inY, inZoom);
263 inTile.getWidth(this); // trigger imageUpdate when image is ready
266 else if (inTile != null) {
267 inTile.getWidth(this);