]> gerrit.simantics Code Review - simantics/district.git/blob - 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
1 package org.simantics.maps.elevation.server;
2
3 import java.io.IOException;
4 import java.nio.file.Files;
5 import java.nio.file.Path;
6 import java.util.List;
7 import java.util.concurrent.atomic.AtomicInteger;
8 import java.util.stream.Stream;
9
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;
15
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;
24
25 public class TiffTileInterface {
26
27     private static final Logger LOGGER = LoggerFactory.getLogger(TiffTileInterface.class);
28
29     private Path tilesFolder;
30     private LoadingCache<Path, TiffInterface> interfaceCache;
31     private int openInterfacesSize;
32     private STRtree index;
33
34     public TiffTileInterface(Path tilesFolder) {
35         this(tilesFolder, 5);
36     }
37
38     public TiffTileInterface(Path tilesFolder, int openInterfacesSize) {
39         if (!Files.isDirectory(tilesFolder)) {
40             throw new IllegalArgumentException("tilesFolder has to be a folder: " + tilesFolder.toAbsolutePath());
41         }
42         this.tilesFolder = tilesFolder;
43         this.index = new STRtree();
44         this.openInterfacesSize = openInterfacesSize;
45
46         this.interfaceCache = Caffeine.newBuilder()
47             .maximumSize(this.openInterfacesSize)
48             .removalListener((key, gdalInterface, cause) -> ((TiffInterface) gdalInterface).close())
49             .build(key -> new TiffInterface(key));
50         
51         try {
52             initializeIndex();
53         } catch (IOException e) {
54             LOGGER.error("Could not initialize index for folder {}", tilesFolder, e);
55         }
56     }
57
58     private TiffInterface openTifInterface(Path tifFile) {
59         return interfaceCache.get(tifFile);
60     }
61
62     private Stream<Path> allTiffFiles() throws IOException {
63         return Files.walk(tilesFolder).filter(Files::isRegularFile).filter(tif -> tif.getFileName().toString().endsWith(".tif"));
64     }
65
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();
72             try {
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());
79                 synchronized(index) {
80                     index.insert(envelope, tifFile);
81                 }
82             } catch (Exception e) {
83                 LOGGER.error("Could not initialize index for file {}", tifFile, e);
84             } finally {
85                 tifInterface.close();
86                 int current = counter.getAndIncrement();
87                 if (current % 100 == 0) {
88                     LOGGER.info("    {}", current);
89                 }
90             }
91         });
92     }
93
94     private static CoordinateReferenceSystem c4326;
95
96     static {
97         try {
98             c4326 = CRS.decode("EPSG:4326");
99         } catch (Exception e) {
100             LOGGER.error("Could not initialize epsg:4326", e);
101         }
102     }
103
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));
110             try {
111                 return tifInterface.lookup(p);
112             } finally {
113                 tifInterface.close();
114             }
115         } else {
116             return new Double(0); // use 0 by default for now
117         }
118     }
119 }