Math 如何在墨卡托地图(JPEG)上从x,y获得纬度和经度?

Math 如何在墨卡托地图(JPEG)上从x,y获得纬度和经度?,math,maps,mapping,latitude-longitude,Math,Maps,Mapping,Latitude Longitude,我有一张墨卡托投影图作为JPEG,我想知道如何将给定的x,y坐标与其纬度和经度关联起来。我已经看过Gudermanian函数,但我真的不知道如何使用该函数并应用它。也就是说,它期望什么样的输入?我发现的实现(JavaScript)的范围似乎介于-PI和PI之间,但我的y值(以像素为单位)与该范围之间的相关性如何 此外,我还发现了这个函数,它获取纬度并返回谷歌地图的平铺,谷歌地图也使用墨卡托。如果我知道如何反求这个函数,我很快就能得到答案了 /*<summary>Get the ver

我有一张墨卡托投影图作为JPEG,我想知道如何将给定的x,y坐标与其纬度和经度关联起来。我已经看过Gudermanian函数,但我真的不知道如何使用该函数并应用它。也就是说,它期望什么样的输入?我发现的实现(JavaScript)的范围似乎介于-PI和PI之间,但我的y值(以像素为单位)与该范围之间的相关性如何

此外,我还发现了这个函数,它获取纬度并返回谷歌地图的平铺,谷歌地图也使用墨卡托。如果我知道如何反求这个函数,我很快就能得到答案了

/*<summary>Get the vertical tile number from a latitude
using Mercator projection formula</summary>*/

    private int getMercatorLatitude(double lati)
    {
        double maxlat = Math.PI;

        double lat = lati;

        if (lat > 90) lat = lat - 180;
        if (lat < -90) lat = lat + 180;

        // conversion degre=>radians
        double phi = Math.PI * lat / 180;

        double res;
        //double temp = Math.Tan(Math.PI / 4 - phi / 2);
        //res = Math.Log(temp);
        res = 0.5 * Math.Log((1 + Math.Sin(phi)) / (1 - Math.Sin(phi)));
        double maxTileY = Math.Pow(2, zoom);
        int result = (int)(((1 - res / maxlat) / 2) * (maxTileY));

        return (result);
    }
/*从纬度获取垂直磁贴编号
用墨卡托投影公式*/
私人商业纬度(双纬度)
{
double maxlat=Math.PI;
双纬度=纬度;
如果(lat>90)lat=lat-180;
如果(纬度<-90)纬度=纬度+180;
//转换度=>弧度
双φ=Math.PI*lat/180;
双res;
//双温=数学Tan(数学PI/4-φ/2);
//res=数学日志(temp);
res=0.5*Math.Log((1+Math.Sin(phi))/(1-Math.Sin(phi));
double maxTileY=Math.Pow(2,缩放);
int结果=(int)((1-res/maxlat)/2)*(maxTileY));
返回(结果);
}

这里有一些代码给你。。。如果你需要更多的解释,请告诉我

    /// <summary>
    /// Calculates the Y-value (inverse Gudermannian function) for a latitude. 
    /// <para><see cref="http://en.wikipedia.org/wiki/Gudermannian_function"/></para>
    /// </summary>
    /// <param name="latitude">The latitude in degrees to use for calculating the Y-value.</param>
    /// <returns>The Y-value for the given latitude.</returns>
    public static double GudermannianInv(double latitude)
    {
        double sign = Math.Sign(latitude);
        double sin = Math.Sin(latitude * RADIANS_PER_DEGREE * sign);
        return sign * (Math.Log((1.0 + sin) / (1.0 - sin)) / 2.0);
    }

    /// <summary>
    /// Returns the Latitude in degrees for a given Y.
    /// </summary>
    /// <param name="y">Y is in the range of +PI to -PI.</param>
    /// <returns>Latitude in degrees.</returns>
    public static double Gudermannian(double y)
    {
        return Math.Atan(Math.Sinh(y)) * DEGREES_PER_RADIAN;
    }
//
///计算纬度的Y值(反古德曼函数)。
/// 
/// 
///用于计算Y值的纬度(度)。
///给定纬度的Y值。
公共静态双格德曼尼诺夫(双纬度)
{
双符号=数学符号(纬度);
双正弦=数学正弦(纬度*弧度/度*符号);
返回符号*(数学日志((1.0+sin)/(1.0-sin))/2.0);
}
/// 
///返回给定Y轴的纬度(以度为单位)。
/// 
///Y在+PI到-PI的范围内。
///以度为单位的纬度。
公共静态双Gudermanian(双y)
{
返回Math.Atan(Math.Sinh(y))*度/弧度;
}
谷歌等公司使用“球形墨卡托”,即使用球形地球模型的墨卡托投影,而不是更慢、更复杂的椭圆方程

转换作为OpenLayers代码的一部分提供:


我做过类似的事情。特别是如果你有一张来自世界某个地方的照片。裁剪后的地图或不是完整的世界地图:

执行反转时的一个重要注意事项是,没有像大多数其他地图投影那样的“墨卡托地图”。存在的每个墨卡托映射都不同,具体取决于输入的φ值。根据维基百科,谷歌使用85.051129,其他地图提供商使用85.05113。因此,必须根据例如GudermanninV(85.05113)对Gudermannian的输入值进行缩放。

完全正确(如果不完全正确)

我刚刚用“理论上的256x256墨卡托瓷砖”(谷歌世界地图的单瓷砖版本)对它进行了测试

这里有更多的代码(JavaScript,但易于理解)需要解释

我住在澳大利亚,纬度约-33°

convertRange(
    GudermannianInv(-33), 
    [Math.PI, - Math.PI], 
    [0, 256]
);
152.88327883810192

如果你从瓷砖顶部往下数152像素,你会发现澳大利亚。通过将结果与已知的良好函数进行比较,我也验证了这个答案的正确性

可以肯定的是,我们可以逆转这种计算:

Gudermannian(
    convertRange(
        152.88, 
        [0, 256], 
        [Math.PI, - Math.PI]
));
我们返回了-32.99613291758226

棘手的部分不是古德曼函数,而是两个尺度之间的转换

幸运的是,我相当懒惰,并且讨厌这种缩放问题,我已经有了一个小功能来为我完成这种混乱的转换

    /**
     * convert number from _n_ of r1[0] .. r1[1] to _n_ of r2[0] .. r2[1]
     * @example `convertRange( 5, [0, 10], [0, 100] ) === 50`
     *
     * @param {number} value
     * @param {array<number>} r1 old range
     * @param {array<number>} r2 new range
     * @returns {number} value adjusted for new range
     */
    function convertRange( value, r1, r2 ) {
        return ( value - r1[0] )
             * ( r2[1] - r2[0] )
             / ( r1[1] - r1[0] )
             +   r2[0];
    }

如果我没记错的话,谷歌使用的是等矩形投影,而不是墨卡托投影。虚拟地球和谷歌都使用墨卡托投影。此外,使用墨卡托投影时的最大有效纬度不是+-90度,大约是+-85.05112878度。极点处的值是无穷大的,所以你必须将其封顶并忽略极点。要真正解决这个问题,你还需要知道使用瓷砖时的缩放级别。我对此有点不清楚。你想做(x,y)->(lat,long)还是(lat,long)->(x,y)?所以,如果我有一张1588像素高的墨卡托地图图像,我想知道y=677的纬度,我会用+PI到-PI的形式计算出677,并称之为古德曼(y,u,in,u,of,PI)?我意识到这是错误的,但你可以看到我的思想在这里停留…例如,在1588像素高的墨卡托地图上,30.0N是从顶部615像素。但是如果我用从PI(0)到-PI(1588)的线性范围来表示615,我得到615->0.70824318。调用上面的Gudermanian(0.70824318)得到37.5587,而不是30.0。显然,问题在于“用线性范围表示615”。那么我该怎么做呢?你基本上必须这样做:lat=gudermanian(Ymax-((y/Height)*(Ymax-Ymin));式中,Ymax和Ymin是通过取+-85.05112878的Gudermanian倒数(或图像的最大和最小纬度的边界)得到的,Height是图像的大小。如果您正在平铺,只要您将Ymax和Ymin替换为平铺的边界,将高度替换为平铺的大小,这也会起作用。这有意义吗?def convert_y_to_lat(y):Ymax=gudermanian_inv(gudermanian(math.pi));Ymin=gudermanian_inv(gudermanian(math.pi))*-1;高度=1588;lat=Gudermanian(Ymax-((y/身高)*(Ymax-Ymin));返回lat;convert_y_to_lat(615)返回4.42964421829。我希望它返回30.0-(
function Gudermannian(y) {
    return Math.atan(Math.sinh(y)) * (180 / Math.PI)
}

function GudermannianInv(latitude)
{
    var sign = Math.sign(latitude);
    var sin  = Math.sin(
                          latitude 
                        * (Math.PI / 180) 
                        * sign
    );
    return sign * (
        Math.log(
            (1 + sin) / (1 - sin)
        ) / 2
    );
}