package net.sourceforge.gpstools.dem;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import net.sourceforge.gpstools.gpx.Wpt;
import net.sourceforge.gpstools.gpx.WptType;
import net.sourceforge.gpstools.utils.GPXUtils;
import net.sourceforge.gpstools.utils.GPXUtils.GeonamesException;
public class HgtElevationModel implements ElevationModel {
private final static int MAX_OPEN_FILES = 10;
private Map<Integer, HgtFile> fileMap = new HashMap<Integer, HgtFile>();
private String baseURI = "http://dds.cr.usgs.gov/srtm/version2_1/";
private Map<Integer, String> uriMap = new HashMap<Integer, String>();
private HgtFile last1;
private HgtFile last2;
private File cacheDir = new File(System.getProperty("java.io.tmpdir"));
public HgtElevationModel() throws IOException {
this.loadSrtm3();
this.loadSrtm1();
}
@Override
public void close() throws IOException {
this.closeCore();
}
private void closeCore() throws IOException {
Collection<Integer> keys = fileMap.keySet();
IOException lastEx = null;
for (Integer cell : keys.toArray(new Integer[keys.size()])) {
HgtFile hgt = fileMap.get(cell);
fileMap.remove(cell);
if (hgt != null) {
try {
hgt.close();
} catch (IOException ioex) {
lastEx = ioex;
}
}
}
if (lastEx != null) {
throw lastEx;
}
}
@Override
public BigDecimal getElevation(BigDecimal lat, BigDecimal lon)
throws DEMException {
if (this.last1 != null) {
try {
return this.last1.getElevation(lat, lon);
} catch (DEMException ex) {
if (this.last2 != null) {
try {
BigDecimal result = this.last2.getElevation(lat, lon);
HgtFile tmp = this.last1;
this.last1 = this.last2;
this.last2 = tmp;
return result;
} catch (DEMException ex2) {
// ignore
}
}
}
}
int cellLat = (int) Math.floor(lat.floatValue());
int cellLon = (int) Math.floor(lon.floatValue());
Integer cellKey = getCellKey(cellLat, cellLon);
HgtFile hgt = this.fileMap.get(cellKey);
if (hgt == null) {
try {
String key = this.uriMap.get(cellKey);
if (key == null) {
throw new DEMException(lat, lon,
"No hgt URL for this location.");
}
URI uri = new URI(getBaseURI() + key);
hgt = new HgtFile(uri, this.getCacheDir());
if (this.fileMap.size() >= MAX_OPEN_FILES) {
try {
this.closeCore();
} catch (IOException ex) {
// ignore
}
}
this.fileMap.put(cellKey, hgt);
} catch (IOException e) {
throw new DEMException(lat, lon, e.getMessage(), e);
} catch (URISyntaxException e) {
// Should never happen.
throw new Error(e);
}
}
BigDecimal result = hgt.getElevation(lat, lon);
this.last2 = this.last1;
this.last1 = hgt;
return result;
}
/**
* @param cacheDir
* the cacheDir to set
*/
public void setCacheDir(File cacheDir) {
if (!cacheDir.isDirectory()) {
throw new IllegalArgumentException(cacheDir
+ " is not a directory.");
}
this.cacheDir = cacheDir;
}
/**
* @return the cacheDir
*/
public File getCacheDir() {
return cacheDir;
}
/**
* Sets the base URI relative to which relative URIs are resolved.
*
* @param baseURI
* the base URI.
*/
public void setBaseURI(String baseURI) {
this.baseURI = baseURI;
}
/**
* Gets the base URI relative to which relative URIs are resolved.
*
* @return the base URI.
*/
public String getBaseURI() {
return this.baseURI;
}
public String getRelativeTileURI(int lat, int lon) {
return this.uriMap.get(getCellKey(lat, lon));
}
public void setRelativeTileURI(int lat, int lon, String relativeURI) {
this.uriMap.put(getCellKey(lat, lon), relativeURI);
}
public URI getAbsoluteTileURI(int lat, int lon) throws URISyntaxException {
String relative = this.getRelativeTileURI(lat, lon);
return (relative == null) ? null
: new URI(this.getBaseURI() + relative);
}
private void loadSrtm1() throws IOException {
String[] dirs = new String[] { "Region_01", "Region_02", "Region_03",
"Region_04", "Region_05", "Region_06", "Region_07" };
for (String dir : dirs) {
loadURLs("SRTM1", dir);
}
}
private void loadSrtm3() throws IOException {
String[] dirs = new String[] { "Africa", "Australia", "Eurasia",
"Islands", "North_America", "South_America" };
for (String dir : dirs) {
loadURLs("SRTM3", dir);
}
}
private void loadURLs(String prefix, String dir) throws IOException {
String resourceName = prefix + "/" + dir + ".url";
InputStream in = HgtElevationModel.class.getResourceAsStream(resourceName);
try {
Reader reader = new InputStreamReader(in, "UTF-8");
BufferedReader lineReader = new BufferedReader(reader);
Matcher m;
String longPrefix = prefix + "/" + dir + "/";
for (String name = lineReader.readLine(); name != null; name = lineReader
.readLine()) {
m = HgtFile.fileNamePattern.matcher(name);
if (m.matches()) {
int lat = Integer.parseInt(m.group(2));
if (m.group(1).charAt(0) == 'S') {
lat = -lat;
}
int lon = Integer.parseInt(m.group(4));
if (m.group(3).charAt(0) == 'W') {
lon = -lon;
}
this.setRelativeTileURI(lat, lon, longPrefix + name);
}
}
} finally {
in.close();
}
}
private static Integer getCellKey(int cellLat, int cellLon) {
return ((cellLat + 90) << 16) + (cellLon + 180);
}
/**
* Prints the elevation for a coordinate pair, evaluated with this class and
* with the geonames srtm3 web service.
*
* @param argv
* the command line parameters
* @throws Exception
*/
public static void main(String[] argv) throws Exception {
double lat0 = Double.parseDouble(argv[0]);
double lon0 = Double.parseDouble(argv[1]);
BigDecimal lat = new BigDecimal(lat0);
BigDecimal lon = new BigDecimal(lon0);
HgtElevationModel hgt = new HgtElevationModel();
try {
System.out.println(hgt.getElevation(lat, lon));
} finally {
hgt.close();
}
WptType pt = new Wpt();
pt.setLat(lat);
pt.setLon(lon);
WptType[] wpts = new WptType[] { pt };
try {
GPXUtils.getInstance().setEle(wpts);
System.out.println(pt.getEle());
} catch (GeonamesException ex) {
System.out.println(ex.getMessage());
}
}
@Override
public String getInfo() {
return this.getBaseURI();
}
}