I want to show a CustomOverlay on a Google Maps V2. The loading of the tiles works as expected, the only thing is, that the shown tiles are not appearing to be sharp at all, kind of blurry. Google did not get me to an usable answer.
I took the approach of adding classical 256px x 256px tiles for the TileProvider. I managed to get a tile source where the images were @2x (retina), which made the whole visual experience much sharper, but I would rather not use this source, as the data transfer rate is four times higher, hence not usable on mobile devices and slow internet speeds.
I included two different examples (screenshots) of the rendered map, both with the same configuration (OSM - 256x256 tiles) and with the provided TileProviderOsm. One is on a Galaxy Nexus and the other on a Nexus 5 phone. Both do not look like they were rendered correctly.
Any idea what I could do to prevent the blurriness or increase the sharpness?
My TileProvider looks like following:
public class TileProviderOsm extends UrlTileProvider {
private static final String MAP_URL = "http://tile.openstreetmap.org/%d/%d/%d.png";
private static int TILE_WIDTH = 256;
private static int TILE_HEIGHT = 256;
public static int MIN_ZOOM = 7;
public static int MAX_ZOOM = 15;
public TileProviderOsm() {
super(TILE_WIDTH, TILE_HEIGHT);
}
@Override
public synchronized URL getTileUrl(int x, int y, int zoom) {
String s = String.format(Locale.US, MAP_URL, zoom, x, y);
URL url = null;
try {
url = new URL(s);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return url;
}
}
Here's how I add the overlay to the map:
map.addTileOverlay(new TileOverlayOptions().tileProvider(new TileProviderOsm()));
Here are some examples of the rendered Google Map:
I am solving this issue by drawing four tiles into one tile.
I wrote this TileProvider which is using another tile provider to create higher resolution tiles.
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import com.google.android.gms.maps.model.Tile;
import com.google.android.gms.maps.model.TileProvider;
import java.io.ByteArrayOutputStream;
public class CanvasTileProvider implements TileProvider {
static final int TILE_SIZE = 512;
private TileProvider mTileProvider;
public CanvasTileProvider(TileProvider tileProvider) {
mTileProvider = tileProvider;
}
@Override
public Tile getTile(int x, int y, int zoom) {
byte[] data;
Bitmap image = getNewBitmap();
Canvas canvas = new Canvas(image);
boolean isOk = onDraw(canvas, zoom, x, y);
data = bitmapToByteArray(image);
image.recycle();
if (isOk) {
Tile tile = new Tile(TILE_SIZE, TILE_SIZE, data);
return tile;
} else {
return mTileProvider.getTile(x, y, zoom);
}
}
Paint paint = new Paint();
private boolean onDraw(Canvas canvas, int zoom, int x, int y) {
x = x * 2;
y = y * 2;
Tile leftTop = mTileProvider.getTile(x, y, zoom + 1);
Tile leftBottom = mTileProvider.getTile(x, y + 1, zoom + 1);
Tile rightTop = mTileProvider.getTile(x + 1, y, zoom + 1);
Tile rightBottom = mTileProvider.getTile(x + 1, y + 1, zoom + 1);
if (leftTop == NO_TILE && leftBottom == NO_TILE && rightTop == NO_TILE && rightBottom == NO_TILE) {
return false;
}
Bitmap bitmap;
if (leftTop != NO_TILE) {
bitmap = BitmapFactory.decodeByteArray(leftTop.data, 0, leftTop.data.length);
canvas.drawBitmap(bitmap, 0, 0, paint);
bitmap.recycle();
}
if (leftBottom != NO_TILE) {
bitmap = BitmapFactory.decodeByteArray(leftBottom.data, 0, leftBottom.data.length);
canvas.drawBitmap(bitmap, 0, 256, paint);
bitmap.recycle();
}
if (rightTop != NO_TILE) {
bitmap = BitmapFactory.decodeByteArray(rightTop.data, 0, rightTop.data.length);
canvas.drawBitmap(bitmap, 256, 0, paint);
bitmap.recycle();
}
if (rightBottom != NO_TILE) {
bitmap = BitmapFactory.decodeByteArray(rightBottom.data, 0, rightBottom.data.length);
canvas.drawBitmap(bitmap, 256, 256, paint);
bitmap.recycle();
}
return true;
}
private Bitmap getNewBitmap() {
Bitmap image = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE,
Bitmap.Config.ARGB_8888);
image.eraseColor(Color.TRANSPARENT);
return image;
}
private static byte[] bitmapToByteArray(Bitmap bm) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, bos);
byte[] data = bos.toByteArray();
try {
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
}
It's a problem with the zoom level and scaling of the map tiles. This seems to be a problem with the Maps API V2 for Android. The behavior is supposedly intended as stated here: gmaps-api-issues #4840
I solved it by requesting larger tiles from the WMS - just replace 256x256 with 512x512.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With