Android TileProvider图形在更高的缩放级别上倾斜
我目前正在Android Maps API v2中使用,并遇到以下问题:我手动绘制到位图中的图形在更高的缩放级别上明显倾斜: 让我解释一下我在这里做什么。我有很多LatLng点,我为地图上的每个点画了一个圆,所以当你们放大时,点会停留在同一个地理位置。正如您在屏幕截图上看到的,圆圈在较低的缩放级别上看起来很好,但当您开始放大时,圆圈会倾斜 这就是它的实现方式: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
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