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