class HeightTile {

    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());

        // first row in the file is the northernmost one
        // http://gis.stackexchange.com/a/43756/9006
        int lonSimilar = (int) (width / horizontalDegree * deltaLon);
        // different fallback methods for lat and lon as we have different rounding (lon -> positive, lat -> negative)
        if (lonSimilar >= width)
            lonSimilar = width - 1;
        int latSimilar = height - 1 - (int) (height / verticalDegree * deltaLat);
        if (latSimilar < 0)
            latSimilar = 0;

        // always keep in mind factor 2 because of short value
        int daPointer = 2 * (latSimilar * width + lonSimilar);
        int value = heights.getShort(daPointer);
        AtomicInteger counter = new AtomicInteger(1);
        if (value == Short.MIN_VALUE)
            return Double.NaN;

        if (calcMean) {
            if (lonSimilar > 0)
                value += includePoint(daPointer - 2, counter);

            if (lonSimilar < width - 1)
                value += includePoint(daPointer + 2, counter);

            if (latSimilar > 0)
                value += includePoint(daPointer - 2 * width, counter);

            if (latSimilar < height - 1)
                value += includePoint(daPointer + 2 * width, counter);
        }

        return (double) value / counter.get();
    }

    public  HeightTile(int minLat, int minLon, int width, int height, double precision, int horizontalDegree, int verticalDegree);

}