]> gerrit.simantics Code Review - simantics/district.git/blobdiff - org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffTileInterface.java
First version of elevation server based on GeoTIFF files
[simantics/district.git] / org.simantics.maps.elevation.server / src / org / simantics / maps / elevation / server / TiffTileInterface.java
diff --git a/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffTileInterface.java b/org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffTileInterface.java
new file mode 100644 (file)
index 0000000..900630d
--- /dev/null
@@ -0,0 +1,119 @@
+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