]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/map/MapTileManager.java
Version 14, October 2012
[GpsPrune.git] / tim / prune / gui / map / MapTileManager.java
1 package tim.prune.gui.map;
2
3 import java.awt.Image;
4 import java.awt.image.ImageObserver;
5 import java.net.MalformedURLException;
6 import java.net.URL;
7
8 import tim.prune.config.Config;
9
10 /**
11  * Class responsible for managing the map tiles,
12  * including invoking the correct memory cacher(s) and/or disk cacher(s)
13  */
14 public class MapTileManager implements ImageObserver
15 {
16         /** Parent object to inform when tiles received */
17         private MapCanvas _parent = null;
18         /** Current map source */
19         private MapSource _mapSource = null;
20         /** Array of tile caches, one per layer */
21         private MemTileCacher[] _tempCaches = null;
22         /** Number of layers */
23         private int _numLayers = -1;
24         /** Current zoom level */
25         private int _zoom = 0;
26         /** Number of tiles in each direction for this zoom level */
27         private int _numTileIndices = 1;
28
29
30         /**
31          * Constructor
32          * @param inParent parent canvas to be informed of updates
33          */
34         public MapTileManager(MapCanvas inParent)
35         {
36                 _parent = inParent;
37                 // Adjust the index of the selected map source
38                 adjustSelectedMap();
39                 resetConfig();
40         }
41
42         /**
43          * Recentre the map
44          * @param inZoom zoom level
45          * @param inTileX x coord of central tile
46          * @param inTileY y coord of central tile
47          */
48         public void centreMap(int inZoom, int inTileX, int inTileY)
49         {
50                 _zoom = inZoom;
51                 // Calculate number of tiles = 2^^zoom
52                 _numTileIndices = 1 << _zoom;
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);
57                         }
58                 }
59         }
60
61         /**
62          * @return true if zoom is too high for tiles
63          */
64         public boolean isOverzoomed()
65         {
66                 // Ask current map source what maximum zoom is
67                 int maxZoom = (_mapSource == null?0:_mapSource.getMaxZoomLevel());
68                 return (_zoom > maxZoom);
69         }
70
71         /**
72          * Clear all the memory caches due to changed config / zoom
73          */
74         public void clearMemoryCaches()
75         {
76                 int numLayers = _mapSource.getNumLayers();
77                 if (_tempCaches == null || _tempCaches.length != numLayers)
78                 {
79                         // Cachers don't match, so need to create the right number of them
80                         _tempCaches = new MemTileCacher[numLayers];
81                         for (int i=0; i<numLayers; i++) {
82                                 _tempCaches[i] = new MemTileCacher();
83                         }
84                 }
85                 else {
86                         // Cachers already there, just need to be cleared
87                         for (int i=0; i<numLayers; i++) {
88                                 _tempCaches[i].clearAll();
89                         }
90                 }
91         }
92
93         /**
94          * Reset the map source configuration, apparently it has changed
95          */
96         public void resetConfig()
97         {
98                 int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
99                 _mapSource = MapSourceLibrary.getSource(sourceNum);
100                 if (_mapSource == null) {_mapSource = MapSourceLibrary.getSource(0);}
101                 clearMemoryCaches();
102                 _numLayers = _mapSource.getNumLayers();
103         }
104
105         /**
106          * Adjust the index of the selected map
107          * (only required if config was loaded from a previous version of GpsPrune)
108          */
109         private void adjustSelectedMap()
110         {
111                 int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
112                 int prevNumFixed = Config.getConfigInt(Config.KEY_NUM_FIXED_MAPS);
113                 // Number of fixed maps not specified in version <=13, default to 6
114                 if (prevNumFixed == 0) prevNumFixed = 6;
115                 int currNumFixed = MapSourceLibrary.getNumFixedSources();
116                 // Only need to do something if the number has changed
117                 if (currNumFixed != prevNumFixed && (sourceNum >= prevNumFixed || sourceNum >= currNumFixed))
118                 {
119                         sourceNum += (currNumFixed - prevNumFixed);
120                         Config.setConfigInt(Config.KEY_MAPSOURCE_INDEX, sourceNum);
121                 }
122                 Config.setConfigInt(Config.KEY_NUM_FIXED_MAPS, currNumFixed);
123         }
124
125         /**
126          * @return the number of layers in the map
127          */
128         public int getNumLayers()
129         {
130                 return _numLayers;
131         }
132
133         /**
134          * @param inLayer layer number, starting from 0
135          * @param inX x index of tile
136          * @param inY y index of tile
137          * @return selected tile if already loaded, or null otherwise
138          */
139         public Image getTile(int inLayer, int inX, int inY)
140         {
141                 if (inY < 0 || inY >= _numTileIndices) return null;
142                 // Wrap tile indices which are too big or too small
143                 inX = ((inX % _numTileIndices) + _numTileIndices) % _numTileIndices;
144
145                 // Check first in memory cache for tile
146                 MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
147                 Image tile = tempCache.getTile(inX, inY);
148                 if (tile != null) {
149                         return tile;
150                 }
151
152                 // Tile wasn't in memory, but maybe it's in disk cache (if there is one)
153                 String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
154                 boolean useDisk = (diskCachePath != null);
155                 boolean onlineMode = Config.getConfigBoolean(Config.KEY_ONLINE_MODE);
156                 if (useDisk)
157                 {
158                         tile = DiskTileCacher.getTile(diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY), onlineMode);
159                         if (tile != null)
160                         {
161                                 // Pass tile to memory cache
162                                 tempCache.setTile(tile, inX, inY, _zoom);
163                                 if (tile.getWidth(this) > 0) {return tile;}
164                                 return null;
165                         }
166                 }
167                 // Tile wasn't in memory or on disk, so if online let's get it
168                 if (onlineMode)
169                 {
170                         try
171                         {
172                                 URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY));
173                                 if (useDisk && DiskTileCacher.saveTile(tileUrl, diskCachePath,
174                                         _mapSource.makeFilePath(inLayer, _zoom, inX, inY), this))
175                                 {
176                                         // Image now copied directly from URL stream to disk cache
177                                 }
178                                 else
179                                 {
180                                         // Load image asynchronously, using observer
181                                         // tile = Toolkit.getDefaultToolkit().createImage(tileUrl);
182                                         // In order to set the http user agent, need to use a TileDownloader instead
183                                         TileDownloader.triggerLoad(this, tileUrl, inLayer, inX, inY, _zoom);
184                                 }
185                         }
186                         catch (MalformedURLException urle) {} // ignore
187                 }
188                 return null;
189         }
190
191         /**
192          * Method called by image loader to inform of updates to the tiles
193          * @param img the image
194          * @param infoflags flags describing how much of the image is known
195          * @param x ignored
196          * @param y ignored
197          * @param width ignored
198          * @param height ignored
199          * @return false to carry on loading, true to stop
200          */
201         public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
202         {
203                 boolean loaded = (infoflags & ImageObserver.ALLBITS) > 0;
204                 boolean error = (infoflags & ImageObserver.ERROR) > 0;
205                 if (loaded || error) {
206                         _parent.tilesUpdated(loaded);
207                 }
208                 return !loaded;
209         }
210
211         /**
212          * Callback method from TileDownloader to let us know that an image has been loaded
213          * @param inTile Loaded Image object
214          * @param inLayer layer index from 0
215          * @param inX x coordinate of tile
216          * @param inY y coordinate of tile
217          * @param inZoom zoom level of loaded image
218          */
219         public void notifyImageLoaded(Image inTile, int inLayer, int inX, int inY, int inZoom)
220         {
221                 if (inTile != null)
222                 {
223                         MemTileCacher tempCache = _tempCaches[inLayer]; // Should probably guard against nulls and array indexes here
224                         if (tempCache.getTile(inX, inY) == null)
225                         {
226                                 // Check with cache that the zoom level is still valid
227                                 tempCache.setTile(inTile, inX, inY, inZoom);
228                                 inTile.getWidth(this); // trigger imageUpdate when image is ready
229                         }
230                 }
231         }
232 }