osm-focus/src/dk/network42/osmfocus/OsmTile.java

269 lines
7.9 KiB
Java

package dk.network42.osmfocus;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Locale;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.util.Log;
public class OsmTile {
private static final String TAG = "OsmTile";
public static final int MIN_ZOOM = 0;
public static final int MAX_ZOOM = 18; // Standard OSM and mapnik
public static final int ZOOM_LEVELS = MAX_ZOOM-MIN_ZOOM;
// See http://wiki.openstreetmap.org/wiki/Tile_usage_policy
public static final int MAX_DOWNLOAD_THREADS = 2;
public int mX, mY, mZoom;
int mDownloadErrs = 0;
boolean mDownloadOngoing = false;
// FIXME: This should not be part of OsmTile, but rather an inherited class
private Bitmap mBitmap = null;
// Geo coords
protected GeoBBox mBox;
protected RectF mBox2 = new RectF(); // FIXME: Merge bboxes
// Screen coords (scaled and Mercator latitudes)
protected RectF mViewBox = new RectF();
public OsmTile(int x, int y, int zoom) {
mX = x;
mY = y;
mZoom = zoom;
mBox = tile2BBox(x, y, zoom);
mBox2.left = (float)(((float)mBox.left)/1E7);
mBox2.right = (float)(((float)mBox.right)/1E7);
mBox2.top = (float)(((float)mBox.bottom)/1E7);
mBox2.bottom = (float)(((float)mBox.top)/1E7);
mViewBox.left = (float) MapView.prescale * mBox2.left;
mViewBox.right = (float) MapView.prescale * mBox2.right;
mViewBox.top = (float)(MapView.prescale * GeoMath.latToMercator(mBox2.top));
mViewBox.bottom = (float)(MapView.prescale * GeoMath.latToMercator(mBox2.bottom));
}
public OsmTile(double lon, double lat, int zoom) {
this(lon2tile(lon, zoom), lat2tile(lat, zoom), zoom);
}
public Bitmap getBitmap() {
return mBitmap;
}
public int getByteSize() {
// FIXME: Wrong for vector tiles
return 4*256*256; // Fixed tile size for OSM in ARGB_8888
// Log.d(TAG, "Get size of tile "+this);
// Bitmap bm = getBitmap();
// if (bm==null) {
// Log.d(TAG, " = 0");
// return 0;
// }
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// return getBitmap().getAllocationByteCount();
// } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
// return bm.getRowBytes() * bm.getHeight();
// }
}
public boolean canDraw(Canvas canvas, PaintConfig pcfg) {
if (mBitmap != null) {
return true;
}
return false;
}
public boolean draw(Canvas canvas, PaintConfig pcfg) {
if (mBitmap != null) {
//Log.d(TAG, "Draw bitmap at "+mBox2+" ("+mViewBox+") zoom="+mZoom);
canvas.drawBitmap(mBitmap, null, mViewBox, pcfg.paint);
return true;
} else if (mDownloadErrs>0) {
drawError(canvas, pcfg);
}
return false;
}
public void drawX(Canvas canvas, PaintConfig pcfg) {
canvas.drawLine(mViewBox.left, mViewBox.bottom, mViewBox.right, mViewBox.top, pcfg.paint);
canvas.drawLine(mViewBox.left, mViewBox.top, mViewBox.right, mViewBox.bottom, pcfg.paint);
}
public void drawP(Canvas canvas, PaintConfig pcfg) {
float xc = (mViewBox.left+mViewBox.right)/2;
float yc = (mViewBox.bottom+mViewBox.top)/2;
canvas.drawLine(mViewBox.left, yc, mViewBox.right, yc, pcfg.paint);
canvas.drawLine(xc, mViewBox.top, xc, mViewBox.bottom, pcfg.paint);
}
public void drawFade(Canvas canvas, PaintConfig pcfg) {
canvas.drawRect(mViewBox, pcfg.fade);
}
public void drawError(Canvas canvas, PaintConfig pcfg) {
canvas.drawRect(mViewBox, pcfg.tileerr);
}
// public void drawText(Canvas canvas, Paint paint, String st) {
// float w = paint.measureText(st);
// canvas.drawText(st, mViewBox.centerX(), mViewBox.centerY(), paint);
// }
public void setQueuedForDownload() {
assert(mDownloadOngoing==false);
mDownloadOngoing = true;
}
public void clearQueuedForDownload() {
assert(mDownloadOngoing==true);
mDownloadOngoing = false;
}
public boolean isQueuedForDownload() {
return mDownloadOngoing;
}
public boolean download(String useragent, String provider) {
Bitmap bm = dlFromUrl(tileUrl(provider));
if (bm != null) {
mBitmap = flipBitmap(bm);
//Log.d(TAG, "Download done");
return true;
} else {
Log.e(TAG, "Download failed, tile="+this);
return false;
}
}
private Bitmap dlFromUrl(String tileurl) {
try {
//Log.d(TAG, "Loading tile "+tileurl);
URL url = new URL(tileurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
//Log.d(TAG, "Connect for tile download");
conn.connect();
InputStream input = conn.getInputStream();
//Log.d(TAG, "Decode bitmap");
Bitmap bm = BitmapFactory.decodeStream(input);
//Log.d(TAG, "Downloaded tile "+tileurl+", bbox="+mBox.toString());
return bm;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private Bitmap flipBitmap(Bitmap bm) {
Matrix m = new Matrix();
m.preScale(1, -1);
Bitmap bmf = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, false);
if (bmf!=null)
bmf.setDensity(DisplayMetrics.DENSITY_DEFAULT);
return bmf;
}
static public int lon2tile(double lon, int zoom) {
int xtile = (int)Math.floor((lon + 180.0)/360.0 * (1<<zoom));
if (xtile < 0)
xtile = 0;
else if (xtile >= (1<<zoom))
xtile = (1<<zoom)-1;
return xtile;
}
static public int lat2tile(double lat, int zoom) {
int ytile = (int)Math.floor((1.0 - Math.log(Math.tan(Math.toRadians(lat)) + 1.0/Math.cos(Math.toRadians(lat))) / Math.PI) / 2 * (1<<zoom));
if (ytile < 0)
ytile = 0;
else if (ytile >= (1<<zoom))
ytile = (1<<zoom)-1;
return ytile;
}
static public double tile2lon(int x, int zoom) {
return x/Math.pow(2.0, zoom)*360.0 - 180.0;
}
static public double tile2lat(int y, int zoom) {
double n = Math.PI - (2.0*Math.PI * y)/Math.pow(2.0, zoom);
return Math.toDegrees(Math.atan(Math.sinh(n)));
}
// Convert world viewport coords to tile 'indexes'
static public Rect worldport2tileport(final RectF worldport, int zoom) {
Rect r = new Rect();
r.left = lon2tile(worldport.left, zoom);
r.right = lon2tile(worldport.right, zoom);
r.top = lat2tile(worldport.top, zoom);
r.bottom = lat2tile(worldport.bottom, zoom);
return r;
}
// See http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
static public String xy2Url(String provider, int x, int y, int zoom) {
String name;
name = String.format(Locale.US, "%s/%d/%d/%d.png", provider, zoom, x, y);
return name;
}
public String tileId(String provider) {
String name;
name = String.format(Locale.US, "%s/%d/%d/%d", provider, mZoom, mX, mY);
return name;
}
static public String tileId(String provider, int x, int y, int zoom) {
String name;
name = String.format(Locale.US, "%s/%d/%d/%d", provider, zoom, x, y);
return name;
}
static public String tileId(String provider, double lon, double lat, int zoom) {
String name;
name = String.format(Locale.US, "%s/%d/%d/%d", provider, zoom, lon2tile(lon, zoom), lat2tile(lat, zoom));
return name;
}
public String tileUrl(String provider) {
return xy2Url(provider, mX, mY, mZoom);
}
static public String tileUrl(String provider, double lon, double lat, int zoom) {
return xy2Url(provider, lon2tile(lon, zoom), lat2tile(lat, zoom), zoom);
}
static public String id2Url(String provider, String id) {
return String.format("%s/%s.png", provider, id);
}
static public GeoBBox pos2BBox(double lon, double lat, int zoom) {
return tile2BBox(lon2tile(lon, zoom), lat2tile(lat, zoom), zoom);
}
static public GeoBBox tile2BBox(int tilex, int tiley, int zoom) {
int left = (int)(tile2lon(tilex, zoom)* 1E7);
int right = (int)(tile2lon(tilex+1, zoom)* 1E7);
int top = (int)(tile2lat(tiley, zoom)* 1E7);
int bottom = (int)(tile2lat(tiley+1, zoom)* 1E7);
GeoBBox box = new GeoBBox(left, bottom, right, top);
return box;
}
public String toString() {
return String.format(Locale.US, "%d/%d/%d", mZoom, mX, mY);
}
}