]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/map/MapTileManager.java
829f824ffa09d06f6e37e2d3bd64e5bd7121725c
[GpsPrune.git] / tim / prune / gui / map / MapTileManager.java
1 package tim.prune.gui.map;
2
3 import java.awt.Image;
4 import java.awt.Toolkit;
5 import java.awt.image.ImageObserver;
6 import java.net.MalformedURLException;
7 import java.net.URL;
8
9 import tim.prune.config.Config;
10
11 /**
12  * Class responsible for managing the map tiles,
13  * including invoking the correct memory cacher(s) and/or disk cacher(s)
14  */
15 public class MapTileManager implements ImageObserver
16 {
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;
31
32
33         /**
34          * Constructor
35          * @param inParent parent canvas to be informed of updates
36          */
37         public MapTileManager(MapCanvas inParent)
38         {
39                 _parent = inParent;
40                 // Adjust the index of the selected map source
41                 adjustSelectedMap();
42                 resetConfig();
43         }
44
45         /**
46          * Recentre the map
47          * @param inZoom zoom level
48          * @param inTileX x coord of central tile
49          * @param inTileY y coord of central tile
50          */
51         public void centreMap(int inZoom, int inTileX, int inTileY)
52         {
53                 _zoom = inZoom;
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);
60                         }
61                 }
62         }
63
64         /**
65          * @return true if zoom is too high for tiles
66          */
67         public boolean isOverzoomed()
68         {
69                 // Ask current map source what maximum zoom is
70                 int maxZoom = (_mapSource == null?0:_mapSource.getMaxZoomLevel());
71                 return (_zoom > maxZoom);
72         }
73
74         /**
75          * Clear all the memory caches due to changed config / zoom
76          */
77         public void clearMemoryCaches()
78         {
79                 int numLayers = _mapSource.getNumLayers();
80                 if (_tempCaches == null || _tempCaches.length != numLayers)
81                 {
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();
86                         }
87                 }
88                 else {
89                         // Cachers already there, just need to be cleared
90                         for (int i=0; i<numLayers; i++) {
91                                 _tempCaches[i].clearAll();
92                         }
93                 }
94         }
95
96         /**
97          * Reset the map source configuration, apparently it has changed
98          */
99         public void resetConfig()
100         {
101                 int sourceNum = Config.getConfigInt(Config.KEY_MAPSOURCE_INDEX);
102                 _mapSource = MapSourceLibrary.getSource(sourceNum);
103                 if (_mapSource == null) {_mapSource = MapSourceLibrary.getSource(0);}
104                 clearMemoryCaches();
105                 _numLayers = _mapSource.getNumLayers();
106         }
107
108         /**
109          * Adjust the index of the selected map
110          * (only required if config was loaded from a previous version of GpsPrune)
111          */
112         private void adjustSelectedMap()
113         {
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))
121                 {
122                         sourceNum += (currNumFixed - prevNumFixed);
123                         Config.setConfigInt(Config.KEY_MAPSOURCE_INDEX, sourceNum);
124                 }
125                 Config.setConfigInt(Config.KEY_NUM_FIXED_MAPS, currNumFixed);
126         }
127
128         /**
129          * @return the number of layers in the map
130          */
131         public int getNumLayers()
132         {
133                 return _numLayers;
134         }
135
136         /**
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
141          */
142         public Image getTile(int inLayer, int inX, int inY)
143         {
144                 // Check tile boundaries
145                 if (inX < 0 || inX >= _numTileIndices || inY < 0 || inY >= _numTileIndices) return null;
146
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);
150                 if (tile != null) {
151                         return tile;
152                 }
153
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);
158                 if (useDisk)
159                 {
160                         tile = DiskTileCacher.getTile(diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY), onlineMode);
161                         if (tile != null)
162                         {
163                                 // Pass tile to memory cache
164                                 tempCache.setTile(tile, inX, inY);
165                                 if (tile.getWidth(this) > 0) {return tile;}
166                                 return null;
167                         }
168                 }
169                 // Tile wasn't in memory or on disk, so if online let's get it
170                 if (onlineMode && _blockedZoom != _zoom)
171                 {
172                         _blockedZoom = 0; // reset to try again
173                         try
174                         {
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))
178                                 {
179                                         // Image now copied directly from URL stream to disk cache
180                                 }
181                                 else
182                                 {
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;}
188                                 }
189                         }
190                         catch (MalformedURLException urle) {} // ignore
191                 }
192                 return null;
193         }
194
195         /**
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
199          * @param x ignored
200          * @param y ignored
201          * @param width ignored
202          * @param height ignored
203          * @return false to carry on loading, true to stop
204          */
205         public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
206         {
207                 boolean loaded = (infoflags & ImageObserver.ALLBITS) > 0;
208                 boolean error = (infoflags & ImageObserver.ERROR) > 0;
209                 if (error) {
210                         _blockedZoom = _zoom;
211                 }
212                 if (loaded || error) {
213                         _parent.tilesUpdated(loaded);
214                 }
215                 return !loaded;
216         }
217 }