]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/function/srtm/SrtmSource.java
Refactor and add comments
[GpsPrune.git] / src / tim / prune / function / srtm / SrtmSource.java
1 package tim.prune.function.srtm;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileOutputStream;
6 import java.io.InputStream;
7 import java.io.IOException;
8 import java.net.URL;
9 import java.net.HttpURLConnection;
10 import java.util.zip.ZipEntry;
11 import java.util.zip.ZipInputStream;
12
13 import tim.prune.GpsPrune;
14
15 /**
16  * Abstract class implemented by each download source
17  */
18 public abstract class SrtmSource {
19         /**
20          * Name of the source
21          * A string used to build function.downloadsrtm.getName() for getting
22          * the string shown to the user in menus and messages, also used to
23          * build cache directory
24          */
25         public abstract String getName();
26
27         /**
28          * Whether the source is ready to use
29          * @return true, or false if configuration is necessary
30          */
31         public abstract boolean isReadyToUse();
32
33         /**
34          * Download one tile of SRTM data to cache
35          * @param inTile the tile to get
36          */
37         public abstract boolean downloadTile(SrtmTile inTile)
38                 throws SrtmSourceException;
39
40         /**
41          * Returns the number of rows of the tile
42          *
43          * Tiles from viewfinderpanoramas have different resolutions depending
44          * on the location.
45          * @param inTile the tile whose size we want
46          * @return number of rows; 1201 or 3601 in current implementation
47          */
48         public abstract int getRowSize(SrtmTile inTile);
49
50         /**
51          * Whether the tile is cached
52          * @return true if the tile is in the cache and ready to be used
53          */
54         public boolean isCached(SrtmTile inTile)
55         {
56                 File cachedFileName = getCacheFileName(inTile);
57                 return cachedFileName != null &&
58                         getCacheFileName(inTile).exists();
59         }
60
61         /**
62          * Get the heigths for that tile
63          *
64          * @return an one-dimentional array of size {@link getRowSize ** 2}
65          * @throws SrtmSourceException notably if the tile is not cached yet
66          */
67         public int[] getTileHeights(SrtmTile inTile)
68                 throws SrtmSourceException
69         {
70                 File cacheFileName = getCacheFileName(inTile);
71                 if (cacheFileName == null)
72                 {
73                         throw new SrtmSourceException("Tile "+inTile.getTileName()+" not in cache");
74                 }
75                 try
76                 {
77                         ZipInputStream inStream = new ZipInputStream(new FileInputStream(cacheFileName));
78                         ZipEntry entry = inStream.getNextEntry();
79                         int rowSize = getRowSize(inTile);
80                         int tileSize = rowSize * rowSize;
81                         if (entry.getSize() != 2 * tileSize)
82                         {
83                                 throw new SrtmSourceException("Tile file "+cacheFileName+" does not have the expected size");
84                         }
85                         return slurpTileHeigths(inStream, tileSize);
86                 }
87                 catch (IOException e)
88                 {
89                         throw new SrtmSourceException("Failure opening "+cacheFileName+" for reading:"+e.getMessage());
90                 }
91
92         }
93
94         // helpers used internally by the client classes
95         protected abstract String getSourceExtension();
96
97         /**
98          * Returns the cache directory for tiles, a sub-directory of the map
99          * cache
100          */
101         protected File getCacheDir()
102         {
103                 return SrtmDiskCache.getCacheDir(getName());
104         }
105
106         /**
107          * Return the name of the filename for that tile
108          * For viewfinderpanoramas.org tiles, that might be a ZIP file
109          * containing several tiles
110          */
111         protected File getCacheFileName(SrtmTile inTile)
112         {
113                 String fileName = inTile.getTileName() + getSourceExtension();
114                 return new File(getCacheDir(), fileName);
115         }
116
117         protected InputStream getStreamToUrl(URL inTileUrl)
118                 throws SrtmSourceException
119         {
120                 try
121                 {
122                         HttpURLConnection conn = (HttpURLConnection) inTileUrl.openConnection();
123                         conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
124                         int status = conn.getResponseCode();
125                         if (status == 200)
126                         {
127                                 return conn.getInputStream();
128                         }
129                         else if (status == 404)
130                         {
131                                 throw new SrtmSourceException("Tile not found: "+conn.getURL());
132                         }
133                         else
134                         {
135                                 throw new SrtmSourceException("Invalid response from server: " +status+conn.getContent());
136                         }
137                 }
138                 catch (IOException e)
139                 {
140                         throw new SrtmSourceException("Error while downloading tile from "+inTileUrl+": "+e.getMessage());
141                 }
142         }
143
144         /**
145          * Helper for downloadTile()
146          * @param inStream Stream to read from, typically from HTTP connection
147          * @param outputFile Where to save the data being read
148          */
149         protected boolean readToFile(InputStream inStream, File outputFile)
150                 throws SrtmSourceException
151         {
152                 try
153                 {
154                         FileOutputStream outStream = new FileOutputStream(outputFile);
155
156                         byte[] buffer = new byte[4096];
157                         int read = 0;
158                         while ((read = inStream.read(buffer)) != -1)
159                         {
160                                 outStream.write(buffer, 0, read);
161                         }
162                         // Make sure streams are closed
163                         try {inStream.close();} catch (Exception e) {}
164                         try {outStream.close();} catch (Exception e) {}
165                         return true;
166                 }
167                 catch (IOException e)
168                 {
169                         throw new SrtmSourceException("Error downloading tile:" + e.getMessage());
170                 }
171         }
172
173         /**
174          * Helper for getTileHeights
175          * @param inStream Stream to read from, assumed to have been advanced
176          *        to the appropriate tile
177          * @param tileSize the expected tileSize (square of getRowSize; assumed
178          *        to have been checked by the caller)
179          * @return the tile heights
180          */
181         protected int[] slurpTileHeigths(ZipInputStream inStream, int tileSize)
182                 throws IOException
183         {
184                 int[] heights = new int[tileSize];
185                 int dataSize = 2 * tileSize;
186                 byte[] buffer = new byte[dataSize];
187                 // Read entire file contents into one byte array
188                 int alreadyRead = 0;
189                 while (alreadyRead < dataSize)
190                 {
191                         alreadyRead += inStream.read(buffer, alreadyRead, dataSize - alreadyRead);
192                 }
193                 for (int i = 0; i < tileSize; i++)
194                 {
195                         // Bytes are signed. Cast high-order to int with sign
196                         // extension, and clamp low-order to its unsigned range
197                         heights[i] = buffer[2 * i] * 256 + (0xff & buffer[2 * i + 1]);
198                 }
199                 // Close stream
200                 inStream.close();
201                 return heights;
202         }
203 }