public double getHeight(double lat, double lon) {
        double deltaLat = Math.abs(lat - minLat);
        double deltaLon = Math.abs(lon - minLon);
        if (deltaLat > latHigherBound || deltaLat < lowerBound)
            throw new IllegalStateException("latitude not in boundary of this file:" + lat + "," + lon + ", this:" + this.toString());
        if (deltaLon > lonHigherBound || deltaLon < lowerBound)
            throw new IllegalStateException("longitude not in boundary of this file:" + lat + "," + lon + ", this:" + this.toString());

        double elevation;
        if (interpolate) {
            double x = (width - 1) * deltaLon / horizontalDegree;
            double y = (height - 1) * (1 - deltaLat / verticalDegree);
            int left = (int) x;
            int top = (int) y;
            int right = left + 1;
            int bottom = top + 1;

            double w00 = getHeightSample(left, top);
            double w01 = getHeightSample(left, bottom);
            double w10 = getHeightSample(right, top);
            double w11 = getHeightSample(right, bottom);

            double topEle = linearInterpolate(w00, w10, x - left);
            double bottomEle = linearInterpolate(w01, w11, x - left);
            elevation = linearInterpolate(topEle, bottomEle, y - top);
        } else {
            // first row in the file is the northernmost one
            // http://gis.stackexchange.com/a/43756/9006
            int x = (int) (width / horizontalDegree * deltaLon);
            // different fallback methods for lat and lon as we have different rounding (lon -> positive, lat -> negative)
            if (x >= width)
                x = width - 1;
            int y = height - 1 - (int) (height / verticalDegree * deltaLat);
            if (y < 0)
                y = 0;

            elevation = getHeightSample(x, y);
        }
        return isValidElevation(elevation) ? elevation : Double.NaN;
    }
@Test
    public void testGetHeight() {
        // data access has same coordinate system as graphical or UI systems have (or the original DEM data has).
        // But HeightTile has lat,lon system ('mathematically')
        int width = 10;
        int height = 20;
        HeightTile instance = new HeightTile(0, 0, width, height, 1e-6, 10, 20);
        DataAccess heights = new RAMDirectory().create("tmp");
        heights.create(2 * width * height);
        instance.setHeights(heights);
        init(heights, width, height, 1);

        // x,y=1,7
        heights.setShort(2 * (17 * width + 1), (short) 70);

        // x,y=2,9
        heights.setShort(2 * (19 * width + 2), (short) 90);

        assertEquals(1, instance.getHeight(5, 5), 1e-3);
        assertEquals(70, instance.getHeight(2.5, 1.5), 1e-3);
        // edge cases for one tile with the boundaries [min,min+degree/width) for lat and lon
        assertEquals(1, instance.getHeight(3, 2), 1e-3);
        assertEquals(70, instance.getHeight(2, 1), 1e-3);

        // edge cases for the whole object
        assertEquals(1, instance.getHeight(+1.0, 2), 1e-3);
        assertEquals(90, instance.getHeight(0.5, 2.5), 1e-3);
        assertEquals(90, instance.getHeight(0.0, 2.5), 1e-3);
        assertEquals(1, instance.getHeight(+0.0, 3), 1e-3);
        assertEquals(1, instance.getHeight(-0.5, 3.5), 1e-3);
        assertEquals(1, instance.getHeight(-0.5, 3.0), 1e-3);
        // fall back to "2,9" if within its boundaries
        assertEquals(90, instance.getHeight(-0.5, 2.5), 1e-3);

        assertEquals(1, instance.getHeight(0, 0), 1e-3);
        assertEquals(1, instance.getHeight(9, 10), 1e-3);
        assertEquals(1, instance.getHeight(10, 9), 1e-3);
        assertEquals(1, instance.getHeight(10, 10), 1e-3);

        // no error
        assertEquals(1, instance.getHeight(10.5, 5), 1e-3);
        assertEquals(1, instance.getHeight(-0.5, 5), 1e-3);
        assertEquals(1, instance.getHeight(1, -0.5), 1e-3);
        assertEquals(1, instance.getHeight(1, 10.5), 1e-3);
    }