import tim.prune.function.settings.SetPathsFunction;
import tim.prune.function.sew.SewTrackSegmentsFunction;
import tim.prune.function.sew.SplitSegmentsFunction;
-import tim.prune.function.srtm.DownloadSrtmFunction;
import tim.prune.function.srtm.LookupSrtmFunction;
import tim.prune.function.weather.GetWeatherForecastFunction;
import tim.prune.load.AudioLoader;
public static GenericFunction FUNCTION_DELETE_BY_DATE = null;
public static SingleNumericParameterFunction FUNCTION_INTERPOLATE = null;
public static GenericFunction FUNCTION_LOOKUP_SRTM = null;
- public static GenericFunction FUNCTION_DOWNLOAD_SRTM = null;
public static GenericFunction FUNCTION_NEARBY_WIKIPEDIA = null;
public static GenericFunction FUNCTION_SEARCH_WIKIPEDIA = null;
public static GenericFunction FUNCTION_SEARCH_OSMPOIS = null;
FUNCTION_DELETE_BY_DATE = new DeleteByDateFunction(inApp);
FUNCTION_INTERPOLATE = new InterpolateFunction(inApp);
FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp);
- FUNCTION_DOWNLOAD_SRTM = new DownloadSrtmFunction(inApp);
FUNCTION_NEARBY_WIKIPEDIA = new GetWikipediaFunction(inApp);
FUNCTION_SEARCH_WIKIPEDIA = new SearchWikipediaNames(inApp);
FUNCTION_SEARCH_OSMPOIS = new SearchOsmPoisFunction(inApp);
public static final String KEY_WAYPOINT_ICON_SIZE = "prune.waypointiconsize";
/** Id of selected timezone */
public static final String KEY_TIMEZONE_ID = "prune.timezoneid";
+ /** Username/password to the Earthdata server for SRTM 1-arcsecond tiles */
+ public static final String KEY_EARTHDATA_AUTH = "prune.earthdataauth";
/** Initialise the default properties */
package tim.prune.function.srtm;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import tim.prune.GenericFunction;
import tim.prune.GpsPrune;
import tim.prune.I18nManager;
-import tim.prune.config.Config;
import tim.prune.data.DoubleRange;
import tim.prune.gui.ProgressDialog;
private ProgressDialog _progress = null;
/** Flag to check whether this function is currently running or not */
private boolean _running = false;
-
+ private SrtmSource _srtmSource = null;
/**
* Constructor
* @param inApp App object
*/
- public DownloadSrtmFunction(App inApp) {
+ public DownloadSrtmFunction(App inApp, SrtmSource inSrtmSource) {
super(inApp);
+ _srtmSource = inSrtmSource;
}
/** @return name key */
public String getNameKey() {
- return "function.downloadsrtm";
+ return "function.downloadsrtm."+_srtmSource.getName();
}
/**
*/
public void begin()
{
+ if (! SrtmDiskCache.ensureCacheIsUsable())
+ {
+ _app.showErrorMessage(getNameKey(), "error.downloadsrtm.nocache");
+ return;
+ }
+ if (! _srtmSource.isReadyToUse())
+ {
+ _app.showErrorMessage(getNameKey(), getNameKey() + ".needsetup");
+ return;
+ }
+
_running = true;
if (_progress == null) {
_progress = new ProgressDialog(_parentFrame, getNameKey());
* Run method using separate thread
*/
public void run()
+ {
+ ArrayList<SrtmTile> tileList = buildCoveringTiles();
+ downloadTiles(tileList);
+ // Finished
+ _running = false;
+ }
+
+ private ArrayList<SrtmTile> buildCoveringTiles()
{
// Compile list of tiles to get
ArrayList<SrtmTile> tileList = new ArrayList<SrtmTile>();
}
}
- downloadTiles(tileList);
- // Finished
- _running = false;
+ return tileList;
}
-
/**
* Download the tiles of SRTM data
* @param inTileList list of tiles to get
*/
private void downloadTiles(ArrayList<SrtmTile> inTileList)
{
+ String errorMessage = "";
// Update progress bar
if (_progress != null)
{
_progress.setValue(0);
}
- String errorMessage = null;
-
- // Check the cache is ok
- final String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
- if (diskCachePath != null)
- {
- File srtmDir = new File(diskCachePath, "srtm");
- if (!srtmDir.exists() && !srtmDir.mkdir()) {
- // can't create the srtm directory
- errorMessage = I18nManager.getText("error.downloadsrtm.nocache");
- }
- }
- else {
- // no cache set up
- errorMessage = I18nManager.getText("error.downloadsrtm.nocache");
- }
-
- // Get urls for each tile
- URL[] urls = TileFinder.getUrls(inTileList);
int numDownloaded = 0;
for (int t=0; t<inTileList.size() && !_progress.isCancelled(); t++)
{
- if (urls[t] != null)
+ if (_srtmSource.isCached(inTileList.get(t)))
{
- // Define streams
- FileOutputStream outStream = null;
- InputStream inStream = null;
- try
+ System.out.println(inTileList.get(t).getTileName()+" already in cache, nothing to do");
+ continue;
+ }
+
+ boolean success;
+ try
+ {
+ success = _srtmSource.downloadTile(inTileList.get(t));
+ if (success)
{
- // Set progress
- _progress.setValue(t);
- // See if we've already got this tile or not
- File outputFile = getFileToWrite(urls[t]);
- if (outputFile != null)
- {
- // System.out.println("Download: Need to download: " + urls[t]);
- outStream = new FileOutputStream(outputFile);
- URLConnection conn = urls[t].openConnection();
- conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
- inStream = conn.getInputStream();
- // Copy all the bytes to the file
- int c;
- while ((c = inStream.read()) != -1)
- {
- outStream.write(c);
- }
-
- numDownloaded++;
- }
- // else System.out.println("Don't need to download: " + urls[t].getFile());
- }
- catch (IOException ioe) {errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
+ numDownloaded++;
}
- // Make sure streams are closed
- try {inStream.close();} catch (Exception e) {}
- try {outStream.close();} catch (Exception e) {}
+ // Set progress
+ _progress.setValue(t + 1);
+ }
+ catch (SrtmSourceException e)
+ {
+ errorMessage += e.getMessage();
}
}
return;
}
- if (errorMessage != null) {
+ if (! errorMessage.equals("")) {
_app.showErrorMessageNoLookup(getNameKey(), errorMessage);
+ return;
}
- else if (numDownloaded == 1)
+ if (numDownloaded == 1)
{
JOptionPane.showMessageDialog(_parentFrame, I18nManager.getTextWithNumber("confirm.downloadsrtm.1", numDownloaded),
I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
}
else if (inTileList.size() > 0) {
- _app.showErrorMessage(getNameKey(), "confirm.downloadsrtm.none");
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("confirm.downloadsrtm.none"));
}
}
-
- /**
- * See whether the SRTM file is already available locally
- * @param inUrl URL for online resource
- * @return file object to write to, or null if already there
- */
- private static File getFileToWrite(URL inUrl)
- {
- String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
- if (diskCachePath != null)
- {
- File srtmDir = new File(diskCachePath, "srtm");
- if (srtmDir.exists() && srtmDir.isDirectory() && srtmDir.canRead())
- {
- File srtmFile = new File(srtmDir, new File(inUrl.getFile()).getName());
- if (!srtmFile.exists() || !srtmFile.canRead() || srtmFile.length() <= 400) {
- return srtmFile;
- }
- }
- }
- return null;
- }
-
- /**
- * @return true if a thread is currently running
- */
- public boolean isRunning()
- {
- return _running;
- }
}
package tim.prune.function.srtm;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.net.URL;
import java.util.ArrayList;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
import javax.swing.JOptionPane;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
-import tim.prune.config.Config;
import tim.prune.data.Altitude;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
import tim.prune.data.Track;
import tim.prune.data.UnitSetLibrary;
import tim.prune.gui.ProgressDialog;
-import tim.prune.tips.TipManager;
import tim.prune.undo.UndoLookupSrtm;
/**
private Track _track = null;
/** Flag for whether this is a real track or a terrain one */
private boolean _normalTrack = true;
- /** Flag set when any tiles had to be downloaded (rather than just loaded locally) */
- private boolean _hadToDownload = false;
/** Flag to check whether this function is currently running or not */
private boolean _running = false;
- /** Expected size of hgt file in bytes */
- private static final long HGT_SIZE = 2884802L;
/** Altitude below which is considered void */
private static final int VOID_VAL = -32768;
private void begin(Track inTrack, boolean inNormalTrack)
{
_running = true;
- _hadToDownload = false;
+ if (! SrtmDiskCache.ensureCacheIsUsable())
+ {
+ _app.showErrorMessage(getNameKey(), "error.cache.notthere");
+ }
if (_progress == null) {
_progress = new ProgressDialog(_parentFrame, getNameKey());
}
for (int i = 0; i < _track.getNumPoints(); i++)
{
// Consider points which don't have altitudes or have zero values
- if (!_track.getPoint(i).hasAltitude()
- || (overwriteZeros && _track.getPoint(i).getAltitude().getValue() == 0))
+ if (needsAltitude(_track.getPoint(i), overwriteZeros))
{
SrtmTile tile = new SrtmTile(_track.getPoint(i));
boolean alreadyGot = false;
lookupValues(tileList, overwriteZeros);
// Finished
_running = false;
- // Show tip if lots of online lookups were necessary
- if (_hadToDownload) {
- _app.showTip(TipManager.Tip_DownloadSrtm);
- }
}
+ /**
+ * true if we need to set the altitude of this point
+ */
+ private boolean needsAltitude(DataPoint point, boolean overwriteZeros)
+ {
+ if (!point.hasAltitude())
+ {
+ return true;
+ }
+ if (overwriteZeros && point.getAltitude().getValue() == 0)
+ {
+ return true;
+ }
+ return false;
+ }
/**
* Lookup the values from SRTM data
_progress.setMaximum(inTileList.size());
_progress.setValue(0);
}
- String errorMessage = null;
- // Get urls for each tile
- URL[] urls = TileFinder.getUrls(inTileList);
+ String errorMessage = "";
for (int t=0; t<inTileList.size() && !_progress.isCancelled(); t++)
{
- if (urls[t] != null)
+ SrtmTile tile = inTileList.get(t);
+ SrtmSource srtmSource = tile.findBestCachedSource();
+
+ if (srtmSource == null)
{
- SrtmTile tile = inTileList.get(t);
- try
+ errorMessage += "Tile "+tile.getTileName()+" not in cache!\n";
+ continue;
+ }
+
+ // Set progress
+ _progress.setValue(t);
+
+ int[] heights;
+ try {
+ heights = srtmSource.getTileHeights(tile);
+ }
+ catch (SrtmSourceException e)
+ {
+ errorMessage += e.getMessage();
+ continue;
+ }
+ int rowSize = srtmSource.getRowSize(tile);
+ if (rowSize <= 0)
+ {
+ errorMessage += "Tile "+tile.getTileName()+" is corrupted";
+ }
+
+ // Loop over all points in track, try to apply altitude from array
+ for (int p = 0; p < _track.getNumPoints(); p++)
+ {
+ DataPoint point = _track.getPoint(p);
+ if (needsAltitude(point, inOverwriteZeros))
{
- // Set progress
- _progress.setValue(t);
- final int ARRLENGTH = 1201 * 1201;
- int[] heights = new int[ARRLENGTH];
- // Open zipinputstream on url and check size
- ZipInputStream inStream = getStreamToHgtFile(urls[t]);
- boolean entryOk = false;
- if (inStream != null)
+ if (new SrtmTile(point).equals(tile))
{
- ZipEntry entry = inStream.getNextEntry();
- entryOk = (entry != null && entry.getSize() == HGT_SIZE);
- if (entryOk)
+ double x = (point.getLongitude().getDouble() - tile.getLongitude()) * (rowSize - 1);
+ double y = rowSize - (point.getLatitude().getDouble() - tile.getLatitude()) * (rowSize - 1);
+ int idx1 = ((int)y)*rowSize + (int)x;
+ try
{
- // Read entire file contents into one byte array
- for (int i = 0; i < ARRLENGTH; i++)
+ int[] fouralts = {heights[idx1], heights[idx1+1], heights[idx1-rowSize], heights[idx1-rowSize+1]};
+ int numVoids = (fouralts[0]==VOID_VAL?1:0) + (fouralts[1]==VOID_VAL?1:0)
+ + (fouralts[2]==VOID_VAL?1:0) + (fouralts[3]==VOID_VAL?1:0);
+ // if (numVoids > 0) System.out.println(numVoids + " voids found");
+ double altitude = 0.0;
+ switch (numVoids)
{
- heights[i] = inStream.read() * 256 + inStream.read();
- if (heights[i] >= 32768) {heights[i] -= 65536;}
+ case 0: altitude = bilinearInterpolate(fouralts, x, y); break;
+ case 1: altitude = bilinearInterpolate(fixVoid(fouralts), x, y); break;
+ case 2:
+ case 3: altitude = averageNonVoid(fouralts); break;
+ default: altitude = VOID_VAL;
}
- }
- // else {
- // System.out.println("length not ok: " + entry.getSize());
- // }
- // Close stream from url
- inStream.close();
- }
-
- if (entryOk)
- {
- // Loop over all points in track, try to apply altitude from array
- for (int p = 0; p < _track.getNumPoints(); p++)
- {
- DataPoint point = _track.getPoint(p);
- if (!point.hasAltitude()
- || (inOverwriteZeros && point.getAltitude().getValue() == 0))
+ // Special case for terrain tracks, don't interpolate voids yet
+ if (!_normalTrack && numVoids > 0) {
+ altitude = VOID_VAL;
+ }
+ if (altitude != VOID_VAL)
{
- if (new SrtmTile(point).equals(tile))
- {
- double x = (point.getLongitude().getDouble() - tile.getLongitude()) * 1200;
- double y = 1201 - (point.getLatitude().getDouble() - tile.getLatitude()) * 1200;
- int idx1 = ((int)y)*1201 + (int)x;
- try
- {
- int[] fouralts = {heights[idx1], heights[idx1+1], heights[idx1-1201], heights[idx1-1200]};
- int numVoids = (fouralts[0]==VOID_VAL?1:0) + (fouralts[1]==VOID_VAL?1:0)
- + (fouralts[2]==VOID_VAL?1:0) + (fouralts[3]==VOID_VAL?1:0);
- // if (numVoids > 0) System.out.println(numVoids + " voids found");
- double altitude = 0.0;
- switch (numVoids)
- {
- case 0: altitude = bilinearInterpolate(fouralts, x, y); break;
- case 1: altitude = bilinearInterpolate(fixVoid(fouralts), x, y); break;
- case 2:
- case 3: altitude = averageNonVoid(fouralts); break;
- default: altitude = VOID_VAL;
- }
- // Special case for terrain tracks, don't interpolate voids yet
- if (!_normalTrack && numVoids > 0) {
- altitude = VOID_VAL;
- }
- if (altitude != VOID_VAL)
- {
- point.setFieldValue(Field.ALTITUDE, ""+altitude, false);
- // depending on settings, this value may have been added as feet, we need to force metres
- point.getAltitude().reset(new Altitude((int)altitude, UnitSetLibrary.UNITS_METRES));
- numAltitudesFound++;
- }
- }
- catch (ArrayIndexOutOfBoundsException obe) {
- // System.err.println("lat=" + point.getLatitude().getDouble() + ", x=" + x + ", y=" + y + ", idx=" + idx1);
- }
- }
+ point.setFieldValue(Field.ALTITUDE, ""+altitude, false);
+ // depending on settings, this value may have been added as feet, we need to force metres
+ point.getAltitude().reset(new Altitude((int)altitude, UnitSetLibrary.UNITS_METRES));
+ numAltitudesFound++;
}
}
+ catch (ArrayIndexOutOfBoundsException obe) {
+ errorMessage += "Point not in tile? lat=" + point.getLatitude().getDouble() + ", x=" + x + ", y=" + y + ", idx=" + idx1+"\n";
+ }
}
}
- catch (IOException ioe) {errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
- }
}
}
return;
}
+ if (! errorMessage.equals("")) {
+ _app.showErrorMessageNoLookup(getNameKey(), errorMessage);
+ return;
+ }
if (numAltitudesFound > 0)
{
// Inform app including undo information
I18nManager.getTextWithNumber("confirm.lookupsrtm", numAltitudesFound));
}
}
- else if (errorMessage != null) {
- _app.showErrorMessageNoLookup(getNameKey(), errorMessage);
- }
else if (inTileList.size() > 0) {
_app.showErrorMessage(getNameKey(), "error.lookupsrtm.nonefound");
}
}
}
- /**
- * See whether the SRTM file is already available locally first, then try online
- * @param inUrl URL for online resource
- * @return ZipInputStream either on the local file or on the downloaded zip file
- */
- private ZipInputStream getStreamToHgtFile(URL inUrl)
- throws IOException
- {
- String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
- if (diskCachePath != null)
- {
- File srtmDir = new File(diskCachePath, "srtm");
- if (srtmDir.exists() && srtmDir.isDirectory() && srtmDir.canRead())
- {
- File srtmFile = new File(srtmDir, new File(inUrl.getFile()).getName());
- if (srtmFile.exists() && srtmFile.isFile() && srtmFile.canRead()
- && srtmFile.length() > 400)
- {
- // System.out.println("Lookup: Using file " + srtmFile.getAbsolutePath());
- // File found, use this one
- return new ZipInputStream(new FileInputStream(srtmFile));
- }
- }
- }
- // System.out.println("Lookup: Trying online: " + inUrl.toString());
- _hadToDownload = true;
- // MAYBE: Only download if we're in online mode?
- return new ZipInputStream(inUrl.openStream());
- }
-
/**
* Perform a bilinear interpolation on the given altitude array
* @param inAltitudes array of four altitude values on corners of square (bl, br, tl, tr)
--- /dev/null
+package tim.prune.function.srtm;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.HttpURLConnection;
+
+import tim.prune.GpsPrune;
+import tim.prune.I18nManager;
+
+public class Srtm3Source extends SrtmSource {
+ /** URL prefix for all tiles */
+ private static final String URL_PREFIX = "https://dds.cr.usgs.gov/srtm/version2_1/SRTM3/";
+ /** Directory names for each continent */
+ private static final String[] CONTINENTS = {"", "Eurasia", "North_America", "Australia",
+ "Islands", "South_America", "Africa"};
+ private byte[] _continents_lookup;
+
+
+ public Srtm3Source()
+ {
+ _continents_lookup = populateContinents();
+ }
+
+ public String getNameKey()
+ {
+ return "function.downloadsrtm." + getName();
+ }
+
+ public String getName()
+ {
+ return "SRTM3_v21";
+ }
+
+ protected String getSourceExtension()
+ {
+ return ".hgt.zip";
+ }
+
+ /**
+ * Read the dat file and get the contents
+ * @return byte array containing file contents
+ */
+ private static byte[] populateContinents()
+ {
+ InputStream in = null;
+ try
+ {
+ // Need absolute path to dat file
+ in = Srtm3Source.class.getResourceAsStream("/tim/prune/function/srtm/srtmtiles.dat");
+ if (in != null)
+ {
+ byte[] buffer = new byte[in.available()];
+ in.read(buffer);
+ in.close();
+ return buffer;
+ }
+ }
+ catch (java.io.IOException e) {
+ System.err.println("Exception trying to read srtmtiles.dat : " + e.getMessage());
+ }
+ finally
+ {
+ try {
+ in.close();
+ }
+ catch (Exception e) {} // ignore
+ }
+ return null;
+ }
+
+ /**
+ * Get the Url for the given tile
+ * @param inTile Tile to get
+ * @return URL
+ */
+ private URL buildUrl(SrtmTile inTile)
+ throws SrtmSourceException
+ {
+
+ // Get byte from lookup array
+ int idx = (inTile.getLatitude() + 59)*360 + (inTile.getLongitude() + 180);
+ int dir;
+ try
+ {
+ dir = _continents_lookup[idx];
+ }
+ catch (ArrayIndexOutOfBoundsException e)
+ {
+ throw new SrtmSourceException("Could not find continent for tile "+inTile.getTileName());
+ }
+ try
+ {
+ return new URL(URL_PREFIX + CONTINENTS[dir] + "/" + inTile.getTileName() + getSourceExtension());
+ }
+ catch (MalformedURLException e)
+ {
+ throw new SrtmSourceException("Could not build URL for tile "+inTile.getTileName());
+ }
+ }
+
+ public boolean isReadyToUse()
+ {
+ return true;
+ }
+
+ public boolean downloadTile(SrtmTile inTile)
+ throws SrtmSourceException
+ {
+ int redirects = 5;
+ URL tileUrl = buildUrl(inTile);
+ File outputFile = getCacheFileName(inTile);
+ System.out.println("Download: Need to download: " + tileUrl);
+
+ try
+ {
+ HttpURLConnection conn = (HttpURLConnection) tileUrl.openConnection();
+
+ // Define streams
+ FileOutputStream outStream = null;
+ InputStream inStream = null;
+
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+
+ int status = conn.getResponseCode();
+ if (status == 200)
+ {
+ inStream = conn.getInputStream();
+ }
+ else if (status == 404)
+ {
+ throw new SrtmSourceException("Tile not found: "+conn.getURL());
+ }
+ else
+ {
+ throw new SrtmSourceException("Invalid response from server: " +status+conn.getContent());
+ }
+
+ outStream = new FileOutputStream(outputFile);
+
+ int c;
+ while ((c = inStream.read()) != -1)
+ {
+ outStream.write(c);
+ }
+ // Make sure streams are closed
+ try {inStream.close();} catch (Exception e) {}
+ try {outStream.close();} catch (Exception e) {}
+ return true;
+ }
+ catch (IOException e)
+ {
+ throw new SrtmSourceException("Error while downloading tile "+inTile.getTileName()+": "+e.getMessage());
+ }
+ }
+
+ public int getRowSize(SrtmTile inTile)
+ {
+ return 1201;
+ }
+}
--- /dev/null
+package tim.prune.function.srtm;
+
+import java.io.File;
+
+import tim.prune.config.Config;
+import tim.prune.I18nManager;
+
+public class SrtmDiskCache {
+
+ private static boolean _cacheIsUsable = false;
+ private static File _cacheDir = null;
+
+ public static boolean ensureCacheIsUsable()
+ {
+
+ if (_cacheIsUsable)
+ {
+ return true;
+ }
+ // Check the cache is ok
+ String diskCachePath = Config.getConfigString(Config.KEY_DISK_CACHE);
+ if (diskCachePath == null)
+ {
+ return false;
+ }
+ File srtmDir = new File(diskCachePath, "srtm");
+ if (!srtmDir.exists() && !srtmDir.mkdir()) {
+ // can't create the srtm directory
+ return false;
+ }
+ _cacheIsUsable = true;
+ _cacheDir = srtmDir;
+ return true;
+ }
+
+ public static File getCacheDir(String inSourceName)
+ {
+ if (_cacheDir == null)
+ {
+ ensureCacheIsUsable();
+ }
+ File cacheDir = new File(_cacheDir, inSourceName);
+ if (!cacheDir.exists() && !cacheDir.mkdir()) {
+ // can't create the srtm directory
+ return null;
+ }
+ return cacheDir;
+ }
+}
--- /dev/null
+package tim.prune.function.srtm;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.CookiePolicy;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.HttpURLConnection;
+
+import tim.prune.App;
+import tim.prune.GpsPrune;
+import tim.prune.config.Config;
+
+/**
+ * Create an account at: https://urs.earthdata.nasa.gov/users/new
+ * Data policy: https://lpdaac.usgs.gov/data/data-citation-and-policies/
+ *
+ */
+
+public class SrtmGl1Source extends SrtmSource {
+ /** URL prefix for all tiles */
+ private static final String URL_PREFIX = "https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL1.003/2000.02.11/";
+ /** Auth URL */
+ private static final String AUTH_URL = "urs.earthdata.nasa.gov";
+
+
+ public SrtmGl1Source()
+ {
+ CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
+ }
+
+ public String getName()
+ {
+ return "SRTMGL1_v003";
+ }
+
+ protected String getSourceExtension()
+ {
+ return ".SRTMGL1.hgt.zip";
+ }
+
+ private URL buildUrl(SrtmTile inTile)
+ throws SrtmSourceException
+ {
+ try {
+ return new URL(URL_PREFIX + inTile.getTileName() + getSourceExtension());
+ }
+ catch (MalformedURLException e)
+ {
+ throw new SrtmSourceException(e.getMessage());
+ }
+ }
+
+ public boolean isReadyToUse()
+ {
+ return getAuth() != null;
+ }
+
+ private String getAuth()
+ {
+ String authString = Config.getConfigString(Config.KEY_EARTHDATA_AUTH);
+ if (authString != null)
+ {
+ return "Basic " + authString;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public boolean downloadTile(SrtmTile inTile)
+ throws SrtmSourceException
+ {
+ return downloadTile(inTile, getAuth());
+ }
+
+ private boolean downloadTile(SrtmTile inTile, String auth)
+ throws SrtmSourceException
+ {
+ URL tileUrl = buildUrl(inTile);
+ File outputFile = getCacheFileName(inTile);
+ System.out.println("Download: Need to download: " + tileUrl);
+ try
+ {
+ HttpURLConnection conn = (HttpURLConnection) tileUrl.openConnection();
+ long fileLength = 0L;
+
+ // Define streams
+ FileOutputStream outStream = null;
+ InputStream inStream = null;
+
+ // Documentation about HTTP interface at:
+ // https://wiki.earthdata.nasa.gov/display/EL/How+To+Access+Data+With+Java
+ int redirects = 0;
+
+ while (redirects < 10) {
+ redirects++;
+
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+ conn.setInstanceFollowRedirects(false);
+ conn.setUseCaches(false);
+ if (conn.getURL().getHost().equals(AUTH_URL))
+ {
+ conn.setRequestProperty("Authorization", auth);
+ }
+
+ int status = conn.getResponseCode();
+ if (status == 200)
+ {
+ // Found the tile, we're good
+ inStream = conn.getInputStream();
+ fileLength = conn.getContentLengthLong();
+ break;
+ }
+ else if (status == 302)
+ {
+ // redirected to SSO server then back to original resource
+ String newUrl = conn.getHeaderField("Location");
+ conn = (HttpURLConnection) (new URL(newUrl)).openConnection();
+ }
+ else if (status == 404)
+ {
+ throw new SrtmSourceException("Tile " + inTile.getTileName() + " not found at " + conn.getURL());
+ }
+ else
+ {
+ throw new SrtmSourceException("Invalid response from server: " + status + conn.getResponseMessage());
+ }
+ }
+
+ // _progress.setValue(t * 10 + 1);
+ outStream = new FileOutputStream(outputFile);
+
+ // Copy all the bytes to the file
+ int c;
+ long written = 0L;
+ while ((c = inStream.read()) != -1)
+ {
+ outStream.write(c);
+ written++;
+ // _progress.setValue(t * 10 + 1 + (int) ((10 * written) / fileLength));
+ }
+ // Make sure streams are closed
+ try {inStream.close();} catch (Exception e) {}
+ try {outStream.close();} catch (Exception e) {}
+ return true;
+ }
+ catch (IOException e)
+ {
+ throw new SrtmSourceException("Error while downloading tile " + inTile.getTileName() + ": "+e.getMessage());
+ }
+ }
+
+ public boolean testAuth(String auth)
+ throws SrtmSourceException
+ {
+ // The only thing special about this tile is that it's the smallest tile
+ // It covers small islands in Malaysia
+ SrtmTile testTile = new SrtmTile(7, 117);
+ if (isCached(testTile))
+ {
+ getCacheFileName(testTile).delete();
+ }
+ return downloadTile(testTile, auth);
+ }
+
+ public int getRowSize(SrtmTile inTile)
+ {
+ return 3601;
+ }
+}
--- /dev/null
+package tim.prune.function.srtm;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public abstract class SrtmSource {
+ public abstract String getName();
+ public abstract boolean isReadyToUse();
+ public abstract boolean downloadTile(SrtmTile inTile)
+ throws SrtmSourceException;
+ public abstract int getRowSize(SrtmTile inTile);
+ protected abstract String getSourceExtension();
+
+ public int[] getTileHeights(SrtmTile inTile)
+ throws SrtmSourceException
+ {
+ File cacheFileName = getCacheFileName(inTile);
+ if (cacheFileName == null)
+ {
+ throw new SrtmSourceException("Tile "+inTile.getTileName()+" not in cache");
+ }
+ try
+ {
+ ZipInputStream inStream = new ZipInputStream(new FileInputStream(cacheFileName));
+ ZipEntry entry = inStream.getNextEntry();
+ int rowSize = getRowSize(inTile);
+ int tileSize = rowSize * rowSize;
+ if (entry.getSize() != 2 * tileSize)
+ {
+ throw new SrtmSourceException("Tile file "+cacheFileName+" does not have the expected size");
+ }
+ int[] heights = new int[tileSize];
+ // Read entire file contents into one byte array
+ for (int i = 0; i < heights.length; i++)
+ {
+ heights[i] = inStream.read() * 256 + inStream.read();
+ if (heights[i] >= 32768) {heights[i] -= 65536;}
+ }
+ // Close stream
+ inStream.close();
+ return heights;
+ }
+ catch (IOException e)
+ {
+ throw new SrtmSourceException("Failure opening "+cacheFileName+" for reading:"+e.getMessage());
+ }
+
+ }
+
+ protected File getCacheDir()
+ {
+ return SrtmDiskCache.getCacheDir(getName());
+ }
+
+ protected File getCacheFileName(SrtmTile inTile)
+ {
+ String fileName = inTile.getTileName() + getSourceExtension();
+ return new File(getCacheDir(), fileName);
+ }
+
+ public boolean isCached(SrtmTile inTile)
+ {
+ return getCacheFileName(inTile).exists();
+ }
+}
--- /dev/null
+package tim.prune.function.srtm;
+
+public class SrtmSourceException extends Exception {
+ public SrtmSourceException(String message) {
+ super(message);
+ }
+}
+ (_longitude >= 0?"E":"W")
+ (Math.abs(_longitude) < 100?"0":"")
+ (Math.abs(_longitude) < 10?"0":"")
- + Math.abs(_longitude)
- + ".hgt.zip";
+ + Math.abs(_longitude);
+ }
+
+ public SrtmSource findBestCachedSource()
+ {
+ SrtmSource[] sources = {new SrtmGl1Source(),
+ new Srtm3Source() };
+ for (int i = 0; i < sources.length; i++)
+ {
+ if (sources[i].isCached(this))
+ {
+ return sources[i];
+ }
+ }
+ return null;
}
}
+++ /dev/null
-package tim.prune.function.srtm;
-
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-
-
-/**
- * Class to get the URLs of the SRTM tiles
- * using the srtmtiles.dat file
- */
-public abstract class TileFinder
-{
- /** URL prefix for all tiles */
- private static final String URL_PREFIX = "https://dds.cr.usgs.gov/srtm/version2_1/SRTM3/";
- /** Directory names for each continent */
- private static final String[] CONTINENTS = {"", "Eurasia", "North_America", "Australia",
- "Islands", "South_America", "Africa"};
-
-
- /**
- * Get the Urls for the given list of tiles
- * @param inTiles list of Tiles to get
- * @return array of URLs
- */
- public static URL[] getUrls(ArrayList<SrtmTile> inTiles)
- {
- if (inTiles == null || inTiles.size() < 1) {return null;}
- URL[] urls = new URL[inTiles.size()];
- // Read dat file into array
- byte[] lookup = readDatFile();
- for (int t=0; t<inTiles.size(); t++)
- {
- SrtmTile tile = inTiles.get(t);
- // Get byte from lookup array
- int idx = (tile.getLatitude() + 59)*360 + (tile.getLongitude() + 180);
- try
- {
- int dir = lookup[idx];
- if (dir > 0) {
- try {
- urls[t] = new URL(URL_PREFIX + CONTINENTS[dir] + "/" + tile.getTileName());
- } catch (MalformedURLException e) {} // ignore error, url stays null
- }
- } catch (ArrayIndexOutOfBoundsException e) {} // ignore error, url stays null
- }
- return urls;
- }
-
- /**
- * Read the dat file and get the contents
- * @return byte array containing file contents
- */
- private static byte[] readDatFile()
- {
- InputStream in = null;
- try
- {
- // Need absolute path to dat file
- in = TileFinder.class.getResourceAsStream("/tim/prune/function/srtm/srtmtiles.dat");
- if (in != null)
- {
- byte[] buffer = new byte[in.available()];
- in.read(buffer);
- in.close();
- return buffer;
- }
- }
- catch (java.io.IOException e) {
- System.err.println("Exception trying to read srtmtiles.dat : " + e.getMessage());
- }
- finally
- {
- try {
- in.close();
- }
- catch (Exception e) {} // ignore
- }
- return null;
- }
-}
import tim.prune.function.browser.UrlGenerator;
import tim.prune.function.browser.WebMapFunction;
import tim.prune.function.search.SearchMapillaryFunction;
+import tim.prune.function.srtm.DownloadSrtmFunction;
+import tim.prune.function.srtm.SrtmGl1Source;
+import tim.prune.function.srtm.Srtm3Source;
/**
* Class to manage the menu bar and tool bar,
private JMenuItem _getGpsiesItem = null;
private JMenuItem _uploadGpsiesItem = null;
private JMenuItem _lookupSrtmItem = null;
- private JMenuItem _downloadSrtmItem = null;
+ private JMenu _downloadSrtmMenu = null;
private JMenuItem _nearbyWikipediaItem = null;
private JMenuItem _nearbyOsmPoiItem = null;
private JMenuItem _showPeakfinderItem = null;
// SRTM
_lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
onlineMenu.add(_lookupSrtmItem);
- _downloadSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_SRTM, false);
- onlineMenu.add(_downloadSrtmItem);
+ // Download SRTM sub-menu
+ _downloadSrtmMenu = new JMenu(I18nManager.getText("function.downloadsrtm"));
+ _downloadSrtmMenu.setEnabled(false);
+ JMenuItem downloadStrmGl1Item = makeMenuItem(new DownloadSrtmFunction(_app, new SrtmGl1Source()));
+ _downloadSrtmMenu.add(downloadStrmGl1Item);
+ JMenuItem downloadStrm3Item = makeMenuItem(new DownloadSrtmFunction(_app, new Srtm3Source()));
+ _downloadSrtmMenu.add(downloadStrm3Item);
+ onlineMenu.add(_downloadSrtmMenu);
+
// Get gpsies tracks
_getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
onlineMenu.add(_getGpsiesItem);
_getWeatherItem.setEnabled(hasData);
_findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
// have we got a cache?
- _downloadSrtmItem.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null);
+ _downloadSrtmMenu.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null);
// have we got any timestamps?
_deleteByDateItem.setEnabled(hasData && _track.hasData(Field.TIMESTAMP));
function.uploadgpsies=Upload track to Gpsies
function.lookupsrtm=Get altitudes from SRTM
function.downloadsrtm=Download SRTM tiles
+function.downloadsrtm.SRTMGL1_v003=Download SRTM 1 arc-second tiles
+function.downloadsrtm.SRTMGL1_v003.needsetup=An Earthdata account is necessary to download SRTM 1 arc-second tiles
+function.downloadsrtm.SRTM3_v21=Download SRTM 3 arc-second tiles
function.getwikipedia=Get nearby Wikipedia articles
function.searchwikipedianames=Search Wikipedia by name
function.searchosmpois=Get nearby OSM points