package org.simantics.maps.elevation.server; import java.awt.image.DataBuffer; import java.nio.file.Path; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.gce.geotiff.GeoTiffReader; 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.simantics.maps.elevation.server.prefs.MapsElevationServerPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TiffInterface { private static final Logger LOGGER = LoggerFactory.getLogger(TiffInterface.class); private final Path tifPath; private GridCoverage2D coverage; private boolean init = false; public TiffInterface(Path tifPath) { this.tifPath = tifPath; loadMetadata(); } private void loadMetadata() { GeoTiffReader reader = null; try { reader = new GeoTiffReader(this.tifPath.toFile()); this.coverage = reader.read(null); this.init = true; } catch (Exception e) { LOGGER.error("Could not load {}", tifPath, e); } finally { if (reader != null) { reader.dispose(); } } } public boolean contains(DirectPosition pos) { ensureInit(); Envelope2D e = coverage.getEnvelope2D(); try { MathTransform transform = CRS.findMathTransform(pos.getCoordinateReferenceSystem(), getCRS(), false); DirectPosition target = transform.transform(pos, null); boolean contains = e.contains(target); return contains; } catch (Exception ex) { ex.printStackTrace(); return false; } } public Number lookup(DirectPosition pos) { ensureInit(); Object r = coverage.evaluate(pos); final int dataType = coverage.getRenderedImage().getSampleModel().getDataType(); int pipeDepthUnderGround = MapsElevationServerPreferences.pipeDepthUnderGround(); switch (dataType) { case DataBuffer.TYPE_BYTE: { // TODO: if the result is byte how does one subtract the pipeDepth form the value? // Might not be even relevant with this use case return new Byte(((byte[]) r)[0]); } case DataBuffer.TYPE_SHORT: // Fall through case DataBuffer.TYPE_USHORT: // Fall through case DataBuffer.TYPE_INT: { int val = ((int[]) r)[0] - pipeDepthUnderGround; return new Integer(val); } case DataBuffer.TYPE_FLOAT: { float val = ((float[]) r)[0] - pipeDepthUnderGround; return new Float(val); } case DataBuffer.TYPE_DOUBLE: { double val = ((double[]) r)[0] - pipeDepthUnderGround; return new Double(val); } default: return null; } } private void ensureInit() { if (!init) { throw new IllegalStateException("Interface is not initialized for " + this.tifPath); } } public void close() { coverage.dispose(true); } public Envelope2D getCornerCoords() { return coverage.getEnvelope2D(); } public CoordinateReferenceSystem getCRS() { return coverage.getCoordinateReferenceSystem(); } }