1 package org.simantics.maps.elevation.server;
3 import java.io.IOException;
4 import java.nio.file.Files;
5 import java.nio.file.Path;
7 import java.util.concurrent.atomic.AtomicInteger;
8 import java.util.stream.Stream;
10 import com.github.benmanes.caffeine.cache.Caffeine;
11 import com.github.benmanes.caffeine.cache.LoadingCache;
12 import com.vividsolutions.jts.geom.Coordinate;
13 import com.vividsolutions.jts.geom.Envelope;
14 import com.vividsolutions.jts.index.strtree.STRtree;
16 import org.geotools.geometry.DirectPosition2D;
17 import org.geotools.geometry.Envelope2D;
18 import org.geotools.referencing.CRS;
19 import org.opengis.geometry.DirectPosition;
20 import org.opengis.referencing.crs.CoordinateReferenceSystem;
21 import org.opengis.referencing.operation.MathTransform;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
25 public class TiffTileInterface {
27 private static final Logger LOGGER = LoggerFactory.getLogger(TiffTileInterface.class);
29 private Path tilesFolder;
30 private LoadingCache<Path, TiffInterface> interfaceCache;
31 private int openInterfacesSize;
32 private STRtree index;
34 public TiffTileInterface(Path tilesFolder) {
38 public TiffTileInterface(Path tilesFolder, int openInterfacesSize) {
39 if (!Files.isDirectory(tilesFolder)) {
40 throw new IllegalArgumentException("tilesFolder has to be a folder: " + tilesFolder.toAbsolutePath());
42 this.tilesFolder = tilesFolder;
43 this.index = new STRtree();
44 this.openInterfacesSize = openInterfacesSize;
46 this.interfaceCache = Caffeine.newBuilder()
47 .maximumSize(this.openInterfacesSize)
48 .removalListener((key, gdalInterface, cause) -> ((TiffInterface) gdalInterface).close())
49 .build(key -> new TiffInterface(key));
53 } catch (IOException e) {
54 LOGGER.error("Could not initialize index for folder {}", tilesFolder, e);
58 private TiffInterface openTifInterface(Path tifFile) {
59 return interfaceCache.get(tifFile);
62 private Stream<Path> allTiffFiles() throws IOException {
63 return Files.walk(tilesFolder).filter(Files::isRegularFile).filter(tif -> tif.getFileName().toString().endsWith(".tif"));
66 public void initializeIndex() throws IOException {
67 LOGGER.info("Initializing index..");
68 AtomicInteger counter = new AtomicInteger();
69 allTiffFiles().parallel().forEach(tifFile -> {
70 TiffInterface tifInterface = openTifInterface(tifFile);
71 Envelope2D coords = tifInterface.getCornerCoords();
73 MathTransform transform = CRS.findMathTransform(tifInterface.getCRS(), c4326);
74 DirectPosition2D min = new DirectPosition2D();
75 DirectPosition2D max = new DirectPosition2D();
76 transform.transform(new DirectPosition2D(coords.getMinX(), coords.getMinY()), min);
77 transform.transform(new DirectPosition2D(coords.getMaxX(), coords.getMaxY()), max);
78 Envelope envelope = new Envelope(min.getX(), max.getX(), min.getY(), max.getY());
80 index.insert(envelope, tifFile);
82 } catch (Exception e) {
83 LOGGER.error("Could not initialize index for file {}", tifFile, e);
86 int current = counter.getAndIncrement();
87 if (current % 100 == 0) {
88 LOGGER.info(" {}", current);
94 private static CoordinateReferenceSystem c4326;
98 c4326 = CRS.decode("EPSG:4326");
99 } catch (Exception e) {
100 LOGGER.error("Could not initialize epsg:4326", e);
104 public Number lookup(double x, double y) {
105 LOGGER.info("Looking up x={} y={}", x, y);
106 DirectPosition p = new DirectPosition2D(c4326, x, y);
107 List<Path> tifFile = (List<Path>) index.query(new Envelope(new Coordinate(x, y)));
108 if (!tifFile.isEmpty()) {
109 TiffInterface tifInterface = openTifInterface(tifFile.get(0));
111 return tifInterface.lookup(p);
113 tifInterface.close();
116 return new Double(0); // use 0 by default for now