]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/save/MapGrouter.java
263a3067f4902b5916c2b5e43d63bfeccdb21c5c
[GpsPrune.git] / src / tim / prune / save / MapGrouter.java
1 package tim.prune.save;
2
3 import tim.prune.data.DoubleRange;
4 import tim.prune.data.Track;
5 import tim.prune.data.TrackExtents;
6 import tim.prune.gui.map.MapSource;
7 import tim.prune.gui.map.MapTileManager;
8 import tim.prune.gui.map.TileConsumer;
9
10 import java.awt.Color;
11 import java.awt.Graphics;
12 import java.awt.Image;
13 import java.awt.image.BufferedImage;
14
15
16 /**
17  * Class to handle the sticking together (grouting) of map tiles
18  * to create a single map image for the current track
19  */
20 public class MapGrouter implements TileConsumer
21 {
22         /** The most recently produced image */
23         private GroutedImage _lastGroutedImage = null;
24
25         /**
26          * Clear the last image, it's not needed any more
27          */
28         public void clearMapImage() {
29                 _lastGroutedImage = null;
30         }
31
32         /**
33          * Grout the required map tiles together according to the track's extent
34          * @param inTrack track object
35          * @param inMapSource map source to use (may have one or two layers)
36          * @param inZoom selected zoom level
37          * @return grouted image, or null if no image could be created
38          */
39         public GroutedImage createMapImage(Track inTrack, MapSource inMapSource, int inZoom)
40         {
41                 return createMapImage(inTrack, inMapSource, inZoom, false);
42         }
43
44         /**
45          * Grout the required map tiles together according to the track's extent
46          * @param inTrack track object
47          * @param inMapSource map source to use (may have one or two layers)
48          * @param inZoom selected zoom level
49          * @param inDownload true to download tiles, false (by default) to just pull from disk
50          * @return grouted image, or null if no image could be created
51          */
52         public GroutedImage createMapImage(Track inTrack, MapSource inMapSource, int inZoom, boolean inDownload)
53         {
54                 // Get the extents of the track including a standard (10%) border around the data
55                 TrackExtents extents = new TrackExtents(inTrack);
56                 extents.applySquareBorder();
57                 DoubleRange xRange = extents.getXRange();
58                 DoubleRange yRange = extents.getYRange();
59
60                 // Work out which tiles are required
61                 final int zoomFactor = 1 << inZoom;
62                 final int minTileX = (int) (xRange.getMinimum() * zoomFactor);
63                 final int maxTileX = (int) (xRange.getMaximum() * zoomFactor);
64                 final int minTileY = (int) (yRange.getMinimum() * zoomFactor);
65                 final int maxTileY = (int) (yRange.getMaximum() * zoomFactor);
66
67                 // Work out how big the final image will be, create a BufferedImage
68                 final int pixCount = (int) (extents.getXRange().getRange() * zoomFactor * 256);
69                 if (pixCount < 2 || inZoom == 0) {return null;}
70                 BufferedImage resultImage = new BufferedImage(pixCount, pixCount, BufferedImage.TYPE_INT_RGB);
71                 Graphics g = resultImage.getGraphics();
72                 g.setColor(Color.WHITE);
73                 g.fillRect(0, 0, pixCount, pixCount);
74                 // Work out where to start drawing the tiles on the image
75                 int xOffset = (int) ((minTileX - xRange.getMinimum() * zoomFactor) * 256);
76
77                 // Make a map tile manager to load (or download) the tiles
78                 MapTileManager tileManager = new MapTileManager(this);
79                 tileManager.setMapSource(inMapSource);
80                 tileManager.enableTileDownloading(inDownload);
81                 tileManager.setReturnIncompleteImages();
82                 tileManager.setZoom(inZoom);
83
84                 int numTilesUsed = 0;
85                 int numTilesMissing = 0;
86
87                 // Loop over the tiles
88                 for (int x = minTileX; x <= maxTileX; x++)
89                 {
90                         int yOffset = (int) ((minTileY - yRange.getMinimum() * zoomFactor) * 256);
91                         for (int y = minTileY; y <= maxTileY; y++)
92                         {
93                                 for (int layer=0; layer < inMapSource.getNumLayers(); layer++)
94                                 {
95                                         Image tile = tileManager.getTile(layer, x, y, true);
96                                         // If we're downloading tiles, wait until the tile isn't null
97                                         int waitCount = 0;
98                                         while (tile == null && inDownload && waitCount < 3)
99                                         {
100                                                 // System.out.println("wait " + waitCount + " for tile to be not null");
101                                                 try {Thread.sleep(250);} catch (InterruptedException e) {}
102                                                 tile = tileManager.getTile(layer, x, y, false); // don't request another download
103                                                 waitCount++;
104                                         }
105                                         // See if there's a tile or not
106                                         if (tile != null)
107                                         {
108                                                 // Wait until tile is available (loaded asynchronously)
109                                                 while (tile.getWidth(null) < 0 && !inDownload)
110                                                 {
111                                                         // System.out.println("Wait for tile width");
112                                                         try {
113                                                                 Thread.sleep(inDownload ? 500 : 100);
114                                                         }
115                                                         catch (InterruptedException ie) {}
116                                                 }
117                                                 // work out where to copy it to, paint it
118                                                 // System.out.println("Painting tile " + x + "," + y + " at " + xOffset + "," + yOffset);
119                                                 numTilesUsed++;
120                                                 g.drawImage(tile, xOffset, yOffset, null);
121                                         }
122                                         else
123                                         {
124                                                 // null tile, that means it's either not available or really slow to start downloading
125                                                 numTilesMissing++;
126                                         }
127                                 }
128                                 yOffset += 256;
129                         }
130                         xOffset += 256;
131                 }
132                 // Get rid of the image if it's empty
133                 if (numTilesUsed == 0) {
134                         resultImage = null;
135                 }
136                 // Store the xy limits in the GroutedImage to make it easier to draw on top
137                 GroutedImage result = new GroutedImage(resultImage, numTilesUsed, numTilesMissing);
138                 result.setXRange(xRange);
139                 result.setYRange(yRange);
140                 return result;
141         }
142
143         /**
144          * Get the grouted map image, using the previously-created one if available
145          * @param inTrack track object
146          * @param inMapSource map source to use (may have one or two layers)
147          * @param inZoom selected zoom level
148          * @return grouted image, or null if no image could be created
149          */
150         public synchronized GroutedImage getMapImage(Track inTrack, MapSource inMapSource, int inZoom)
151         {
152                 if (_lastGroutedImage == null) {
153                         _lastGroutedImage = createMapImage(inTrack, inMapSource, inZoom);
154                 }
155                 return _lastGroutedImage;
156         }
157
158         /**
159          * @param inTrack track object
160          * @param inZoom selected zoom level
161          * @return true if the image size is acceptable
162          */
163         public static boolean isZoomLevelOk(Track inTrack, int inZoom)
164         {
165                 // Get the extents of the track including a standard (10%) border around the data
166                 TrackExtents extents = new TrackExtents(inTrack);
167                 extents.applySquareBorder();
168
169                 // Work out how big the final image will be
170                 final int zoomFactor = 1 << inZoom;
171                 final int pixCount = (int) (extents.getXRange().getRange() * zoomFactor * 256);
172                 return pixCount > 2 && pixCount < 4000;
173         }
174
175         /** React to tiles being updated by the tile manager */
176         public void tilesUpdated(boolean inIsOk)
177         {
178                 // Doesn't need any action
179         }
180 }