如何将1米转换为像素距离?

11

当我开发Android地图应用程序时,我想在地图上画一个半径为1米的圆。众所周知,我无法直接绘制1米,我需要根据缩放级别将1米转换为两个像素之间的距离。如何进行转换?是否有任何API可供使用。

Canvas.draw(x, y, radius),我应该给这个方法传递什么值?


1
如果您想将1米转换为两个像素,并且您需要一个1米半径,则需要将“2”传递给“radius”参数。现在,您说“取决于缩放级别”,因此我猜您将不得不传递类似于“2 *(zoomLevel * zoomLevelCoefficient)”的内容。 - David Hedlund
这是一个有趣的问题,你找到适合自己的解决方案了吗? - Idistic
5个回答

11

假设您的地图是谷歌地图,它们使用墨卡托投影,因此您需要使用该投影进行转换。 在墨卡托投影下,像素代表的米数随着纬度的变化而变化,因此尽管米在地球半径比较小,但纬度非常重要。

以下所有示例都是 JavaScript,因此您可能需要将它们翻译成中文。

这里是坐标系统的一般说明:

http://code.google.com/apis/maps/documentation/javascript/maptypes.html#WorldCoordinates

此示例包含一个 MercatorProjection 对象,其中包括 fromLatLngToPoint() 和 fromPointToLatLng() 方法:

http://code.google.com/apis/maps/documentation/javascript/examples/map-coordinates.html

一旦您将 (x,y) 转换为 (lat,lon),这是如何绘制圆形的:

// Pseudo code
var d = radius/6378800; // 6378800 is Earth radius in meters
var lat1 = (PI/180)* centerLat;
var lng1 = (PI/180)* centerLng;

// Go around a circle from 0 to 360 degrees, every 10 degrees
for (var a = 0 ; a < 361 ; a+=10 ) {
    var tc = (PI/180)*a;
    var y = asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc));
    var dlng = atan2(sin(tc)*sin(d)*cos(lat1),cos(d)-sin(lat1)*sin(y));
    var x = ((lng1-dlng+PI) % (2*PI)) - PI ;
    var lat = y*(180/PI);
    var lon = x*(180/PI);

    // Convert the lat and lon to pixel (x,y) 
}

这两个混搭应用程序在地球表面绘制给定半径的圆:

http://maps.forum.nu/gm_sensitive_circle2.html

http://maps.forum.nu/gm_drag_polygon.html

如果您选择忽略投影,则可以使用笛卡尔坐标,并使用勾股定理简单绘制圆形:

http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates


8
请看api中的Projection对象。它有一个叫做metersToEquatorPixels()的方法。根据api中的描述,它可能只在赤道上准确,但我认为值得一提,以防您不关心精度。
以下是在覆盖层的draw方法中使用此方法的方式,给定圆的半径(以米为单位)和要绘制圆的纬度和经度:
Projection projection = mapView.getProjection();
Point center = projection.toPixels(new GeoPoint(yourLat * E6, yourLong * E6), null);
float radius = projection.metersToEquatorPixels(radiusInMeters);
canvas.draw(center.x, center.y, radius, new Paint());

你可能会对这个话题感兴趣,它提供了一个简单的公式来补偿墨卡托投影的失真:https://dev59.com/fXI95IYBdhLWcg3w8y6N - Romain

3
public static int metersToRadius(float meters, MapView map, double latitude) {
    return (int) (map.getProjection().metersToEquatorPixels(meters) * (1/ Math.cos(Math.toRadians(latitude))));         
}

3

三个问题你需要问: 1- 地图有多大 2- 缩放级别是多少 3- 屏幕有多大

让我们假设地图的宽高比与您的屏幕相同(如果不同,则需要考虑如何裁剪(垂直或水平)或如何拉伸并更改第1点的答案)

一旦您知道了第1和第3个问题的答案,就可以计算出100%缩放情况下米和像素之间的比率,因此您将拥有每米像素数

接下来,您需要保持一个缩放因子(例如:双倍大小的缩放为200%)

您的调用绘制圆形的代码应如下所示:

 Canvas.draw(x,y, radius_in_meters * pixels_per_meter * zoom_factor/100);

找到每米像素是这个问题的关键。你的地图有多大很可能可以通过纬度x经度的方式轻松回答,而不是以像素平方为单位。如何从纬度x经度转换为平方像素? - jamesh

1

您可以计算所需半径的缩放级别:

首先,我们需要计算手机屏幕的宽度。在缩放级别1下,地球赤道长度为256像素,每个后续缩放级别都会使表示地球赤道所需像素数量加倍。以下函数返回屏幕将显示1公里宽度区域的缩放级别。

private int calculateZoomLevel() {

    int ht, screenWidth;
    DisplayMetrics displaymetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
    ht = displaymetrics.heightPixels;
    screenWidth = displaymetrics.widthPixels;

    double equatorLength = 40075004; // in meters
    double widthInPixels = screenWidth;
    double metersPerPixel = equatorLength / 256;
    int zoomLevel = 1;
    while ((metersPerPixel * widthInPixels) > 1000) {
        metersPerPixel /= 2;
        ++zoomLevel;
    }

    Log.i(tag, "zoom level = " + zoomLevel);
    return zoomLevel;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接