1 package tim.prune.gui.map;
4 import java.awt.Toolkit;
5 import java.awt.image.ImageObserver;
6 import java.net.MalformedURLException;
9 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 /** Parent object to inform when tiles received */
18 private MapCanvas _parent = null;
19 /** Current map source */
20 private MapSource _mapSource = null;
21 /** Array of tile caches, one per layer */
22 private MemTileCacher[] _tempCaches = null;
23 /** Number of layers */
24 private int _numLayers = -1;
25 /** Current zoom level */
26 private int _zoom = 0;
27 /** Currently blocked zoom level, to prevent looping for non-existent images */
28 private int _blockedZoom = 0;
29 /** Number of tiles in each direction for this zoom level */
30 private int _numTileIndices = 1;
35 * @param inParent parent canvas to be informed of updates
37 public MapTileManager(MapCanvas inParent)
40 // Adjust the index of the selected map source
47 * @param inZoom zoom level
48 * @param inTileX x coord of central tile
49 * @param inTileY y coord of central tile
51 public void centreMap(int inZoom, int inTileX, int inTileY)
54 // Calculate number of tiles = 2^^zoom
55 _numTileIndices = 1 << _zoom;
56 // Pass params onto all memory cachers
57 if (_tempCaches != null) {
58 for (int i=0; i<_tempCaches.length; i++) {
59 _tempCaches[i].centreMap(inZoom, inTileX, inTileY);
65 * @return true if zoom is too high for tiles
67 public boolean isOverzoomed()
69 // Ask current map source what maximum zoom is
70 int maxZoom = (_mapSource == null?0:_mapSource.getMaxZoomLevel());
71 return (_zoom > maxZoom);
75 * Clear all the memory caches due to changed config / zoom
77 public void clearMemoryCaches()
79 int numLayers = _mapSource.getNumLayers();
80 if (_tempCaches == null || _tempCaches.length != numLayers)
82 // Cachers don't match, so need to create the right number of them
83 _tempCaches = new MemTileCacher[numLayers];
84 for (int i=0; i<numLayers; i++) {
85 _tempCaches[i] = new MemTileCacher();
89 // Cachers already there, just need to be cleared
90 for (int i=0; i<numLayers; i++) {
91 _tempCaches[i].clearAll();
97 * Reset the map source configuration, apparently it has changed
99 public void resetConfig()
101 int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
102 _mapSource = MapSourceLibrary.getSource(sourceNum);
103 if (_mapSource == null) {_mapSource = MapSourceLibrary.getSource(0);}
105 _numLayers = _mapSource.getNumLayers();
109 * Adjust the index of the selected map
110 * (only required if config was loaded from a previous version of GpsPrune)
112 private void adjustSelectedMap()
114 int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
115 int prevNumFixed = Config.getConfigInt(Config.KEY_NUM_FIXED_MAPS);
116 // Number of fixed maps not specified in version <=13, default to 6
117 if (prevNumFixed == 0) prevNumFixed = 6;
118 int currNumFixed = MapSourceLibrary.getNumFixedSources();
119 // Only need to do something if the number has changed
120 if (currNumFixed != prevNumFixed && (sourceNum >= prevNumFixed || sourceNum >= currNumFixed))
122 sourceNum += (currNumFixed - prevNumFixed);
123 Config.setConfigInt(Config.KEY_MAPSOURCE_INDEX, sourceNum);
125 Config.setConfigInt(Config.KEY_NUM_FIXED_MAPS, currNumFixed);
129 * @return the number of layers in the map
131 public int getNumLayers()
137 * @param inLayer layer number, starting from 0
138 * @param inX x index of tile
139 * @param inY y index of tile
140 * @return selected tile if already loaded, or null otherwise
142 public Image getTile(int inLayer, int inX, int inY)
144 // Check tile boundaries
145 if (inX < 0 || inX >= _numTileIndices || inY < 0 || inY >= _numTileIndices) return null;
147 // Check first in memory cache for tile
148 MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
149 Image tile = tempCache.getTile(inX, inY);
154 // Tile wasn't in memory, but maybe it's in disk cache (if there is one)
155 String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
156 boolean useDisk = (diskCachePath != null);
157 boolean onlineMode = Config.getConfigBoolean(Config.KEY_ONLINE_MODE);
160 tile = DiskTileCacher.getTile(diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY), onlineMode);
163 // Pass tile to memory cache
164 tempCache.setTile(tile, inX, inY);
165 if (tile.getWidth(this) > 0) {return tile;}
169 // Tile wasn't in memory or on disk, so if online let's get it
170 if (onlineMode && _blockedZoom != _zoom)
172 _blockedZoom = 0; // reset to try again
175 URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY));
176 if (useDisk && DiskTileCacher.saveTile(tileUrl, diskCachePath,
177 _mapSource.makeFilePath(inLayer, _zoom, inX, inY), this))
179 // Image now copied directly from URL stream to disk cache
183 // Load image asynchronously, using observer
184 tile = Toolkit.getDefaultToolkit().createImage(tileUrl);
185 // Pass to memory cache
186 _tempCaches[inLayer].setTile(tile, inX, inY);
187 if (tile.getWidth(this) > 0) {return tile;}
190 catch (MalformedURLException urle) {} // ignore
196 * Method called by image loader to inform of updates to the tiles
197 * @param img the image
198 * @param infoflags flags describing how much of the image is known
201 * @param width ignored
202 * @param height ignored
203 * @return false to carry on loading, true to stop
205 public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
207 boolean loaded = (infoflags & ImageObserver.ALLBITS) > 0;
208 boolean error = (infoflags & ImageObserver.ERROR) > 0;
210 _blockedZoom = _zoom;
212 if (loaded || error) {
213 _parent.tilesUpdated(loaded);