--- /dev/null
+package org.simantics.maps.elevation.server;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Stream;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.index.strtree.STRtree;
+
+import org.geotools.geometry.DirectPosition2D;
+import org.geotools.geometry.Envelope2D;
+import org.geotools.referencing.CRS;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.MathTransform;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TiffTileInterface {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TiffTileInterface.class);
+
+ private Path tilesFolder;
+ private LoadingCache<Path, TiffInterface> interfaceCache;
+ private int openInterfacesSize;
+ private STRtree index;
+
+ public TiffTileInterface(Path tilesFolder) {
+ this(tilesFolder, 5);
+ }
+
+ public TiffTileInterface(Path tilesFolder, int openInterfacesSize) {
+ if (!Files.isDirectory(tilesFolder)) {
+ throw new IllegalArgumentException("tilesFolder has to be a folder: " + tilesFolder.toAbsolutePath());
+ }
+ this.tilesFolder = tilesFolder;
+ this.index = new STRtree();
+ this.openInterfacesSize = openInterfacesSize;
+
+ this.interfaceCache = Caffeine.newBuilder()
+ .maximumSize(this.openInterfacesSize)
+ .removalListener((key, gdalInterface, cause) -> ((TiffInterface) gdalInterface).close())
+ .build(key -> new TiffInterface(key));
+
+ try {
+ initializeIndex();
+ } catch (IOException e) {
+ LOGGER.error("Could not initialize index for folder {}", tilesFolder, e);
+ }
+ }
+
+ private TiffInterface openTifInterface(Path tifFile) {
+ return interfaceCache.get(tifFile);
+ }
+
+ private Stream<Path> allTiffFiles() throws IOException {
+ return Files.walk(tilesFolder).filter(Files::isRegularFile).filter(tif -> tif.getFileName().toString().endsWith(".tif"));
+ }
+
+ public void initializeIndex() throws IOException {
+ LOGGER.info("Initializing index..");
+ AtomicInteger counter = new AtomicInteger();
+ allTiffFiles().parallel().forEach(tifFile -> {
+ TiffInterface tifInterface = openTifInterface(tifFile);
+ Envelope2D coords = tifInterface.getCornerCoords();
+ try {
+ MathTransform transform = CRS.findMathTransform(tifInterface.getCRS(), c4326);
+ DirectPosition2D min = new DirectPosition2D();
+ DirectPosition2D max = new DirectPosition2D();
+ transform.transform(new DirectPosition2D(coords.getMinX(), coords.getMinY()), min);
+ transform.transform(new DirectPosition2D(coords.getMaxX(), coords.getMaxY()), max);
+ Envelope envelope = new Envelope(min.getX(), max.getX(), min.getY(), max.getY());
+ synchronized(index) {
+ index.insert(envelope, tifFile);
+ }
+ } catch (Exception e) {
+ LOGGER.error("Could not initialize index for file {}", tifFile, e);
+ } finally {
+ tifInterface.close();
+ int current = counter.getAndIncrement();
+ if (current % 100 == 0) {
+ LOGGER.info(" {}", current);
+ }
+ }
+ });
+ }
+
+ private static CoordinateReferenceSystem c4326;
+
+ static {
+ try {
+ c4326 = CRS.decode("EPSG:4326");
+ } catch (Exception e) {
+ LOGGER.error("Could not initialize epsg:4326", e);
+ }
+ }
+
+ public Number lookup(double x, double y) {
+ LOGGER.info("Looking up x={} y={}", x, y);
+ DirectPosition p = new DirectPosition2D(c4326, x, y);
+ List<Path> tifFile = (List<Path>) index.query(new Envelope(new Coordinate(x, y)));
+ if (!tifFile.isEmpty()) {
+ TiffInterface tifInterface = openTifInterface(tifFile.get(0));
+ try {
+ return tifInterface.lookup(p);
+ } finally {
+ tifInterface.close();
+ }
+ } else {
+ return new Double(0); // use 0 by default for now
+ }
+ }
+}
\ No newline at end of file