Android TileProvider图形在更高的缩放级别上倾斜

Android TileProvider图形在更高的缩放级别上倾斜,android,graphics,google-maps-android-api-2,tile,Android,Graphics,Google Maps Android Api 2,Tile,我目前正在Android Maps API v2中使用,并遇到以下问题:我手动绘制到位图中的图形在更高的缩放级别上明显倾斜: 让我解释一下我在这里做什么。我有很多LatLng点,我为地图上的每个点画了一个圆,所以当你们放大时,点会停留在同一个地理位置。正如您在屏幕截图上看到的,圆圈在较低的缩放级别上看起来很好,但当您开始放大时,圆圈会倾斜 这就是它的实现方式: package trickyandroid.com.locationtracking; import android.content

我目前正在Android Maps API v2中使用,并遇到以下问题:我手动绘制到位图中的图形在更高的缩放级别上明显倾斜:

让我解释一下我在这里做什么。我有很多LatLng点,我为地图上的每个点画了一个圆,所以当你们放大时,点会停留在同一个地理位置。正如您在屏幕截图上看到的,圆圈在较低的缩放级别上看起来很好,但当您开始放大时,圆圈会倾斜

这就是它的实现方式:

package trickyandroid.com.locationtracking;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log;

import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Tile;
import com.google.android.gms.maps.model.TileProvider;
import com.google.maps.android.geometry.Point;
import com.google.maps.android.projection.SphericalMercatorProjection;

import java.io.ByteArrayOutputStream;

/**
 * Created by paveld on 8/8/14.
 */
public class CustomTileProvider implements TileProvider {

    private final int TILE_SIZE = 256;

    private int density = 1;
    private int tileSizeScaled;
    private Paint circlePaint;
    private SphericalMercatorProjection projection;
    private Point[] points;

    public CustomTileProvider(Context context) {
        density = 3; //hardcoded for now, but should be driven by DisplayMetrics.density
        tileSizeScaled = TILE_SIZE * density;

        projection = new SphericalMercatorProjection(TILE_SIZE);

        points = generatePoints();

        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);
        circlePaint.setColor(0xFF000000);
        circlePaint.setStyle(Paint.Style.FILL);
    }

    private Point[] generatePoints() {
        Point[] points = new Point[6];
        points[0] = projection.toPoint(new LatLng(47.603861, -122.333393));
        points[1] = projection.toPoint(new LatLng(47.600389, -122.326741));
        points[2] = projection.toPoint(new LatLng(47.598942, -122.318973));
        points[3] = projection.toPoint(new LatLng(47.599000, -122.311549));
        points[4] = projection.toPoint(new LatLng(47.601373, -122.301721));
        points[5] = projection.toPoint(new LatLng(47.609764, -122.311850));

        return points;
    }

    @Override
    public Tile getTile(int x, int y, int zoom) {
        Bitmap bitmap = Bitmap.createBitmap(tileSizeScaled, tileSizeScaled, Bitmap.Config.ARGB_8888);
        float scale = (float) (Math.pow(2, zoom) * density);
        Matrix m = new Matrix();
        m.setScale(scale, scale);
        m.postTranslate(-x * tileSizeScaled, -y * tileSizeScaled);

        Canvas c = new Canvas(bitmap);
        c.setMatrix(m);

        for (Point p : points) {
            c.drawCircle((float) p.x, (float) p.y, 20 / scale, circlePaint);
        }

        return bitmapToTile(bitmap);
    }

    private Tile bitmapToTile(Bitmap bmp) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
        byte[] bitmapdata = stream.toByteArray();
        return new Tile(tileSizeScaled, tileSizeScaled, bitmapdata);
    }
}
逻辑告诉我,之所以会发生这种情况,是因为我只将LatLng转换为1个平铺的屏幕位置(256x256,即缩放级别0),然后为了将此屏幕点转换为其他缩放级别,我需要缩放位图并将其转换到适当的位置。同时,由于位图是缩放的,我需要补偿圆半径,所以我用缩放因子除以半径。因此,在缩放级别19时,我的比例因子已经是1572864,这是巨大的。这就像通过巨大的放大镜看这个圆圈一样。这就是为什么我有这种效果

因此,我认为解决方案是避免位图缩放和仅缩放/平移屏幕坐标。在这种情况下,我的圆半径将始终保持不变,不会缩小

不幸的是,矩阵数学并不是我最强的技能,所以我的问题是-如何缩放/转换任意缩放级别的点集,并为缩放级别“0”计算点集

实现这一点最简单的方法是为每个缩放级别提供不同的投影实例,但由于“地理点->屏幕点转换”操作非常昂贵,我将保留此方法作为备份,并使用一些简单的数学来转换现有的屏幕点

注意 请注意,我需要专门定制的
瓷砖Provider
,因为在应用程序中,我将绘制比圆形复杂得多的瓷砖。所以简单的
Marker
类在这里对我不起作用

更新 即使我知道如何转换单个点并避免位图缩放:

c.drawCircle((float) p.x * scale - (x * tileSizeScaled), (float) p.y * scale - (y * tileSizeScaled), 20, circlePaint);
我仍然不知道如何处理
Path
对象。我无法像您对单个点那样平移/缩放路径,因此我仍然必须缩放位图,这会再次导致图形瑕疵(笔划宽度在更高的缩放级别上倾斜):

下面是一段代码片段:

package trickyandroid.com.locationtracking;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;

import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Tile;
import com.google.android.gms.maps.model.TileProvider;
import com.google.maps.android.geometry.Point;
import com.google.maps.android.projection.SphericalMercatorProjection;

import java.io.ByteArrayOutputStream;

/**
 * Created by paveld on 8/8/14.
 */
public class CustomTileProvider implements TileProvider {

    private final int TILE_SIZE = 256;

    private int density = 1;
    private int tileSizeScaled;
    private SphericalMercatorProjection projection;
    private Point[] points;
    private Path path;
    private Paint pathPaint;

    public CustomTileProvider(Context context) {
        density = 3; //hardcoded for now, but should be driven by DisplayMetrics.density
        tileSizeScaled = TILE_SIZE * density;

        projection = new SphericalMercatorProjection(TILE_SIZE);

        points = generatePoints();
        path = generatePath(points);

        pathPaint = new Paint();
        pathPaint.setAntiAlias(true);
        pathPaint.setColor(0xFF000000);
        pathPaint.setStyle(Paint.Style.STROKE);
        pathPaint.setStrokeCap(Paint.Cap.ROUND);
        pathPaint.setStrokeJoin(Paint.Join.ROUND);
    }

    private Path generatePath(Point[] points) {
        Path path = new Path();
        path.moveTo((float) points[0].x, (float) points[0].y);
        for (int i = 1; i < points.length; i++) {
            path.lineTo((float) points[i].x, (float) points[i].y);
        }
        return path;
    }

    private Point[] generatePoints() {
        Point[] points = new Point[10];
        points[0] = projection.toPoint(new LatLng(47.603861, -122.333393));
        points[1] = projection.toPoint(new LatLng(47.600389, -122.326741));
        points[2] = projection.toPoint(new LatLng(47.598942, -122.318973));
        points[3] = projection.toPoint(new LatLng(47.599000, -122.311549));
        points[4] = projection.toPoint(new LatLng(47.601373, -122.301721));
        points[5] = projection.toPoint(new LatLng(47.609764, -122.311850));
        points[6] = projection.toPoint(new LatLng(47.599221, -122.311531));
        points[7] = projection.toPoint(new LatLng(47.599663, -122.312410));
        points[8] = projection.toPoint(new LatLng(47.598823, -122.312614));
        points[9] = projection.toPoint(new LatLng(47.599959, -122.310651));

        return points;
    }

    @Override
    public Tile getTile(int x, int y, int zoom) {
        Bitmap bitmap = Bitmap.createBitmap(tileSizeScaled, tileSizeScaled, Bitmap.Config.ARGB_8888);
        float scale = (float) (Math.pow(2, zoom) * density);

        Canvas c = new Canvas(bitmap);
        Matrix m = new Matrix();
        m.setScale(scale, scale);
        m.postTranslate(-x * tileSizeScaled, -y * tileSizeScaled);

        c.setMatrix(m);

        pathPaint.setStrokeWidth(6 * density / scale);
        c.drawPath(path, pathPaint);
        return bitmapToTile(bitmap);
    }

    private Tile bitmapToTile(Bitmap bmp) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
        byte[] bitmapdata = stream.toByteArray();
        return new Tile(tileSizeScaled, tileSizeScaled, bitmapdata);
    }
}
package trickyandroid.com.locationtracking;
导入android.content.Context;
导入android.graphics.Bitmap;
导入android.graphics.Canvas;
导入android.graphics.Matrix;
导入android.graphics.Paint;
导入android.graphics.Path;
导入com.google.android.gms.maps.model.LatLng;
导入com.google.android.gms.maps.model.Tile;
导入com.google.android.gms.maps.model.TileProvider;
导入com.google.maps.android.geometry.Point;
导入com.google.maps.android.projection.SphericalMercatorProjection;
导入java.io.ByteArrayOutputStream;
/**
*由paveld于2014年8月8日创建。
*/
公共类CustomTileProvider实现TileProvider{
专用最终整块大小=256;
私有整数密度=1;
私有int TILESIZESCALE;
私人球面相机投影;
私人点[]点;
专用路径;
私人涂料;
公共CustomTileProvider(上下文){
density=3;//目前已硬编码,但应该由DisplayMetrics.density驱动
tileSizeScaled=瓷砖尺寸*密度;
投影=新的球形摄像机投影(瓷砖尺寸);
点=生成点();
路径=生成路径(点);
pathPaint=新绘制();
pathPaint.setAntiAlias(true);
setColor(0xFF000000);
pathPaint.setStyle(Paint.Style.STROKE);
PATHPRAINT.setStrokeCap(Paint.Cap.ROUND);
pathPaint.setStrokeJoin(Paint.Join.ROUND);
}
专用路径生成路径(点[]点){
路径路径=新路径();
移动到((浮动)点[0].x,(浮动)点[0].y);
对于(int i=1;i