]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.maps.elevation.server/src/org/simantics/maps/elevation/server/TiffTileInterface.java
b85f234c72d935ef4121877e8d9cc299d73b3fed
[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.awt.geom.Rectangle2D;
4 import java.io.IOException;
5 import java.nio.file.Files;
6 import java.nio.file.Path;
7 import java.util.Collection;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.concurrent.ConcurrentHashMap;
11 import java.util.concurrent.atomic.AtomicInteger;
12 import java.util.stream.Collectors;
13 import java.util.stream.Stream;
14
15 import org.geotools.geometry.DirectPosition2D;
16 import org.geotools.geometry.Envelope2D;
17 import org.geotools.referencing.CRS;
18 import org.opengis.geometry.DirectPosition;
19 import org.opengis.referencing.crs.CoordinateReferenceSystem;
20 import org.opengis.referencing.operation.MathTransform;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import com.github.benmanes.caffeine.cache.Caffeine;
25 import com.github.benmanes.caffeine.cache.LoadingCache;
26 import com.vividsolutions.jts.geom.Coordinate;
27 import com.vividsolutions.jts.geom.Envelope;
28 import com.vividsolutions.jts.index.strtree.STRtree;
29
30 public class TiffTileInterface {
31
32     private static final Logger LOGGER = LoggerFactory.getLogger(TiffTileInterface.class);
33
34     private Path tilesFolder;
35     private LoadingCache<Path, TiffInterface> interfaceCache;
36     private int openInterfacesSize;
37     private STRtree index;
38
39     public TiffTileInterface(Path tilesFolder) {
40         this(tilesFolder, 5);
41     }
42
43     public TiffTileInterface(Path tilesFolder, int openInterfacesSize) {
44         if (!Files.isDirectory(tilesFolder)) {
45             throw new IllegalArgumentException("tilesFolder has to be a folder: " + tilesFolder.toAbsolutePath());
46         }
47         this.tilesFolder = tilesFolder;
48         this.index = new STRtree();
49         this.openInterfacesSize = openInterfacesSize;
50
51         this.interfaceCache = Caffeine.newBuilder()
52             .maximumSize(this.openInterfacesSize)
53             .removalListener((key, gdalInterface, cause) -> ((TiffInterface) gdalInterface).close())
54             .build(key -> new TiffInterface(key));
55         
56         try {
57             initializeIndex();
58         } catch (IOException e) {
59             LOGGER.error("Could not initialize index for folder {}", tilesFolder, e);
60         }
61     }
62
63     private TiffInterface openTifInterface(Path tifFile) {
64         return interfaceCache.get(tifFile);
65     }
66
67     private Stream<Path> allTiffFiles() throws IOException {
68         return Files.walk(tilesFolder).filter(Files::isRegularFile).filter(tif -> tif.getFileName().toString().endsWith(".tif"));
69     }
70
71     public void initializeIndex() throws IOException {
72         LOGGER.info("Initializing index..");
73         AtomicInteger counter = new AtomicInteger();
74         allTiffFiles().parallel().forEach(tifFile -> {
75             TiffInterface tifInterface = openTifInterface(tifFile);
76             Envelope2D coords = tifInterface.getCornerCoords();
77             try {
78                 MathTransform transform = CRS.findMathTransform(tifInterface.getCRS(), c4326);
79                 DirectPosition2D min = new DirectPosition2D();
80                 DirectPosition2D max = new DirectPosition2D();
81                 transform.transform(new DirectPosition2D(coords.getMinX(), coords.getMinY()), min);
82                 transform.transform(new DirectPosition2D(coords.getMaxX(), coords.getMaxY()), max);
83                 Envelope envelope = new Envelope(min.getX(), max.getX(), min.getY(), max.getY());
84                 synchronized(index) {
85                     index.insert(envelope, tifFile);
86                 }
87                 envelopes.put(tifFile, envelope);
88             } catch (Exception e) {
89                 LOGGER.error("Could not initialize index for file {}", tifFile, e);
90             } finally {
91                 tifInterface.close();
92                 int current = counter.getAndIncrement();
93                 if (current % 100 == 0) {
94                     LOGGER.info("    {}", current);
95                 }
96             }
97         });
98     }
99
100     private Map<Path, Envelope> envelopes = new ConcurrentHashMap<>();
101
102     public Collection<Rectangle2D> getBoundingBoxes() {
103         Collection<Rectangle2D> rects = envelopes.values().stream().map(env -> {
104             double x = env.getMinX();
105             double y = env.getMinY();
106             return new Rectangle2D.Double(x, y, env.getMaxX() - x, env.getMaxY() - y);
107         }).collect(Collectors.toList());
108         return rects;
109     }
110
111     private static CoordinateReferenceSystem c4326;
112
113     static {
114         try {
115             c4326 = CRS.decode("EPSG:4326");
116         } catch (Exception e) {
117             LOGGER.error("Could not initialize epsg:4326", e);
118         }
119     }
120
121     public Number lookup(double x, double y) {
122         LOGGER.info("Looking up x={} y={}", x, y);
123         DirectPosition p = new DirectPosition2D(c4326, x, y);
124         List<Path> tifFiles = (List<Path>) index.query(new Envelope(new Coordinate(x, y)));
125         for (Path tifFile : tifFiles) {
126             TiffInterface tifInterface = openTifInterface(tifFile);
127             if (tifInterface.contains(p)) {
128                 try {
129                     return tifInterface.lookup(p);
130                 } finally {
131                     tifInterface.close();
132                 }
133             } else {
134                 System.out.println("not found");
135             }
136         }
137         return new Double(0); // use 0 by default for now
138     }
139 }