package de.westnordost.streetcomplete.util;
import android.graphics.Point;
import android.graphics.Rect;
import java.util.ArrayList;
import java.util.List;
import de.westnordost.osmapi.map.data.BoundingBox;
import de.westnordost.osmapi.map.data.LatLon;
import de.westnordost.osmapi.map.data.OsmLatLon;
/** Taken from http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Java */
public class SlippyMapMath
{
public static Rect enclosingTiles(BoundingBox bbox, int zoom)
{
double notTheNextTile = 0.0000001;
Point min = enclosingTile(bbox.getMin(), zoom);
Point max = enclosingTile(
new OsmLatLon(
bbox.getMaxLatitude() - notTheNextTile,
bbox.getMaxLongitude() - notTheNextTile),
zoom);
return new Rect(min.x, max.y, max.x, min.y);
}
public static Point enclosingTile(LatLon pos, int zoom)
{
double radianLat = Math.toRadians(pos.getLatitude());
int tiles = 1<<zoom;
int xtile = (int) Math.floor( (pos.getLongitude() + 180) / 360 * tiles ) ;
int ytile = (int) Math.floor( (1 - Math.log(Math.tan(radianLat) + 1 / Math.cos(radianLat)) / Math.PI) / 2 * tiles ) ;
xtile = Math.max(0, Math.min(xtile , tiles - 1));
ytile = Math.max(0, Math.min(ytile , tiles - 1));
return new Point(xtile, ytile);
}
public static BoundingBox asBoundingBoxOfEnclosingTiles(BoundingBox bbox, int zoom)
{
return asBoundingBox(enclosingTiles(bbox, zoom), zoom);
}
public static BoundingBox asBoundingBox(Point tile, int zoom)
{
return new BoundingBox(
tile2lat(tile.y + 1, zoom),
tile2lon(tile.x, zoom),
tile2lat(tile.y, zoom),
tile2lon(tile.x + 1, zoom)
);
}
public static BoundingBox asBoundingBox(Rect tiles, int zoom)
{
return new BoundingBox(
tile2lat(tiles.bottom + 1, zoom),
tile2lon(tiles.left, zoom),
tile2lat(tiles.top, zoom),
tile2lon(tiles.right + 1, zoom)
);
}
public static List<Point> asTileList(Rect tiles)
{
int size = (1 + tiles.height()) * (1 + tiles.width());
List<Point> tileList = new ArrayList<>(size);
for(int y = tiles.top; y <= tiles.bottom; ++y)
for(int x = tiles.left; x <= tiles.right; ++x)
tileList.add(new Point(x,y));
return tileList;
}
/** Minimum rect that encloses all the given tiles */
public static Rect minRect(List<Point> tiles)
{
if(tiles.isEmpty()) return null;
Integer bottom = null, top = null, left = null, right = null;
for(Point p : tiles)
{
if(bottom == null || bottom < p.y) bottom = p.y;
if(top == null || top > p.y) top = p.y;
if(left == null || left > p.x) left = p.x;
if(right == null || right < p.x) right = p.x;
}
return new Rect(left, top, right, bottom);
}
private static double tile2lon(int x, int z) {
return x / Math.pow(2.0, z) * 360.0 - 180;
}
private static double tile2lat(int y, int z) {
double n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z);
return Math.toDegrees(Math.atan(Math.sinh(n)));
}
}