Skip to content

Commit

Permalink
GH #129: Implement USGSElevationProvider (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
rsarathy authored Jun 16, 2024
1 parent 15c577b commit b98eff4
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 30 deletions.
4 changes: 2 additions & 2 deletions bay-area/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ graphhopper:


# To populate your graph with elevation data use SRTM, default is noop (no elevation). Read more about it in docs/core/elevation.md
graph.elevation.provider: srtm
graph.elevation.provider: multi


# default location for cache is /tmp/srtm
# graph.elevation.cache_dir: ./srtmprovider/
graph.elevation.cache_dir: /data/elevation/


# If you have a slow disk or plenty of RAM change the default MMAP to:
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/com/graphhopper/GraphHopper.java
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,8 @@ private static ElevationProvider createElevationProvider(GraphHopperConfig ghCon
elevationProvider = new MultiSourceElevationProvider(cacheDirStr);
} else if (eleProviderStr.equalsIgnoreCase("skadi")) {
elevationProvider = new SkadiProvider(cacheDirStr);
} else if (eleProviderStr.equalsIgnoreCase("usgs")) {
elevationProvider = new USGSProvider(cacheDirStr);
}

if (elevationProvider instanceof TileBasedElevationProvider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@
* @author Robin Boldt
*/
public abstract class AbstractTiffElevationProvider extends TileBasedElevationProvider {
private final Map<String, HeightTile> cacheData = new HashMap<>();
final Map<String, HeightTile> cacheData = new HashMap<>();
final double precision = 1e7;

private final int WIDTH;
private final int HEIGHT;
final int WIDTH;
final int HEIGHT;

// Degrees of latitude covered by this tile
final int LAT_DEGREE;
Expand Down Expand Up @@ -178,7 +178,7 @@ private void downloadFile(File downloadFile, String url) throws IOException {
}
}

private void fillDataAccessWithElevationData(Raster raster, DataAccess heights, int dataAccessWidth) {
void fillDataAccessWithElevationData(Raster raster, DataAccess heights, int dataAccessWidth) {
final int height = raster.getHeight();
final int width = raster.getWidth();
int x = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,31 @@
public class MultiSourceElevationProvider extends TileBasedElevationProvider {

// Usually a high resolution provider in the SRTM area
private final TileBasedElevationProvider srtmProvider;
private final TileBasedElevationProvider primary;
// The fallback provider that provides elevation data globally
private final TileBasedElevationProvider globalProvider;
private final TileBasedElevationProvider fallback;

public MultiSourceElevationProvider(TileBasedElevationProvider srtmProvider, TileBasedElevationProvider globalProvider) {
super(srtmProvider.cacheDir.getAbsolutePath());
this.srtmProvider = srtmProvider;
this.globalProvider = globalProvider;
public MultiSourceElevationProvider(
TileBasedElevationProvider primary,
TileBasedElevationProvider fallback) {
super(primary.cacheDir.getAbsolutePath());
this.primary = primary;
this.fallback = fallback;
}

public MultiSourceElevationProvider() {
this(new CGIARProvider(), new GMTEDProvider());
}

public MultiSourceElevationProvider(String cacheDir) {
this(new CGIARProvider(cacheDir), new GMTEDProvider(cacheDir));
this(new USGSProvider(cacheDir), new SRTMProvider(cacheDir));
}

@Override
public double getEle(double lat, double lon) {
// Sometimes the cgiar data north of 59.999 equals 0
if (lat < 59.999 && lat > -56) {
return srtmProvider.getEle(lat, lon);
}
return globalProvider.getEle(lat, lon);
if (lat > 37 && lat < 38 && lon > -123 && lon < -122)
return primary.getEle(lat, lon);
return fallback.getEle(lat, lon);
}

/**
Expand All @@ -65,46 +65,45 @@ public MultiSourceElevationProvider setBaseURL(String baseURL) {
if (urls.length != 2) {
throw new IllegalArgumentException("The base url must consist of two urls separated by a ';'. The first for cgiar, the second for gmted");
}
srtmProvider.setBaseURL(urls[0]);
globalProvider.setBaseURL(urls[1]);
primary.setBaseURL(urls[0]);
fallback.setBaseURL(urls[1]);
return this;
}

@Override
public MultiSourceElevationProvider setDAType(DAType daType) {
srtmProvider.setDAType(daType);
globalProvider.setDAType(daType);
primary.setDAType(daType);
fallback.setDAType(daType);
return this;
}

@Override
public MultiSourceElevationProvider setInterpolate(boolean interpolate) {
srtmProvider.setInterpolate(interpolate);
globalProvider.setInterpolate(interpolate);
primary.setInterpolate(interpolate);
fallback.setInterpolate(interpolate);
return this;
}

@Override
public boolean canInterpolate() {
return srtmProvider.canInterpolate() && globalProvider.canInterpolate();
return primary.canInterpolate() && fallback.canInterpolate();
}

@Override
public void release() {
srtmProvider.release();
globalProvider.release();
primary.release();
fallback.release();
}

@Override
public MultiSourceElevationProvider setAutoRemoveTemporaryFiles(boolean autoRemoveTemporary) {
srtmProvider.setAutoRemoveTemporaryFiles(autoRemoveTemporary);
globalProvider.setAutoRemoveTemporaryFiles(autoRemoveTemporary);
primary.setAutoRemoveTemporaryFiles(autoRemoveTemporary);
fallback.setAutoRemoveTemporaryFiles(autoRemoveTemporary);
return this;
}

@Override
public String toString() {
return "multi";
}

}
131 changes: 131 additions & 0 deletions core/src/main/java/com/graphhopper/reader/dem/USGSProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.graphhopper.reader.dem;

import static com.graphhopper.util.Helper.close;

import com.graphhopper.storage.DataAccess;
import java.awt.image.Raster;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import org.apache.xmlgraphics.image.codec.tiff.TIFFDecodeParam;
import org.apache.xmlgraphics.image.codec.tiff.TIFFImageDecoder;
import org.apache.xmlgraphics.image.codec.util.SeekableStream;

public class USGSProvider extends AbstractTiffElevationProvider {

String filename = "n38w123";

public USGSProvider(String cacheDir) {
this("", cacheDir, "", 10812, 10812, 1, 1);
}

public USGSProvider(String baseUrl, String cacheDir,
String downloaderName, int width, int height, int latDegree,
int lonDegree) {
super(baseUrl, cacheDir, downloaderName, width, height, latDegree,
lonDegree);
}

public static void main(String[] args) {
USGSProvider elevationProvider = new USGSProvider("/tmp/");

// Market Street ~-5ft to 260ft in prod.
System.out.println("Elevation: " + elevationProvider.getEle(37.7903317182555, -122.39999824547087) + "m");
System.out.println("Elevation: " + elevationProvider.getEle(37.79112431722635, -122.39901032204128) + "m");

// Mount Davidson, expected: ~283m
System.out.println("Elevation: " + elevationProvider.getEle(37.738259, -122.45463) + "m");
}

@Override
boolean isOutsideSupportedArea(double lat, double lon) {
return lat < 37 || lat > 38 || lon < -123 || lon > -122;
}

@Override
int getMinLatForTile(double lat) {
return 37;
}

@Override
int getMinLonForTile(double lon) {
return -123;
}

@Override
String getFileNameOfLocalFile(double lat, double lon) {
return filename + ".tif";
}

@Override
String getFileName(double lat, double lon) {
return filename;
}

@Override
String getDownloadURL(double lat, double lon) {
return "";
}

@Override
public double getEle(double lat, double lon) {
// Return fast, if there is no data available
if (isOutsideSupportedArea(lat, lon)) return 0;

lat = (int) (lat * precision) / precision;
lon = (int) (lon * precision) / precision;
String name = getFileName(lat, lon);
HeightTile demProvider = cacheData.get(name);
if (demProvider == null) {
if (!cacheDir.exists()) cacheDir.mkdirs();

int minLat = getMinLatForTile(lat);
int minLon = getMinLonForTile(lon);
// less restrictive against boundary checking
demProvider = new HeightTile(minLat, minLon, WIDTH, HEIGHT,
LON_DEGREE * precision, LON_DEGREE, LAT_DEGREE);
demProvider.setInterpolate(interpolate);

cacheData.put(name, demProvider);
DataAccess heights = getDirectory().create(name + ".gh");
demProvider.setHeights(heights);
boolean loadExisting = false;
try {
loadExisting = heights.loadExisting();
} catch (Exception ex) {
logger.warn("cannot load {}, error: {}", name, ex.getMessage());
}

if (!loadExisting) {
File file = new File(cacheDir,
new File(getFileNameOfLocalFile(lat, lon)).getName());

// short == 2 bytes
heights.create(2 * WIDTH * HEIGHT);

Raster raster = generateRasterFromFile(file, name + ".tif");
super.fillDataAccessWithElevationData(raster, heights, WIDTH);

} // loadExisting
}

if (demProvider.isSeaLevel()) return 0;
return demProvider.getHeight(lat, lon);
}

@Override
Raster generateRasterFromFile(File file, String tifName) {
SeekableStream ss = null;
try {
InputStream is = new FileInputStream(file);
ss = SeekableStream.wrapInputStream(is, true);
TIFFImageDecoder imageDecoder = new TIFFImageDecoder(ss, new TIFFDecodeParam());
return imageDecoder.decodeAsRaster();
} catch (Exception e) {
throw new RuntimeException("Can't decode " + file.getName(), e);
} finally {
if (ss != null)
close(ss);
}
}
}

0 comments on commit b98eff4

Please sign in to comment.