如何用metersToEquatorPixels实现任意纬度的米到像素的转换

今天又是神奇的发现:我希望在MapView的Overlay子类里画一个半径为 R米 的圆,于是用 metersToEquatorPixels 函数,将一个距离  R米 转换为一个以当前缩放等级下像素为单位的距离。调用的时候也没太注意,结果画出来的圆确实让我费解了阵子,仔细一看发现Equator这个单词,于是大概有点明白了。查了一下etersToEquatorPixels 函数的解释:该方法把以米为计量单位的距离(沿赤道)在当前缩放水平下转换到一个以像素(水平)为计量单位的距离。在默认的Mercator投影变换下,对于给定的距离,当远离赤道时,变换后确切的像素数量会增加。

SO,解决办法就是要将赤道,也就是零纬度的米对应的像素距离换算成我们需要的纬度的 米 对应的像素距离。我们可以写这么一个方法来实现:

 

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

 

 

 
当然重点就是 /(Math.cos(Math.toRadians(latitude))) 这一串了。其实看了下面的这幅图,结合一下初高中的几何知识,不难明白为什么要这么写。



 

以上转自: http://cosyattic.com/archives/152

 

以下原创:

 

其实也可以这么理解,根据“GOOGLE MAP 源码”(关于源码,请参见:http://rainbow702.iteye.com/blog/1124280),我们可以看到该方法的实现:

public float metersToEquatorPixels(float meters) {
    return (float)meters*circumference /CIRCUMFERENCE_IN_METERS);
}

 

而 circumference 与 CIRCUMFERENCE_IN_METERS 又分别是什么呢?如下:

private static final double CIRCUMFERENCE_IN_METERS = 40075160.0;
private int tiles = 1 << zoomLevel;
private double circumference = tileSize * tiles;

 

 其中, zoomLevel 是你的 MapView 当前的缩放比例,tileSize 定义如下:

private MapSourceInfo mapSourceInfo = new CloudmadeSourceInfo("b06a3135a4eb5848a225483969f56967");
private int tileSize = mapSourceInfo.getTileSize();

CloudmadeSourceInfo 类的定义如下:
class CloudmadeSourceInfo implements MapSourceInfo {
    private final String apiKey; 
    private final int tileSize;
    private final int style;
    private final String attribution = 
      "\u00a9 2009 CloudMade - Map data CC-BY-SA 2009\nOpenStreetMap.org contributors - Terms of Use";
    
    CloudmadeSourceInfo(String apiKey) {
        this(apiKey, 256);
    }
    CloudmadeSourceInfo(String apiKey, int tileSize) {
        this(apiKey, tileSize, 1);
    }
    CloudmadeSourceInfo(String apiKey, int tileSize, int style) {
        this.apiKey = apiKey;
        this.tileSize = tileSize;
        this.style = style;
    }
    public int getMaxZoom() {
        return 18;
    }

    public String getName() {
        return "Open Street Maps Cloudmade renderer";
    }

    public String getTileUri(int x, int y, int zoom) {
        return "http://b.tile.cloudmade.com/"+ apiKey +"/" + style + "/" + tileSize + "/" + zoom + "/" + x + "/" + y + ".png";
    }

    public int getTileSize() {
        return tileSize;
    }

    public String getAttribution() {
      return attribution;
    }

}

 

所以,假设赤道的半径为 R,我们所处的纬度为 α 度的平面(如上图红线所画的平面)的半径为 r,那么应该有以下等式成立(请再看一下metersToEquatorPixels的实现):

meters * (circumference /2πR) = P
meters * (circumference / 2πr) = p

 以上,P 是在赤道上 meters 米对应的像素的长度,而 p 是在 α 度纬度上 meters 米对应的像素的长度,进而可以得出以下等式:

2πR * P = 2πr * p

 从而,最终得出以下等式:

2πR * P = 2πr * p

p = (R / r) * P

 那么, (R / r) 是什么呢,请看上图,只要是学习简单的立体几何的人应该都可以看来吧。对,它就等于 sec (α), 亦即原帖中所得的结果 1/cos (α)。

 

这样一来,大家都应该知道是怎么一回事了吧。希望对看到这篇博客的人有所帮助。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值