Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blurred custom tiles on Android Maps V2

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:

Google Maps V2 on a Nexus 5 - OSM Map tiles look blurryGoogle Maps V2 on a Galaxy Nexu - OSM Map tiles look blurry

like image 456
Devdroid Avatar asked Dec 04 '22 06:12

Devdroid


2 Answers

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;
    }
}
like image 69
RadekJ Avatar answered Dec 10 '22 11:12

RadekJ


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.

like image 31
pegel Avatar answered Dec 10 '22 13:12

pegel